From bfca76650db57e6aad083cf87171694992b60528 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 26 Sep 2019 09:32:22 +0300 Subject: [PATCH] backend: proxy Zabbix API requests WIP --- go.mod | 2 + go.sum | 7 ++++ pkg/datasource.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/go.mod b/go.mod index 8a573ce..d69e1bc 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,10 @@ go 1.12 require ( github.com/bitly/go-simplejson v0.5.0 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/grafana/grafana_plugin_model v0.0.0-20180518082423-84176c64269d github.com/hashicorp/go-hclog v0.9.2 github.com/hashicorp/go-plugin v1.0.1 + github.com/kr/pretty v0.1.0 // indirect golang.org/x/net v0.0.0-20180826012351-8a410e7b638d ) diff --git a/go.sum b/go.sum index 810a376..f6cbd91 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,6 +17,11 @@ github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cR github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= diff --git a/pkg/datasource.go b/pkg/datasource.go index 2623b87..7eb348d 100644 --- a/pkg/datasource.go +++ b/pkg/datasource.go @@ -1,13 +1,24 @@ package main import ( + "bytes" + "crypto/tls" + "encoding/json" "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "time" simplejson "github.com/bitly/go-simplejson" "github.com/grafana/grafana_plugin_model/go/datasource" hclog "github.com/hashicorp/go-hclog" plugin "github.com/hashicorp/go-plugin" "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" ) type ZabbixDatasource struct { @@ -63,5 +74,95 @@ func (ds *ZabbixDatasource) ZabbixAPIQuery(ctx context.Context, tsdbReq *datasou jsonQueries = append(jsonQueries, json) } + if len(jsonQueries) == 0 { + return nil, errors.New("At least one query should be provided") + } + + jsonQuery := jsonQueries[0].Get("target") + + // TODO: inject auth token (obtain from 'user.login' first) + reqBodyJson, err := json.Marshal(map[string]interface{}{ + "jsonrpc": "2.0", + "id": 2, + "method": jsonQuery.Get("method").MustString(), + "params": jsonQuery.Get("params"), + }) + if err != nil { + return nil, err + } + + dsInfo := tsdbReq.GetDatasource() + zabbixUrlStr := dsInfo.GetUrl() + zabbixUrl, err := url.Parse(zabbixUrlStr) + if err != nil { + return nil, err + } + + jsonDataStr := dsInfo.GetJsonData() + jsonData, err := simplejson.NewJson([]byte(jsonDataStr)) + if err != nil { + return nil, err + } + + zabbixLogin := jsonData.Get("username").MustString() + // zabbixPassword := jsonData.Get("password").MustString() + ds.logger.Debug("ZabbixAPIQuery", "url", zabbixUrl, "user", zabbixLogin) + + var body io.Reader + body = bytes.NewReader(reqBodyJson) + rc, ok := body.(io.ReadCloser) + if !ok && body != nil { + rc = ioutil.NopCloser(body) + } + + req := &http.Request{ + Method: "POST", + URL: zabbixUrl, + Header: map[string][]string{ + "Content-Type": {"application/json"}, + }, + Body: rc, + } + + response, err := ds.MakeHttpRequest(ctx, req) + ds.logger.Debug("ZabbixAPIQuery", "response", string(response)) + return nil, errors.New("ZabbixAPIQuery is not implemented yet") } + +func (ds *ZabbixDatasource) MakeHttpRequest(ctx context.Context, req *http.Request) ([]byte, error) { + res, err := ctxhttp.Do(ctx, httpClient, req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("invalid status code. status: %v", res.Status) + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + return body, nil +} + +var httpClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + Renegotiation: tls.RenegotiateFreelyAsClient, + }, + Proxy: http.ProxyFromEnvironment, + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + }, + Timeout: time.Duration(time.Second * 30), +}