From 0e9fe9dc409669767fa6e5a7a0e09fac1c95ef68 Mon Sep 17 00:00:00 2001 From: beejeebus Date: Wed, 5 Nov 2025 22:07:44 +0000 Subject: [PATCH] Register external datasource plugins on startup Current code only registers core datasource k8s api groups. Add external plugins. Companion grafana-enterprise PR: https://github.com/grafana/grafana-enterprise/pull/10125 --- pkg/registry/apis/datasource/register.go | 48 ++++++++++++------------ pkg/server/wire_gen.go | 4 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/pkg/registry/apis/datasource/register.go b/pkg/registry/apis/datasource/register.go index 90b1ad8a52f..61709652d31 100644 --- a/pkg/registry/apis/datasource/register.go +++ b/pkg/registry/apis/datasource/register.go @@ -3,10 +3,8 @@ package datasource import ( "context" "encoding/json" - "errors" "fmt" "maps" - "path/filepath" "github.com/prometheus/client_golang/prometheus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -23,7 +21,6 @@ import ( datasourceV0 "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1" queryV0 "github.com/grafana/grafana/pkg/apis/query/v0alpha1" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" - "github.com/grafana/grafana/pkg/configprovider" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/manager/sources" "github.com/grafana/grafana/pkg/promlib/models" @@ -31,7 +28,6 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/apiserver/builder" "github.com/grafana/grafana/pkg/services/featuremgmt" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource/kinds" ) @@ -53,7 +49,6 @@ type DataSourceAPIBuilder struct { } func RegisterAPIService( - cfgProvider configprovider.ConfigProvider, features featuremgmt.FeatureToggles, apiRegistrar builder.APIRegistrar, pluginClient plugins.Client, // access to everything @@ -61,6 +56,7 @@ func RegisterAPIService( contextProvider PluginContextWrapper, accessControl accesscontrol.AccessControl, reg prometheus.Registerer, + pluginSources sources.Registry, ) (*DataSourceAPIBuilder, error) { // We want to expose just a limited set of plugins //nolint:staticcheck // not yet migrated to OpenFeature @@ -75,13 +71,9 @@ func RegisterAPIService( var err error var builder *DataSourceAPIBuilder - cfg, err := cfgProvider.Get(context.Background()) + pluginJSONs, err := getDatasourcePlugins(pluginSources) if err != nil { - return nil, err - } - pluginJSONs, err := getCorePlugins(cfg) - if err != nil { - return nil, err + return nil, fmt.Errorf("error getting list of datasource plugins: %s", err) } ids := []string{ @@ -299,21 +291,29 @@ func (b *DataSourceAPIBuilder) GetOpenAPIDefinitions() openapi.GetOpenAPIDefinit } } -func getCorePlugins(cfg *setting.Cfg) ([]plugins.JSONData, error) { - coreDataSourcesPath := filepath.Join(cfg.StaticRootPath, "app", "plugins", "datasource") - coreDataSourcesSrc := sources.NewLocalSource( - plugins.ClassCore, - []string{coreDataSourcesPath}, - ) +func getDatasourcePlugins(pluginSources sources.Registry) ([]plugins.JSONData, error) { + var pluginJSONs []plugins.JSONData - res, err := coreDataSourcesSrc.Discover(context.Background()) - if err != nil { - return nil, errors.New("failed to load core data source plugins") - } + // It's possible that the same plugin will be found in different sources. + // Registering the same plugin twice in the API is Probably A Bad Thing, + // so this map keeps track of uniques, so we can skip duplicates. + var uniquePlugins = map[string]bool{} - pluginJSONs := make([]plugins.JSONData, 0, len(res)) - for _, p := range res { - pluginJSONs = append(pluginJSONs, p.Primary.JSONData) + for _, pluginSource := range pluginSources.List(context.Background()) { + res, err := pluginSource.Discover(context.Background()) + if err != nil { + return nil, err + } + for _, p := range res { + if p.Primary.JSONData.Type == plugins.TypeDataSource { + if _, found := uniquePlugins[p.Primary.JSONData.ID]; found { + backend.Logger.Info("Found duplicate plugin %s when registering API groups.", p.Primary.JSONData.ID) + continue + } + uniquePlugins[p.Primary.JSONData.ID] = true + pluginJSONs = append(pluginJSONs, p.Primary.JSONData) + } + } } return pluginJSONs, nil } diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index 13b764f7238..b37a6913ce4 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -847,7 +847,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService) dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl) snapshotsAPIBuilder := dashboardsnapshot.RegisterAPIService(serviceImpl, apiserverService, cfg, featureToggles, sqlStore, registerer) - dataSourceAPIBuilder, err := datasource.RegisterAPIService(configProvider, featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer) + dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService) if err != nil { return nil, err } @@ -1485,7 +1485,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService) dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl) snapshotsAPIBuilder := dashboardsnapshot.RegisterAPIService(serviceImpl, apiserverService, cfg, featureToggles, sqlStore, registerer) - dataSourceAPIBuilder, err := datasource.RegisterAPIService(configProvider, featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer) + dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService) if err != nil { return nil, err }