From 80e1191ca064841713dd30fdbcfe3370727ef7ca Mon Sep 17 00:00:00 2001 From: Kyle Brandt Date: Thu, 23 Apr 2020 14:08:21 -0400 Subject: [PATCH] Backend Plugins: (breaking change) Add PluginContext (#23788) * breaking change for newer backend plugins * use exported protobuf converters and sdk types to reduce duplicate code * uses grafana-plugin-sdk-go@v0.54.0 --- go.mod | 4 +- go.sum | 21 +++- pkg/api/datasources.go | 61 +++++------- pkg/api/plugins.go | 44 +++++---- pkg/plugins/backendplugin/backend_plugin.go | 99 ++----------------- pkg/plugins/backendplugin/contracts.go | 36 ------- pkg/plugins/backendplugin/manager.go | 42 ++++---- .../wrapper/datasource_plugin_wrapper_v2.go | 67 ++++++++----- pkg/plugins/transform_plugin.go | 10 +- pkg/tsdb/frame_util.go | 2 +- 10 files changed, 151 insertions(+), 235 deletions(-) diff --git a/go.mod b/go.mod index fbaadbf0137..e6572a67c9f 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/gorilla/websocket v1.4.1 github.com/gosimple/slug v1.4.2 github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 - github.com/grafana/grafana-plugin-sdk-go v0.42.0 + github.com/grafana/grafana-plugin-sdk-go v0.54.0 github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd github.com/hashicorp/go-plugin v1.2.2 github.com/hashicorp/go-version v1.1.0 @@ -76,7 +76,7 @@ require ( golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect - golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 google.golang.org/grpc v1.27.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect diff --git a/go.sum b/go.sum index 5bf037ea29a..f39a60310b8 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/apache/arrow/go/arrow v0.0.0-20191025121910-b789226ccb21 h1:xI+FQ/TsyD7jy22vP9wQBtJsPFRQTLZHfrr1IMLl0g0= -github.com/apache/arrow/go/arrow v0.0.0-20191025121910-b789226ccb21/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= +github.com/apache/arrow/go/arrow v0.0.0-20200403134915-89ce1cadb678 h1:R72+9UXiP7TnpTAdznM1okjzyqb3bzopSA7HCP7p3gM= +github.com/apache/arrow/go/arrow v0.0.0-20200403134915-89ce1cadb678/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/aws/aws-sdk-go v1.25.48 h1:J82DYDGZHOKHdhx6hD24Tm30c2C3GchYGfN0mf9iKUk= github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -98,6 +98,7 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -133,8 +134,12 @@ github.com/gosimple/slug v1.4.2 h1:jDmprx3q/9Lfk4FkGZtvzDQ9Cj9eAmsjzeQGp24PeiQ= github.com/gosimple/slug v1.4.2/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0= github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 h1:SPdxCL9BChFTlyi0Khv64vdCW4TMna8+sxL7+Chx+Ag= github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4/go.mod h1:nc0XxBzjeGcrMltCDw269LoWF9S8ibhgxolCdA1R8To= -github.com/grafana/grafana-plugin-sdk-go v0.42.0 h1:8oiAQa/uABBFT70GDAv3BnqHfdMOxy/P8SzYVURJH6Y= -github.com/grafana/grafana-plugin-sdk-go v0.42.0/go.mod h1:xRhfTHl+Dkqf2Py6Lr4pcHBC5pm8/N+IwPJ0R/iAHMM= +github.com/grafana/grafana-plugin-sdk-go v0.54.0 h1:DTrS6LmEo2gUfq2OLDTraNC1+vZKT8LO+hrOXJPJ2/c= +github.com/grafana/grafana-plugin-sdk-go v0.54.0/go.mod h1:cJqtJv4uuWOB3SAUToTMa8yQoNxEtF07CabXHwsr5fc= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd h1:rNuUHR+CvK1IS89MMtcF0EpcVMZtjKfPRp4MEmt/aTs= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= @@ -169,6 +174,7 @@ github.com/jung-kurt/gofpdf v1.10.1 h1:mbprTswkr0n86clAmJ4NGCFC4fdGySCAshWzrYb3x github.com/jung-kurt/gofpdf v1.10.1/go.mod h1:s/VXv+TdctEOx2wCEguezYaR7f0OwUAd6H9VGfRkcSs= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -188,6 +194,7 @@ github.com/linkedin/goavro/v2 v2.9.7 h1:Vd++Rb/RKcmNJjM0HP/JJFMEWa21eUBVKPYlKehO github.com/linkedin/goavro/v2 v2.9.7/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA= github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ= github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0= +github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/mattetti/filebuffer v1.0.0 h1:ixTvQ0JjBTwWbdpDZ98lLrydo7KRi8xNRIi5RFszsbY= github.com/mattetti/filebuffer v1.0.0/go.mod h1:X6nyAIge2JGVmuJt2MFCqmHrb/5IHiphfHtot0s5cnI= @@ -320,8 +327,11 @@ github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -390,6 +400,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -408,6 +419,8 @@ golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapK golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 3b2f18c00f5..25a80b985f5 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -6,11 +6,13 @@ import ( "net/url" "sort" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/backendplugin" + "github.com/grafana/grafana/pkg/plugins/datasource/wrapper" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util/errutil" ) @@ -296,23 +298,18 @@ func (hs *HTTPServer) CallDatasourceResource(c *models.ReqContext) { return } - config := backendplugin.PluginConfig{ - OrgID: c.OrgId, - PluginID: plugin.Id, - DataSourceConfig: &backendplugin.DataSourceConfig{ - ID: ds.Id, - Name: ds.Name, - URL: ds.Url, - Database: ds.Database, - User: ds.User, - BasicAuthEnabled: ds.BasicAuth, - BasicAuthUser: ds.BasicAuthUser, - JSONData: ds.JsonData, - DecryptedSecureJSONData: ds.DecryptedValues(), - Updated: ds.Updated, - }, - } - hs.BackendPluginManager.CallResource(config, c, c.Params("*")) + dsInstanceSettings, err := wrapper.ModelToInstanceSettings(ds) + if err != nil { + c.JsonApiErr(500, "Unable to process datasource instance model", err) + } + + pCtx := backend.PluginContext{ + User: wrapper.BackendUserFromSignedInUser(c.SignedInUser), + OrgID: c.OrgId, + PluginID: plugin.Id, + DataSourceInstanceSettings: dsInstanceSettings, + } + hs.BackendPluginManager.CallResource(pCtx, c, c.Params("*")) } func convertModelToDtos(ds *models.DataSource) dtos.DataSource { @@ -367,24 +364,18 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) { return } - config := &backendplugin.PluginConfig{ - OrgID: c.OrgId, - PluginID: plugin.Id, - DataSourceConfig: &backendplugin.DataSourceConfig{ - ID: ds.Id, - Name: ds.Name, - URL: ds.Url, - Database: ds.Database, - User: ds.User, - BasicAuthEnabled: ds.BasicAuth, - BasicAuthUser: ds.BasicAuthUser, - JSONData: ds.JsonData, - DecryptedSecureJSONData: ds.DecryptedValues(), - Updated: ds.Updated, - }, - } - - resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), config) + dsInstanceSettings, err := wrapper.ModelToInstanceSettings(ds) + if err != nil { + c.JsonApiErr(500, "Unable to get datasource model", err) + } + pCtx := backend.PluginContext{ + User: wrapper.BackendUserFromSignedInUser(c.SignedInUser), + OrgID: c.OrgId, + PluginID: plugin.Id, + DataSourceInstanceSettings: dsInstanceSettings, + } + + resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), pCtx) if err != nil { if err == backendplugin.ErrPluginNotRegistered { c.JsonApiErr(404, "Plugin not found", err) diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index 28c31479bd7..3262dd16335 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -7,49 +7,56 @@ import ( "sort" "time" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/util/errutil" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/backendplugin" + "github.com/grafana/grafana/pkg/plugins/datasource/wrapper" "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util/errutil" ) // ErrPluginNotFound is returned when an requested plugin is not installed. var ErrPluginNotFound error = errors.New("plugin not found, no installed plugin with that id") -func (hs *HTTPServer) getPluginConfig(pluginID string, user *models.SignedInUser) (backendplugin.PluginConfig, error) { - pluginConfig := backendplugin.PluginConfig{} +func (hs *HTTPServer) getPluginContext(pluginID string, user *models.SignedInUser) (backend.PluginContext, error) { + pc := backend.PluginContext{} plugin, exists := plugins.Plugins[pluginID] if !exists { - return pluginConfig, ErrPluginNotFound + return pc, ErrPluginNotFound } - var jsonData *simplejson.Json + var jsonData json.RawMessage var decryptedSecureJSONData map[string]string var updated time.Time ps, err := hs.getCachedPluginSettings(pluginID, user) if err != nil { if err != models.ErrPluginSettingNotFound { - return pluginConfig, errutil.Wrap("Failed to get plugin settings", err) + return pc, errutil.Wrap("Failed to get plugin settings", err) + } + jsonData, err = json.Marshal(ps.JsonData) + if err != nil { + return pc, errutil.Wrap("Failed to unmarshal plugin json data", err) } - jsonData = simplejson.New() decryptedSecureJSONData = make(map[string]string) } else { decryptedSecureJSONData = ps.DecryptedValues() updated = ps.Updated } - return backendplugin.PluginConfig{ - OrgID: user.OrgId, - PluginID: plugin.Id, - JSONData: jsonData, - DecryptedSecureJSONData: decryptedSecureJSONData, - Updated: updated, + return backend.PluginContext{ + OrgID: user.OrgId, + PluginID: plugin.Id, + User: wrapper.BackendUserFromSignedInUser(user), + AppInstanceSettings: &backend.AppInstanceSettings{ + JSONData: jsonData, + DecryptedSecureJSONData: decryptedSecureJSONData, + Updated: updated, + }, }, nil } @@ -289,7 +296,7 @@ func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) Response { func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response { pluginID := c.Params("pluginId") - config, err := hs.getPluginConfig(pluginID, c.SignedInUser) + pCtx, err := hs.getPluginContext(pluginID, c.SignedInUser) if err != nil { if err == ErrPluginNotFound { return Error(404, "Plugin not found, no installed plugin with that id", nil) @@ -298,7 +305,7 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response { return Error(500, "Failed to get plugin settings", err) } - resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), &config) + resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), pCtx) if err != nil { if err == backendplugin.ErrPluginNotRegistered { return Error(404, "Plugin not found", err) @@ -346,7 +353,7 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response { func (hs *HTTPServer) CallResource(c *models.ReqContext) { pluginID := c.Params("pluginId") - config, err := hs.getPluginConfig(pluginID, c.SignedInUser) + pCtx, err := hs.getPluginContext(pluginID, c.SignedInUser) if err != nil { if err == ErrPluginNotFound { c.JsonApiErr(404, "Plugin not found, no installed plugin with that id", nil) @@ -356,8 +363,7 @@ func (hs *HTTPServer) CallResource(c *models.ReqContext) { c.JsonApiErr(500, "Failed to get plugin settings", err) return } - - hs.BackendPluginManager.CallResource(config, c, c.Params("*")) + hs.BackendPluginManager.CallResource(pCtx, c, c.Params("*")) } func (hs *HTTPServer) getCachedPluginSettings(pluginID string, user *models.SignedInUser) (*models.PluginSetting, error) { diff --git a/pkg/plugins/backendplugin/backend_plugin.go b/pkg/plugins/backendplugin/backend_plugin.go index fc99d949a2a..673525f574b 100644 --- a/pkg/plugins/backendplugin/backend_plugin.go +++ b/pkg/plugins/backendplugin/backend_plugin.go @@ -4,10 +4,10 @@ import ( "context" "errors" "net/http" - "time" datasourceV1 "github.com/grafana/grafana-plugin-model/go/datasource" rendererV1 "github.com/grafana/grafana-plugin-model/go/renderer" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2" @@ -175,50 +175,21 @@ func (p *BackendPlugin) CollectMetrics(ctx context.Context) (*pluginv2.CollectMe return res, nil } -func (p *BackendPlugin) checkHealth(ctx context.Context, config *PluginConfig) (*pluginv2.CheckHealthResponse, error) { +var toProto = backend.ToProto() + +func (p *BackendPlugin) checkHealth(ctx context.Context, pCtx backend.PluginContext) (*pluginv2.CheckHealthResponse, error) { if p.diagnostics == nil || p.client == nil || p.client.Exited() { return &pluginv2.CheckHealthResponse{ Status: pluginv2.CheckHealthResponse_UNKNOWN, }, nil } - jsonDataBytes, err := config.JSONData.ToDB() - if err != nil { - return nil, err - } - - pconfig := &pluginv2.PluginConfig{ - OrgId: config.OrgID, - PluginId: config.PluginID, - JsonData: jsonDataBytes, - DecryptedSecureJsonData: config.DecryptedSecureJSONData, - LastUpdatedMS: config.Updated.UnixNano() / int64(time.Millisecond), - } - - if config.DataSourceConfig != nil { - datasourceJSONData, err := config.DataSourceConfig.JSONData.ToDB() - if err != nil { - return nil, err - } - - pconfig.DatasourceConfig = &pluginv2.DataSourceConfig{ - Id: config.DataSourceConfig.ID, - Name: config.DataSourceConfig.Name, - Url: config.DataSourceConfig.URL, - User: config.DataSourceConfig.User, - Database: config.DataSourceConfig.Database, - BasicAuthEnabled: config.DataSourceConfig.BasicAuthEnabled, - BasicAuthUser: config.DataSourceConfig.BasicAuthUser, - JsonData: datasourceJSONData, - DecryptedSecureJsonData: config.DataSourceConfig.DecryptedSecureJSONData, - LastUpdatedMS: config.DataSourceConfig.Updated.Unix() / int64(time.Millisecond), - } - } + protoContext := toProto.PluginContext(pCtx) var res *pluginv2.CheckHealthResponse - err = InstrumentPluginRequest(p.id, "checkhealth", func() error { + err := InstrumentPluginRequest(p.id, "checkhealth", func() error { var innerErr error - res, innerErr = p.diagnostics.CheckHealth(ctx, &pluginv2.CheckHealthRequest{Config: pconfig}) + res, innerErr = p.diagnostics.CheckHealth(ctx, &pluginv2.CheckHealthRequest{PluginContext: protoContext}) return innerErr }) @@ -237,66 +208,14 @@ func (p *BackendPlugin) checkHealth(ctx context.Context, config *PluginConfig) ( return res, nil } -func (p *BackendPlugin) callResource(ctx context.Context, req CallResourceRequest) (callResourceResultStream, error) { +func (p *BackendPlugin) callResource(ctx context.Context, req *backend.CallResourceRequest) (callResourceResultStream, error) { p.logger.Debug("Calling resource", "path", req.Path, "method", req.Method) if p.resource == nil || p.client == nil || p.client.Exited() { return nil, errors.New("plugin not running, cannot call resource") } - reqHeaders := map[string]*pluginv2.StringList{} - for k, v := range req.Headers { - reqHeaders[k] = &pluginv2.StringList{Values: v} - } - - jsonDataBytes, err := req.Config.JSONData.ToDB() - if err != nil { - return nil, err - } - - protoReq := &pluginv2.CallResourceRequest{ - Config: &pluginv2.PluginConfig{ - OrgId: req.Config.OrgID, - PluginId: req.Config.PluginID, - JsonData: jsonDataBytes, - DecryptedSecureJsonData: req.Config.DecryptedSecureJSONData, - LastUpdatedMS: req.Config.Updated.UnixNano() / int64(time.Millisecond), - }, - Path: req.Path, - Method: req.Method, - Url: req.URL, - Headers: reqHeaders, - Body: req.Body, - } - - if req.User != nil { - protoReq.User = &pluginv2.User{ - Name: req.User.Name, - Login: req.User.Login, - Email: req.User.Email, - Role: string(req.User.OrgRole), - } - } - - if req.Config.DataSourceConfig != nil { - datasourceJSONData, err := req.Config.DataSourceConfig.JSONData.ToDB() - if err != nil { - return nil, err - } - - protoReq.Config.DatasourceConfig = &pluginv2.DataSourceConfig{ - Id: req.Config.DataSourceConfig.ID, - Name: req.Config.DataSourceConfig.Name, - Url: req.Config.DataSourceConfig.URL, - Database: req.Config.DataSourceConfig.Database, - User: req.Config.DataSourceConfig.User, - BasicAuthEnabled: req.Config.DataSourceConfig.BasicAuthEnabled, - BasicAuthUser: req.Config.DataSourceConfig.BasicAuthUser, - JsonData: datasourceJSONData, - DecryptedSecureJsonData: req.Config.DataSourceConfig.DecryptedSecureJSONData, - LastUpdatedMS: req.Config.DataSourceConfig.Updated.UnixNano() / int64(time.Millisecond), - } - } + protoReq := toProto.CallResourceRequest(req) protoStream, err := p.resource.CallResource(ctx, protoReq) if err != nil { diff --git a/pkg/plugins/backendplugin/contracts.go b/pkg/plugins/backendplugin/contracts.go index 0ca66d14dce..37ae086de99 100644 --- a/pkg/plugins/backendplugin/contracts.go +++ b/pkg/plugins/backendplugin/contracts.go @@ -2,10 +2,6 @@ package backendplugin import ( "strconv" - "time" - - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" ) @@ -76,38 +72,6 @@ type CollectMetricsResult struct { PrometheusMetrics []byte } -type DataSourceConfig struct { - ID int64 - Name string - URL string - User string - Database string - BasicAuthEnabled bool - BasicAuthUser string - JSONData *simplejson.Json - DecryptedSecureJSONData map[string]string - Updated time.Time -} - -type PluginConfig struct { - OrgID int64 - PluginID string - JSONData *simplejson.Json - DecryptedSecureJSONData map[string]string - Updated time.Time - DataSourceConfig *DataSourceConfig -} - -type CallResourceRequest struct { - Config PluginConfig - Path string - Method string - URL string - Headers map[string][]string - Body []byte - User *models.SignedInUser -} - // CallResourceResult call resource result. type CallResourceResult struct { Status int diff --git a/pkg/plugins/backendplugin/manager.go b/pkg/plugins/backendplugin/manager.go index 21a5af24504..677d95ced62 100644 --- a/pkg/plugins/backendplugin/manager.go +++ b/pkg/plugins/backendplugin/manager.go @@ -2,12 +2,14 @@ package backendplugin import ( "context" + "encoding/json" "errors" "fmt" "io" "sync" "time" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util/errutil" @@ -45,9 +47,9 @@ type Manager interface { // CollectMetrics collects metrics from a registered backend plugin. CollectMetrics(ctx context.Context, pluginID string) (*CollectMetricsResult, error) // CheckHealth checks the health of a registered backend plugin. - CheckHealth(ctx context.Context, pluginConfig *PluginConfig) (*CheckHealthResult, error) + CheckHealth(ctx context.Context, pCtx backend.PluginContext) (*CheckHealthResult, error) // CallResource calls a plugin resource. - CallResource(pluginConfig PluginConfig, ctx *models.ReqContext, path string) + CallResource(pluginConfig backend.PluginContext, ctx *models.ReqContext, path string) } type manager struct { @@ -187,7 +189,7 @@ func (m *manager) CollectMetrics(ctx context.Context, pluginID string) (*Collect } // CheckHealth checks the health of a registered backend plugin. -func (m *manager) CheckHealth(ctx context.Context, pluginConfig *PluginConfig) (*CheckHealthResult, error) { +func (m *manager) CheckHealth(ctx context.Context, pluginConfig backend.PluginContext) (*CheckHealthResult, error) { m.pluginsMu.RLock() p, registered := m.plugins[pluginConfig.PluginID] m.pluginsMu.RUnlock() @@ -209,10 +211,14 @@ func (m *manager) CheckHealth(ctx context.Context, pluginConfig *PluginConfig) ( return checkHealthResultFromProto(res), nil } +type keepCookiesJSONModel struct { + KeepCookies []string `json:"keepCookies"` +} + // CallResource calls a plugin resource. -func (m *manager) CallResource(config PluginConfig, reqCtx *models.ReqContext, path string) { +func (m *manager) CallResource(pCtx backend.PluginContext, reqCtx *models.ReqContext, path string) { m.pluginsMu.RLock() - p, registered := m.plugins[config.PluginID] + p, registered := m.plugins[pCtx.PluginID] m.pluginsMu.RUnlock() if !registered { @@ -221,14 +227,15 @@ func (m *manager) CallResource(config PluginConfig, reqCtx *models.ReqContext, p } clonedReq := reqCtx.Req.Clone(reqCtx.Req.Context()) - keepCookieNames := []string{} - if config.JSONData != nil { - if keepCookies := config.JSONData.Get("keepCookies"); keepCookies != nil { - keepCookieNames = keepCookies.MustStringArray() + keepCookieModel := keepCookiesJSONModel{} + if dis := pCtx.DataSourceInstanceSettings; dis != nil { + err := json.Unmarshal(dis.JSONData, &keepCookieModel) + if err != nil { + p.logger.Error("Failed to to unpack JSONData in datasource instance settings", "error", err) } } - proxyutil.ClearCookieHeader(clonedReq, keepCookieNames) + proxyutil.ClearCookieHeader(clonedReq, keepCookieModel.KeepCookies) proxyutil.PrepareProxyRequest(clonedReq) body, err := reqCtx.Req.Body().Bytes() @@ -237,14 +244,13 @@ func (m *manager) CallResource(config PluginConfig, reqCtx *models.ReqContext, p return } - req := CallResourceRequest{ - Config: config, - Path: path, - Method: clonedReq.Method, - URL: clonedReq.URL.String(), - Headers: clonedReq.Header, - Body: body, - User: reqCtx.SignedInUser, + req := &backend.CallResourceRequest{ + PluginContext: pCtx, + Path: path, + Method: clonedReq.Method, + URL: clonedReq.URL.String(), + Headers: clonedReq.Header, + Body: body, } err = InstrumentPluginRequest(p.id, "resource", func() error { diff --git a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go index 6f1ae8d838f..5163e0030cc 100644 --- a/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go +++ b/pkg/plugins/datasource/wrapper/datasource_plugin_wrapper_v2.go @@ -3,11 +3,11 @@ package wrapper import ( "context" "fmt" - "time" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/plugins/backendplugin" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" @@ -25,41 +25,42 @@ type DatasourcePluginWrapperV2 struct { pluginType string } -func (tw *DatasourcePluginWrapperV2) Query(ctx context.Context, ds *models.DataSource, query *tsdb.TsdbQuery) (*tsdb.Response, error) { +func ModelToInstanceSettings(ds *models.DataSource) (*backend.DataSourceInstanceSettings, error) { jsonDataBytes, err := ds.JsonData.MarshalJSON() if err != nil { return nil, err } + return &backend.DataSourceInstanceSettings{ + ID: ds.Id, + Name: ds.Name, + URL: ds.Url, + Database: ds.Database, + User: ds.User, + BasicAuthEnabled: ds.BasicAuth, + BasicAuthUser: ds.BasicAuthUser, + JSONData: jsonDataBytes, + DecryptedSecureJSONData: ds.DecryptedValues(), + Updated: ds.Updated, + }, nil +} + +func (tw *DatasourcePluginWrapperV2) Query(ctx context.Context, ds *models.DataSource, query *tsdb.TsdbQuery) (*tsdb.Response, error) { + instanceSettings, err := ModelToInstanceSettings(ds) + if err != nil { + return nil, err + } + pbQuery := &pluginv2.QueryDataRequest{ - Config: &pluginv2.PluginConfig{ - OrgId: ds.OrgId, - PluginId: tw.pluginId, - LastUpdatedMS: ds.Updated.UnixNano() / int64(time.Millisecond), - DatasourceConfig: &pluginv2.DataSourceConfig{ - Id: ds.Id, - Name: ds.Name, - Url: ds.Url, - Database: ds.Database, - User: ds.User, - BasicAuthEnabled: ds.BasicAuth, - BasicAuthUser: ds.BasicAuthUser, - JsonData: jsonDataBytes, - DecryptedSecureJsonData: ds.DecryptedValues(), - }, + PluginContext: &pluginv2.PluginContext{ + OrgId: ds.OrgId, + PluginId: tw.pluginId, + User: backend.ToProto().User(BackendUserFromSignedInUser(query.User)), + DataSourceInstanceSettings: backend.ToProto().DataSourceInstanceSettings(instanceSettings), }, Queries: []*pluginv2.DataQuery{}, } - if query.User != nil { - pbQuery.User = &pluginv2.User{ - Name: query.User.Name, - Login: query.User.Login, - Email: query.User.Email, - Role: string(query.User.OrgRole), - } - } - for _, q := range query.Queries { modelJSON, err := q.Model.MarshalJSON() if err != nil { @@ -110,3 +111,17 @@ func (tw *DatasourcePluginWrapperV2) Query(ctx context.Context, ds *models.DataS return tR, nil } + +// BackendUserFromSignedInUser converts Grafana's SignedInUser model +// to the backend plugin's model. +func BackendUserFromSignedInUser(su *models.SignedInUser) *backend.User { + if su == nil { + return nil + } + return &backend.User{ + Login: su.Login, + Name: su.Name, + Email: su.Name, + Role: string(su.OrgRole), + } +} diff --git a/pkg/plugins/transform_plugin.go b/pkg/plugins/transform_plugin.go index bbbd4d008ef..2d30a63f822 100644 --- a/pkg/plugins/transform_plugin.go +++ b/pkg/plugins/transform_plugin.go @@ -77,7 +77,9 @@ type TransformWrapper struct { func (tw *TransformWrapper) Transform(ctx context.Context, query *tsdb.TsdbQuery) (*tsdb.Response, error) { pbQuery := &pluginv2.QueryDataRequest{ - Config: &pluginv2.PluginConfig{}, + PluginContext: &pluginv2.PluginContext{ + // TODO: Things probably + }, Queries: []*pluginv2.DataQuery{}, } @@ -134,12 +136,12 @@ func (s *transformCallback) QueryData(ctx context.Context, req *pluginv2.QueryDa datasourceID := int64(0) - if req.Config.DatasourceConfig != nil { - datasourceID = req.Config.DatasourceConfig.Id + if req.PluginContext.DataSourceInstanceSettings != nil { + datasourceID = req.PluginContext.DataSourceInstanceSettings.Id } getDsInfo := &models.GetDataSourceByIdQuery{ - OrgId: req.Config.OrgId, + OrgId: req.PluginContext.OrgId, Id: datasourceID, } diff --git a/pkg/tsdb/frame_util.go b/pkg/tsdb/frame_util.go index 6add81f43fc..d75542c29e9 100644 --- a/pkg/tsdb/frame_util.go +++ b/pkg/tsdb/frame_util.go @@ -49,7 +49,7 @@ func FrameToSeriesSlice(frame *data.Frame) (TimeSeriesSlice, error) { // If Long, make wide if tsSchema.Type == data.TimeSeriesTypeLong { var err error - frame, err = data.LongToWide(frame) + frame, err = data.LongToWide(frame, nil) if err != nil { return nil, errutil.Wrap("failed to convert long to wide series when converting from dataframe", err) }