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/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/grpcplugin/serve.go b/backend/grpcplugin/serve.go index b1f9622bb..757649b5b 100644 --- a/backend/grpcplugin/serve.go +++ b/backend/grpcplugin/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 bb880b158..43eadd812 100644 --- a/backend/serve.go +++ b/backend/serve.go @@ -2,6 +2,7 @@ package backend import ( "github.com/grafana/grafana-plugin-sdk-go/backend/grpcplugin" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/prometheus/client_golang/prometheus" ) @@ -13,8 +14,20 @@ 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) + pluginOpts := grpcplugin.ServeOpts{ DiagnosticsServer: newDiagnosticsSDKAdapter(prometheus.DefaultGatherer, opts.CheckHealthHandler), } @@ -31,5 +44,99 @@ func Serve(opts ServeOpts) error { pluginOpts.TransformServer = newTransformSDKAdapter(opts.TransformDataHandler) } - return grpcplugin.Serve(pluginOpts) + grpcplugin.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, + } + + if opts.PluginProvider != nil { + p := opts.PluginProvider(logger, c) + grpcplugin.Serve(grpcplugin.ServeOpts{ + DiagnosticsServer: newDiagnosticsSDKAdapter(prometheus.DefaultGatherer, p), + ResourceServer: newResourceSDKAdapter(p), + }) + return + } + + if opts.DataSourcePluginProvider != nil { + p := opts.DataSourcePluginProvider(logger, c) + grpcplugin.Serve(grpcplugin.ServeOpts{ + DiagnosticsServer: newDiagnosticsSDKAdapter(prometheus.DefaultGatherer, p), + ResourceServer: newResourceSDKAdapter(p), + DataServer: newDataSDKAdapter(p), + }) + return + } + + if opts.TransformPluginProvider != nil { + p := opts.TransformPluginProvider(logger, c) + grpcplugin.Serve(grpcplugin.ServeOpts{ + TransformServer: newTransformSDKAdapter(p), + }) + 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 413f37601..43c2011c8 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,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= @@ -91,6 +93,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=