diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index 803d966ee18..39de6911008 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "sync" "time" "github.com/grafana/grafana/pkg/api/cloudwatch" @@ -18,7 +19,29 @@ import ( "github.com/grafana/grafana/pkg/util" ) +type proxyTransportCache struct { + cache map[int64]cachedTransport + sync.Mutex +} + +type cachedTransport struct { + updated time.Time + + *http.Transport +} + +var ptc = proxyTransportCache{ + cache: make(map[int64]cachedTransport), +} + func DataProxyTransport(ds *m.DataSource) (*http.Transport, error) { + ptc.Lock() + defer ptc.Unlock() + + if t, present := ptc.cache[ds.Id]; present && ds.Updated.Equal(t.updated) { + return t.Transport, nil + } + transport := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, @@ -56,6 +79,12 @@ func DataProxyTransport(ds *m.DataSource) (*http.Transport, error) { } transport.TLSClientConfig.Certificates = []tls.Certificate{cert} } + + ptc.cache[ds.Id] = cachedTransport{ + Transport: transport, + updated: ds.Updated, + } + return transport, nil } diff --git a/pkg/api/dataproxy_test.go b/pkg/api/dataproxy_test.go index 5e6e62de0a3..50abf317e8a 100644 --- a/pkg/api/dataproxy_test.go +++ b/pkg/api/dataproxy_test.go @@ -4,6 +4,7 @@ import ( "net/http" "net/url" "testing" + "time" . "github.com/smartystreets/goconvey/convey" @@ -16,6 +17,7 @@ import ( func TestDataSourceProxy(t *testing.T) { Convey("When getting graphite datasource proxy", t, func() { + clearCache() ds := m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE} targetUrl, err := url.Parse(ds.Url) proxy := NewReverseProxy(&ds, "/render", targetUrl) @@ -38,6 +40,7 @@ func TestDataSourceProxy(t *testing.T) { }) Convey("When getting influxdb datasource proxy", t, func() { + clearCache() ds := m.DataSource{ Type: m.DS_INFLUXDB_08, Url: "http://influxdb:8083", @@ -65,38 +68,85 @@ func TestDataSourceProxy(t *testing.T) { }) }) + Convey("When caching a datasource proxy", t, func() { + clearCache() + ds := m.DataSource{ + Id: 1, + Url: "http://k8s:8001", + Type: "Kubernetes", + } + + t1, err := DataProxyTransport(&ds) + So(err, ShouldBeNil) + + t2, err := DataProxyTransport(&ds) + So(err, ShouldBeNil) + + Convey("Should be using the cached proxy", func() { + So(t2, ShouldEqual, t1) + }) + }) + Convey("When getting kubernetes datasource proxy", t, func() { + clearCache() setting.SecretKey = "password" json := simplejson.New() json.Set("tlsAuth", true) json.Set("tlsAuthWithCACert", true) + + t := time.Now() ds := m.DataSource{ - Url: "htttp://k8s:8001", - Type: "Kubernetes", - JsonData: json, - SecureJsonData: map[string][]byte{ - "tlsCACert": util.Encrypt([]byte(caCert), "password"), - "tlsClientCert": util.Encrypt([]byte(clientCert), "password"), - "tlsClientKey": util.Encrypt([]byte(clientKey), "password"), - }, + Url: "http://k8s:8001", + Type: "Kubernetes", + Updated: t.Add(-2 * time.Minute), } - targetUrl, err := url.Parse(ds.Url) - proxy := NewReverseProxy(&ds, "", targetUrl) - proxy.Transport, err = DataProxyTransport(&ds) + + transport, err := DataProxyTransport(&ds) So(err, ShouldBeNil) - transport, ok := proxy.Transport.(*http.Transport) + Convey("Should have no cert", func() { + So(transport.TLSClientConfig.InsecureSkipVerify, ShouldEqual, true) + }) + + ds.JsonData = json + ds.SecureJsonData = map[string][]byte{ + "tlsCACert": util.Encrypt([]byte(caCert), "password"), + "tlsClientCert": util.Encrypt([]byte(clientCert), "password"), + "tlsClientKey": util.Encrypt([]byte(clientKey), "password"), + } + ds.Updated = t.Add(-1 * time.Minute) + + transport, err = DataProxyTransport(&ds) + So(err, ShouldBeNil) Convey("Should add cert", func() { - So(ok, ShouldBeTrue) So(transport.TLSClientConfig.InsecureSkipVerify, ShouldEqual, false) So(len(transport.TLSClientConfig.Certificates), ShouldEqual, 1) }) + + ds.JsonData = nil + ds.SecureJsonData = map[string][]byte{} + ds.Updated = t + + transport, err = DataProxyTransport(&ds) + So(err, ShouldBeNil) + + Convey("Should remove cert", func() { + So(transport.TLSClientConfig.InsecureSkipVerify, ShouldEqual, true) + So(len(transport.TLSClientConfig.Certificates), ShouldEqual, 0) + }) }) } +func clearCache() { + ptc.Lock() + defer ptc.Unlock() + + ptc.cache = make(map[int64]cachedTransport) +} + const caCert string = `-----BEGIN CERTIFICATE----- MIIDATCCAemgAwIBAgIJAMQ5hC3CPDTeMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV BAMMDGNhLWs4cy1zdGhsbTAeFw0xNjEwMjcwODQyMjdaFw00NDAzMTQwODQyMjda