Plugins: Core plugins register via backend factory provider (#43171)
* refactoring store interface and init flow * fix import * fix linter * refactor resource calling * load with class * re-order args * fix tests * fix linter * remove old creator * add custom config struct * fix some tests * cleanup * fix * tackle plugins * fix linter * refactor and fix test * add connect failure error * add fix for azure, cloud monitoring and test data * restructure * remove unused err * add fake tracer for test * fix grafana ds plugin
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
package coreplugin
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
|
||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
||||
"github.com/grafana/grafana/pkg/tsdb/loki"
|
||||
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
||||
"github.com/grafana/grafana/pkg/tsdb/mysql"
|
||||
"github.com/grafana/grafana/pkg/tsdb/opentsdb"
|
||||
"github.com/grafana/grafana/pkg/tsdb/postgres"
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus"
|
||||
"github.com/grafana/grafana/pkg/tsdb/tempo"
|
||||
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
|
||||
)
|
||||
|
||||
const (
|
||||
CloudWatch = "cloudwatch"
|
||||
CloudMonitoring = "stackdriver"
|
||||
AzureMonitor = "grafana-azure-monitor-datasource"
|
||||
Elasticsearch = "elasticsearch"
|
||||
Graphite = "graphite"
|
||||
InfluxDB = "influxdb"
|
||||
Loki = "loki"
|
||||
OpenTSDB = "opentsdb"
|
||||
Prometheus = "prometheus"
|
||||
Tempo = "tempo"
|
||||
TestData = "testdata"
|
||||
PostgreSQL = "postgres"
|
||||
MySQL = "mysql"
|
||||
MSSQL = "mssql"
|
||||
Grafana = "grafana"
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
store map[string]backendplugin.PluginFactoryFunc
|
||||
}
|
||||
|
||||
func NewRegistry(store map[string]backendplugin.PluginFactoryFunc) *Registry {
|
||||
return &Registry{
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
func ProvideCoreRegistry(am *azuremonitor.Service, cw *cloudwatch.CloudWatchService, cm *cloudmonitoring.Service,
|
||||
es *elasticsearch.Service, grap *graphite.Service, idb *influxdb.Service, lk *loki.Service, otsdb *opentsdb.Service,
|
||||
pr *prometheus.Service, t *tempo.Service, td *testdatasource.Service, pg *postgres.Service, my *mysql.Service,
|
||||
ms *mssql.Service, graf *grafanads.Service) *Registry {
|
||||
return NewRegistry(map[string]backendplugin.PluginFactoryFunc{
|
||||
CloudWatch: asBackendPlugin(cw.Executor),
|
||||
CloudMonitoring: asBackendPlugin(cm),
|
||||
AzureMonitor: asBackendPlugin(am),
|
||||
Elasticsearch: asBackendPlugin(es),
|
||||
Graphite: asBackendPlugin(grap),
|
||||
InfluxDB: asBackendPlugin(idb),
|
||||
Loki: asBackendPlugin(lk),
|
||||
OpenTSDB: asBackendPlugin(otsdb),
|
||||
Prometheus: asBackendPlugin(pr),
|
||||
Tempo: asBackendPlugin(t),
|
||||
TestData: asBackendPlugin(td),
|
||||
PostgreSQL: asBackendPlugin(pg),
|
||||
MySQL: asBackendPlugin(my),
|
||||
MSSQL: asBackendPlugin(ms),
|
||||
Grafana: asBackendPlugin(graf),
|
||||
})
|
||||
}
|
||||
|
||||
func (cr *Registry) Get(pluginID string) backendplugin.PluginFactoryFunc {
|
||||
return cr.store[pluginID]
|
||||
}
|
||||
|
||||
func asBackendPlugin(svc interface{}) backendplugin.PluginFactoryFunc {
|
||||
opts := backend.ServeOpts{}
|
||||
if queryHandler, ok := svc.(backend.QueryDataHandler); ok {
|
||||
opts.QueryDataHandler = queryHandler
|
||||
}
|
||||
if resourceHandler, ok := svc.(backend.CallResourceHandler); ok {
|
||||
opts.CallResourceHandler = resourceHandler
|
||||
}
|
||||
if streamHandler, ok := svc.(backend.StreamHandler); ok {
|
||||
opts.StreamHandler = streamHandler
|
||||
}
|
||||
if healthHandler, ok := svc.(backend.CheckHealthHandler); ok {
|
||||
opts.CheckHealthHandler = healthHandler
|
||||
}
|
||||
|
||||
if opts.QueryDataHandler != nil || opts.CallResourceHandler != nil ||
|
||||
opts.CheckHealthHandler != nil || opts.StreamHandler != nil {
|
||||
return New(opts)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -7,18 +7,23 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/grpcplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
|
||||
func ProvideService() *Service {
|
||||
return &Service{}
|
||||
type Service struct {
|
||||
coreRegistry *coreplugin.Registry
|
||||
}
|
||||
|
||||
func (*Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
for _, provider := range []PluginBackendProvider{RendererProvider, DefaultProvider} {
|
||||
func ProvideService(coreRegistry *coreplugin.Registry) *Service {
|
||||
return &Service{
|
||||
coreRegistry: coreRegistry,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
for _, provider := range []PluginBackendProvider{CorePluginProvider(ctx, s.coreRegistry), RendererProvider, DefaultProvider} {
|
||||
if factory := provider(ctx, p); factory != nil {
|
||||
return factory
|
||||
}
|
||||
@@ -43,6 +48,17 @@ var RendererProvider PluginBackendProvider = func(_ context.Context, p *plugins.
|
||||
}
|
||||
|
||||
var DefaultProvider PluginBackendProvider = func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
// TODO check for executable
|
||||
cmd := plugins.ComposePluginStartCommand(p.Executable)
|
||||
return grpcplugin.NewBackendPlugin(p.ID, filepath.Join(p.PluginDir, cmd))
|
||||
}
|
||||
|
||||
var CorePluginProvider = func(ctx context.Context, registry *coreplugin.Registry) PluginBackendProvider {
|
||||
return func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
if !p.IsCorePlugin() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return registry.Get(p.ID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"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/backendplugin/coreplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
|
||||
@@ -904,7 +906,7 @@ func newLoader(cfg *plugins.Cfg) *Loader {
|
||||
return &Loader{
|
||||
cfg: cfg,
|
||||
pluginFinder: finder.New(),
|
||||
pluginInitializer: initializer.New(cfg, &provider.Service{}, &fakeLicensingService{}),
|
||||
pluginInitializer: initializer.New(cfg, provider.ProvideService(coreplugin.NewRegistry(make(map[string]backendplugin.PluginFactoryFunc))), &fakeLicensingService{}),
|
||||
signatureValidator: signature.NewValidator(&signature.UnsignedPluginAuthorizer{Cfg: cfg}),
|
||||
errs: make(map[string]*plugins.SignatureError),
|
||||
log: &fakeLogger{},
|
||||
|
||||
@@ -471,17 +471,10 @@ func (m *PluginManager) shutdown(ctx context.Context) {
|
||||
|
||||
// corePluginPaths provides a list of the Core plugin paths which need to be scanned on init()
|
||||
func corePluginPaths(cfg *setting.Cfg) []string {
|
||||
datasourcePaths := []string{
|
||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/alertmanager"),
|
||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/dashboard"),
|
||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/jaeger"),
|
||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/mixed"),
|
||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/zipkin"),
|
||||
}
|
||||
|
||||
datasourcePaths := filepath.Join(cfg.StaticRootPath, "app/plugins/datasource")
|
||||
panelsPath := filepath.Join(cfg.StaticRootPath, "app/plugins/panel")
|
||||
|
||||
return append(datasourcePaths, panelsPath)
|
||||
return []string{datasourcePaths, panelsPath}
|
||||
}
|
||||
|
||||
// pluginSettingPaths provides a plugin paths defined in cfg.PluginSettings which need to be scanned on init()
|
||||
|
||||
@@ -2,16 +2,38 @@ package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/signature"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
|
||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
||||
"github.com/grafana/grafana/pkg/tsdb/loki"
|
||||
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
||||
"github.com/grafana/grafana/pkg/tsdb/mysql"
|
||||
"github.com/grafana/grafana/pkg/tsdb/opentsdb"
|
||||
"github.com/grafana/grafana/pkg/tsdb/postgres"
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus"
|
||||
"github.com/grafana/grafana/pkg/tsdb/tempo"
|
||||
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -40,13 +62,34 @@ func TestPluginManager_int_init(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
tracer := &fakeTracer{}
|
||||
|
||||
license := &licensing.OSSLicensingService{
|
||||
Cfg: cfg,
|
||||
}
|
||||
|
||||
hcp := httpclient.NewProvider()
|
||||
am := azuremonitor.ProvideService(cfg, hcp, tracer)
|
||||
cw := cloudwatch.ProvideService(cfg, hcp)
|
||||
cm := cloudmonitoring.ProvideService(hcp, tracer)
|
||||
es := elasticsearch.ProvideService(hcp)
|
||||
grap := graphite.ProvideService(hcp, tracer)
|
||||
idb := influxdb.ProvideService(hcp)
|
||||
lk := loki.ProvideService(hcp, tracer)
|
||||
otsdb := opentsdb.ProvideService(hcp)
|
||||
pr := prometheus.ProvideService(hcp, tracer)
|
||||
tmpo := tempo.ProvideService(hcp)
|
||||
td := testdatasource.ProvideService(cfg)
|
||||
pg := postgres.ProvideService(cfg)
|
||||
my := mysql.ProvideService(cfg, hcp)
|
||||
ms := mssql.ProvideService(cfg)
|
||||
graf := grafanads.ProvideService(cfg)
|
||||
|
||||
coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf)
|
||||
|
||||
pmCfg := plugins.FromGrafanaCfg(cfg)
|
||||
pm, err := ProvideService(cfg, nil, loader.New(pmCfg, license,
|
||||
&signature.UnsignedPluginAuthorizer{Cfg: pmCfg}, &provider.Service{}), nil)
|
||||
&signature.UnsignedPluginAuthorizer{Cfg: pmCfg}, provider.ProvideService(coreRegistry)), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
verifyCorePluginCatalogue(t, pm)
|
||||
@@ -92,12 +135,27 @@ func verifyCorePluginCatalogue(t *testing.T, pm *PluginManager) {
|
||||
}
|
||||
|
||||
expDataSources := map[string]struct{}{
|
||||
"alertmanager": {},
|
||||
"dashboard": {},
|
||||
"input": {},
|
||||
"jaeger": {},
|
||||
"mixed": {},
|
||||
"zipkin": {},
|
||||
"cloudwatch": {},
|
||||
"stackdriver": {},
|
||||
"grafana-azure-monitor-datasource": {},
|
||||
"elasticsearch": {},
|
||||
"graphite": {},
|
||||
"influxdb": {},
|
||||
"loki": {},
|
||||
"opentsdb": {},
|
||||
"prometheus": {},
|
||||
"tempo": {},
|
||||
"testdata": {},
|
||||
"postgres": {},
|
||||
"mysql": {},
|
||||
"mssql": {},
|
||||
"grafana": {},
|
||||
"alertmanager": {},
|
||||
"dashboard": {},
|
||||
"input": {},
|
||||
"jaeger": {},
|
||||
"mixed": {},
|
||||
"zipkin": {},
|
||||
}
|
||||
|
||||
expApps := map[string]struct{}{
|
||||
@@ -177,3 +235,19 @@ func verifyPluginStaticRoutes(t *testing.T, pm *PluginManager) {
|
||||
assert.Contains(t, routes, "test-app")
|
||||
assert.Equal(t, routes["test-app"].Directory, testAppPlugin.PluginDir)
|
||||
}
|
||||
|
||||
type fakeTracer struct {
|
||||
tracing.Tracer
|
||||
}
|
||||
|
||||
func (ft *fakeTracer) Run(context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ft *fakeTracer) Start(ctx context.Context, _ string, _ ...trace.SpanStartOption) (context.Context, tracing.Span) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (ft *fakeTracer) Inject(context.Context, http.Header, tracing.Span) {
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user