From c1d1fd65d1f1578d9acfb8614a61e05903492e59 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 17 Mar 2020 12:04:51 +0100 Subject: [PATCH 1/2] Alternatives for serving plugin --- backend/adapter.go | 3 +- backend/adapter_diagnostics_test.go | 6 +- backend/diagnostics/doc.go | 2 + backend/diagnostics/health_ok_handler.go | 21 ++++ backend/plugin/serve.go | 4 +- backend/resource/doc.go | 2 + backend/resource/not_found_handler.go | 22 ++++ backend/serve.go | 126 ++++++++++++++++++++++- go.sum | 3 + 9 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 backend/diagnostics/doc.go create mode 100644 backend/diagnostics/health_ok_handler.go create mode 100644 backend/resource/doc.go create mode 100644 backend/resource/not_found_handler.go diff --git a/backend/adapter.go b/backend/adapter.go index f5cf27531..395d95ae5 100644 --- a/backend/adapter.go +++ b/backend/adapter.go @@ -14,6 +14,7 @@ import ( // sdkAdapter adapter between low level plugin protocol and SDK interfaces. type sdkAdapter struct { + metricGatherer prometheus.Gatherer CheckHealthHandler CheckHealthHandler QueryDataHandler QueryDataHandler CallResourceHandler CallResourceHandler @@ -21,7 +22,7 @@ type sdkAdapter struct { } func (a *sdkAdapter) CollectMetrics(ctx context.Context, protoReq *pluginv2.CollectMetricsRequest) (*pluginv2.CollectMetricsResponse, error) { - mfs, err := prometheus.DefaultGatherer.Gather() + mfs, err := a.metricGatherer.Gather() if err != nil { return nil, err } diff --git a/backend/adapter_diagnostics_test.go b/backend/adapter_diagnostics_test.go index ac14491f8..1281b05ea 100644 --- a/backend/adapter_diagnostics_test.go +++ b/backend/adapter_diagnostics_test.go @@ -6,13 +6,17 @@ import ( "errors" "testing" + "github.com/prometheus/client_golang/prometheus" + "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" "github.com/prometheus/common/expfmt" "github.com/stretchr/testify/require" ) func TestCollectMetrcis(t *testing.T) { - adapter := &sdkAdapter{} + adapter := &sdkAdapter{ + metricGatherer: prometheus.DefaultGatherer, + } res, err := adapter.CollectMetrics(context.Background(), &pluginv2.CollectMetricsRequest{}) require.NoError(t, err) require.NotNil(t, res) diff --git a/backend/diagnostics/doc.go b/backend/diagnostics/doc.go new file mode 100644 index 000000000..43c66b7a5 --- /dev/null +++ b/backend/diagnostics/doc.go @@ -0,0 +1,2 @@ +// Package diagnostics provides support for handling health checks. +package diagnostics diff --git a/backend/diagnostics/health_ok_handler.go b/backend/diagnostics/health_ok_handler.go new file mode 100644 index 000000000..36ad1aad7 --- /dev/null +++ b/backend/diagnostics/health_ok_handler.go @@ -0,0 +1,21 @@ +package diagnostics + +import ( + "context" + + "github.com/grafana/grafana-plugin-sdk-go/backend" +) + +type okHandler struct { +} + +func (h *okHandler) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { + return &backend.CheckHealthResult{ + Status: backend.HealthStatusOk, + }, nil +} + +// OKCheckHealthHandler check health handler that returns backend.HealthStatusOk status. +func OKCheckHealthHandler() backend.CheckHealthHandler { + return &okHandler{} +} diff --git a/backend/plugin/serve.go b/backend/plugin/serve.go index 629a1f9f7..615cf9f06 100644 --- a/backend/plugin/serve.go +++ b/backend/plugin/serve.go @@ -18,7 +18,7 @@ type ServeOpts struct { } // Serve starts serving the plugin over gRPC. -func Serve(opts ServeOpts) error { +func Serve(opts ServeOpts) { versionedPlugins := make(map[int]plugin.PluginSet) pSet := make(plugin.PluginSet) @@ -57,6 +57,4 @@ func Serve(opts ServeOpts) error { VersionedPlugins: versionedPlugins, GRPCServer: opts.GRPCServer, }) - - return nil } diff --git a/backend/resource/doc.go b/backend/resource/doc.go new file mode 100644 index 000000000..45823c01f --- /dev/null +++ b/backend/resource/doc.go @@ -0,0 +1,2 @@ +// Package resource provides support for handling resource calls. +package resource diff --git a/backend/resource/not_found_handler.go b/backend/resource/not_found_handler.go new file mode 100644 index 000000000..cc20b5bbf --- /dev/null +++ b/backend/resource/not_found_handler.go @@ -0,0 +1,22 @@ +package resource + +import ( + "context" + "net/http" + + "github.com/grafana/grafana-plugin-sdk-go/backend" +) + +type notFoundHandler struct { +} + +func (h *notFoundHandler) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + return sender.Send(&backend.CallResourceResponse{ + Status: http.StatusNotFound, + }) +} + +// NotFoundHandler call resource handler that returns HTTP 404 status code. +func NotFoundHandler() backend.CallResourceHandler { + return ¬FoundHandler{} +} diff --git a/backend/serve.go b/backend/serve.go index 69c5f5484..039d36203 100644 --- a/backend/serve.go +++ b/backend/serve.go @@ -1,7 +1,9 @@ package backend import ( + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/plugin" + "github.com/prometheus/client_golang/prometheus" ) //ServeOpts options for serving plugins. @@ -12,9 +14,22 @@ type ServeOpts struct { TransformDataHandler TransformDataHandler } +type ConfigurePlugin struct { + Metrics prometheus.Registerer +} + +type ServePluginFunc func(logger log.Logger, c ConfigurePlugin) ServeOpts + // Serve starts serving the plugin over gRPC. -func Serve(opts ServeOpts) error { +func Serve(fn ServePluginFunc) { + logger := log.New() + c := ConfigurePlugin{ + Metrics: prometheus.DefaultRegisterer, + } + opts := fn(logger, c) + sdkAdapter := &sdkAdapter{ + metricGatherer: prometheus.DefaultGatherer, CheckHealthHandler: opts.CheckHealthHandler, CallResourceHandler: opts.CallResourceHandler, QueryDataHandler: opts.QueryDataHandler, @@ -37,5 +52,112 @@ func Serve(opts ServeOpts) error { pluginOpts.TransformServer = sdkAdapter } - return plugin.Serve(pluginOpts) + plugin.Serve(pluginOpts) +} + +type Plugin interface { + CheckHealthHandler + CallResourceHandler +} + +type DataSourcePlugin interface { + Plugin + QueryDataHandler +} + +type TransformPlugin interface { + TransformDataHandler +} + +type PluginFactoryFunc func(logger log.Logger, c ConfigurePlugin) Plugin +type DataSourcePluginFactoryFunc func(logger log.Logger, c ConfigurePlugin) DataSourcePlugin +type TransformPluginFactoryFunc func(logger log.Logger, c ConfigurePlugin) TransformPlugin + +//ServePluginOpts options for serving plugins. +type ServePluginOpts struct { + PluginProvider PluginFactoryFunc + DataSourcePluginProvider DataSourcePluginFactoryFunc + TransformPluginProvider TransformPluginFactoryFunc +} + +func servePluginExample(opts ServePluginOpts) { + logger := log.New() + c := ConfigurePlugin{ + Metrics: prometheus.DefaultRegisterer, + } + + sdkAdapter := &sdkAdapter{ + metricGatherer: prometheus.DefaultGatherer, + } + + if opts.PluginProvider != nil { + p := opts.PluginProvider(logger, c) + sdkAdapter.CheckHealthHandler = p + sdkAdapter.CallResourceHandler = p + + plugin.Serve(plugin.ServeOpts{ + DiagnosticsServer: sdkAdapter, + ResourceServer: sdkAdapter, + }) + return + } + + if opts.DataSourcePluginProvider != nil { + p := opts.DataSourcePluginProvider(logger, c) + sdkAdapter.CheckHealthHandler = p + sdkAdapter.CallResourceHandler = p + sdkAdapter.QueryDataHandler = p + + plugin.Serve(plugin.ServeOpts{ + DiagnosticsServer: sdkAdapter, + ResourceServer: sdkAdapter, + DataServer: sdkAdapter, + }) + return + } + + if opts.TransformPluginProvider != nil { + p := opts.TransformPluginProvider(logger, c) + sdkAdapter.TransformDataHandler = p + + plugin.Serve(plugin.ServeOpts{ + TransformServer: sdkAdapter, + }) + return + } + + panic("invalid arguments for serve plugin") +} + +// ServePlugin starts serving the plugin over gRPC. +func ServePlugin(factory PluginFactoryFunc) { + if factory == nil { + panic("factory func cannot be nil") + } + + servePluginExample(ServePluginOpts{ + PluginProvider: factory, + }) +} + +// ServeDataSourcePlugin starts serving the data source plugin over gRPC. +func ServeDataSourcePlugin(factory DataSourcePluginFactoryFunc) { + if factory == nil { + panic("factory func cannot be nil") + } + + servePluginExample(ServePluginOpts{ + DataSourcePluginProvider: factory, + }) +} + +// ServeTransformPlugin starts serving the plugin over gRPC. +func ServeTransformPlugin(factory TransformPluginFactoryFunc) { + if factory == nil { + panic("factory func cannot be nil") + } + + servePluginExample(ServePluginOpts{ + TransformPluginProvider: factory, + }) } diff --git a/go.sum b/go.sum index bbb238747..67ecc1cb4 100644 --- a/go.sum +++ b/go.sum @@ -89,6 +89,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= @@ -96,6 +98,7 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= From 57a3311b968b6cb66552895c83be9f1e13d590e3 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 1 Apr 2020 13:39:33 +0200 Subject: [PATCH 2/2] Exploratory: New datasource package for serving data source plugin --- backend/datasource/datasource.go | 85 ++++++++++++++++++++++++++++++++ backend/datasource/serve.go | 25 ++++++++++ backend/serve.go | 8 +-- 3 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 backend/datasource/datasource.go create mode 100644 backend/datasource/serve.go diff --git a/backend/datasource/datasource.go b/backend/datasource/datasource.go new file mode 100644 index 000000000..aa68e43c5 --- /dev/null +++ b/backend/datasource/datasource.go @@ -0,0 +1,85 @@ +package datasource + +import ( + "context" + + "github.com/grafana/grafana-plugin-sdk-go/backend" +) + +// CheckDataSourceHealthRequest contains the healthcheck request +type CheckDataSourceHealthRequest struct { + pluginConfig backend.PluginConfig + OrgID int64 + DataSourceConfig backend.DataSourceConfig +} + +// CheckDataSourceHealthHandler enables users to send health check +// requests to a data source plugin. +type CheckDataSourceHealthHandler interface { + CheckDataSourceHealth(ctx context.Context, req *CheckDataSourceHealthRequest) (*backend.CheckHealthResult, error) +} + +type CheckDataSourceHealthHandlerFunc func(ctx context.Context, req *CheckDataSourceHealthRequest) (*backend.CheckHealthResult, error) + +func (fn CheckDataSourceHealthHandlerFunc) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { + return fn(ctx, &CheckDataSourceHealthRequest{ + pluginConfig: req.PluginConfig, + OrgID: req.PluginConfig.OrgID, + DataSourceConfig: *(req.PluginConfig.DataSourceConfig), + }) +} + +type CallDataSourceResourceRequest struct { + pluginConfig backend.PluginConfig + OrgID int64 + DataSourceConfig backend.DataSourceConfig + Path string + Method string + URL string + Headers map[string][]string + Body []byte + User *backend.User +} + +// CallDataSourceResourceHandler handles resource calls. +type CallDataSourceResourceHandler interface { + CallDataSourceResource(ctx context.Context, req *CallDataSourceResourceRequest, sender backend.CallResourceResponseSender) error +} + +type CallDataSourceResourceHandlerFunc func(ctx context.Context, req *CallDataSourceResourceRequest, sender backend.CallResourceResponseSender) error + +func (fn CallDataSourceResourceHandlerFunc) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + return fn(ctx, &CallDataSourceResourceRequest{ + pluginConfig: req.PluginConfig, + OrgID: req.PluginConfig.OrgID, + DataSourceConfig: *(req.PluginConfig.DataSourceConfig), + User: req.User, + Path: req.Path, + URL: req.URL, + Method: req.Method, + Headers: req.Headers, + Body: req.Body, + }, sender) +} + +func NewCheckDataSourceHealthHandlerFunc(h backend.CheckHealthHandler) CheckDataSourceHealthHandlerFunc { + return func(ctx context.Context, req *CheckDataSourceHealthRequest) (*backend.CheckHealthResult, error) { + return h.CheckHealth(ctx, &backend.CheckHealthRequest{ + PluginConfig: req.pluginConfig, + }) + } +} + +func NewCallDataSourceResourceHandlerFunc(h backend.CallResourceHandler) CallDataSourceResourceHandlerFunc { + return func(ctx context.Context, req *CallDataSourceResourceRequest, sender backend.CallResourceResponseSender) error { + return h.CallResource(ctx, &backend.CallResourceRequest{ + PluginConfig: req.pluginConfig, + User: req.User, + Path: req.Path, + URL: req.URL, + Method: req.Method, + Headers: req.Headers, + Body: req.Body, + }, sender) + } +} diff --git a/backend/datasource/serve.go b/backend/datasource/serve.go new file mode 100644 index 000000000..b12636da8 --- /dev/null +++ b/backend/datasource/serve.go @@ -0,0 +1,25 @@ +package datasource + +import ( + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" +) + +type Plugin interface { + CheckDataSourceHealthHandler + CallDataSourceResourceHandler + backend.QueryDataHandler +} + +type PluginFactoryFunc func(logger log.Logger, c backend.ConfigurePlugin) Plugin + +func Serve(fn PluginFactoryFunc) { + backend.Serve(func(logger log.Logger, c backend.ConfigurePlugin) backend.ServeOpts { + ds := fn(logger, c) + return backend.ServeOpts{ + CheckHealthHandler: CheckDataSourceHealthHandlerFunc(ds.CheckDataSourceHealth), + CallResourceHandler: CallDataSourceResourceHandlerFunc(ds.CallDataSourceResource), + QueryDataHandler: ds, + } + }) +} diff --git a/backend/serve.go b/backend/serve.go index 5b7a2cf5d..9e8b5fb82 100644 --- a/backend/serve.go +++ b/backend/serve.go @@ -72,7 +72,7 @@ type ServePluginOpts struct { TransformPluginProvider TransformPluginFactoryFunc } -func servePluginExample(opts ServePluginOpts) { +func ServePluginExample(opts ServePluginOpts) { logger := log.New() c := ConfigurePlugin{ Metrics: prometheus.DefaultRegisterer, @@ -114,7 +114,7 @@ func ServePlugin(factory PluginFactoryFunc) { panic("factory func cannot be nil") } - servePluginExample(ServePluginOpts{ + ServePluginExample(ServePluginOpts{ PluginProvider: factory, }) } @@ -125,7 +125,7 @@ func ServeDataSourcePlugin(factory DataSourcePluginFactoryFunc) { panic("factory func cannot be nil") } - servePluginExample(ServePluginOpts{ + ServePluginExample(ServePluginOpts{ DataSourcePluginProvider: factory, }) } @@ -136,7 +136,7 @@ func ServeTransformPlugin(factory TransformPluginFactoryFunc) { panic("factory func cannot be nil") } - servePluginExample(ServePluginOpts{ + ServePluginExample(ServePluginOpts{ TransformPluginProvider: factory, }) }