diff --git a/apps/advisor/pkg/app/checks/plugincheck/check_test.go b/apps/advisor/pkg/app/checks/plugincheck/check_test.go index 60b49870af6..61d79ba304e 100644 --- a/apps/advisor/pkg/app/checks/plugincheck/check_test.go +++ b/apps/advisor/pkg/app/checks/plugincheck/check_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/grafana/grafana-app-sdk/logging" + "github.com/stretchr/testify/assert" + advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/repo" @@ -12,7 +14,6 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" - "github.com/stretchr/testify/assert" ) func TestRun(t *testing.T) { @@ -22,7 +23,7 @@ func TestRun(t *testing.T) { pluginInfo []repo.PluginInfo pluginPreinstalled []string pluginManaged []string - pluginProvisioned []string + pluginProvisioned []provisionedplugins.Plugin pluginErrors []*plugins.Error expectedFailures []advisor.CheckReportFailure }{ @@ -117,7 +118,7 @@ func TestRun(t *testing.T) { pluginInfo: []repo.PluginInfo{ {Status: "deprecated", Slug: "plugin5", Version: "1.1.0"}, // This should be ignored }, - pluginProvisioned: []string{"plugin5"}, + pluginProvisioned: []provisionedplugins.Plugin{{ID: "plugin5"}}, expectedFailures: []advisor.CheckReportFailure{}, }, { @@ -281,10 +282,10 @@ func (m *mockManagedPlugins) ManagedPlugins(ctx context.Context) []string { type mockProvisionedPlugins struct { provisionedplugins.Manager - provisioned []string + provisioned []provisionedplugins.Plugin } -func (m *mockProvisionedPlugins) ProvisionedPlugins(ctx context.Context) ([]string, error) { +func (m *mockProvisionedPlugins) ProvisionedPlugins(_ context.Context) ([]provisionedplugins.Plugin, error) { return m.provisioned, nil } diff --git a/pkg/infra/metrics/metrics.go b/pkg/infra/metrics/metrics.go index a8af3153d4c..b29ac24a116 100644 --- a/pkg/infra/metrics/metrics.go +++ b/pkg/infra/metrics/metrics.go @@ -202,6 +202,10 @@ var ( grafanaPluginTargetInfoDesc *prometheus.GaugeVec + grafanaPluginFileSystemInfoDesc *prometheus.GaugeVec + + grafanaPluginProvisioningInfoDesc *prometheus.GaugeVec + // StatsTotalLibraryPanels is a metric of total number of library panels stored in Grafana. StatsTotalLibraryPanels prometheus.Gauge @@ -578,6 +582,18 @@ func init() { Namespace: ExporterName, }, []string{"plugin_id", "target"}) + grafanaPluginFileSystemInfoDesc = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "plugin_filesystem_info", + Help: "A metric with a constant '1' value labeled by pluginId and filesystem type", + Namespace: ExporterName, + }, []string{"plugin_id", "filesystem_type"}) + + grafanaPluginProvisioningInfoDesc = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "plugin_provisioning_info", + Help: "A metric with a constant '1' value labeled by pluginId and cloud provisioning method", + Namespace: ExporterName, + }, []string{"plugin_id", "provisioning_method"}) + StatsTotalDashboardVersions = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "stat_totals_dashboard_versions", Help: "total amount of dashboard versions in the database", @@ -722,6 +738,14 @@ func SetPluginTargetInformation(pluginID, target string) { grafanaPluginTargetInfoDesc.WithLabelValues(pluginID, target).Set(1) } +func SetPluginFSInformation(pluginID, fsType string) { + grafanaPluginFileSystemInfoDesc.WithLabelValues(pluginID, fsType).Set(1) +} + +func SetPluginProvisioningInformation(pluginID, provisioningMethod string) { + grafanaPluginProvisioningInfoDesc.WithLabelValues(pluginID, provisioningMethod).Set(1) +} + func initMetricVars(reg prometheus.Registerer) { reg.MustRegister( MInstanceStart, @@ -777,6 +801,8 @@ func initMetricVars(reg prometheus.Registerer) { StatsTotalDataSources, grafanaPluginBuildInfoDesc, grafanaPluginTargetInfoDesc, + grafanaPluginFileSystemInfoDesc, + grafanaPluginProvisioningInfoDesc, StatsTotalDashboardVersions, StatsTotalAnnotations, StatsTotalAlertRules, diff --git a/pkg/plugins/ifaces.go b/pkg/plugins/ifaces.go index 36d0eda594c..e50f059e815 100644 --- a/pkg/plugins/ifaces.go +++ b/pkg/plugins/ifaces.go @@ -72,6 +72,7 @@ type UpdateInfo struct { type FS interface { fs.FS + Type() string Base() string Files() ([]string, error) Rel(string) (string, error) diff --git a/pkg/plugins/localfiles.go b/pkg/plugins/localfiles.go index c0ff62b13c3..a6584641a55 100644 --- a/pkg/plugins/localfiles.go +++ b/pkg/plugins/localfiles.go @@ -30,6 +30,10 @@ func NewLocalFS(basePath string) LocalFS { return LocalFS{basePath: basePath} } +func (f LocalFS) Type() string { + return "local" +} + // fileIsAllowed takes an absolute path to a file and an os.FileInfo for that file, and it checks if access to that // file is allowed or not. Access to a file is allowed if the file is in the FS's Base() directory, and if it's a // symbolic link it should not end up outside the plugin's directory. @@ -215,6 +219,10 @@ func NewStaticFS(fs FS) (StaticFS, error) { }, nil } +func (f StaticFS) Type() string { + return f.FS.Type() +} + // Open checks that name is an allowed file and, if so, it returns a fs.File to access it, by calling the // underlying FS' Open() method. // If access is denied, the function returns ErrFileNotExist. diff --git a/pkg/plugins/manager/fakes/fakes.go b/pkg/plugins/manager/fakes/fakes.go index 8a5f40b0205..531acef3548 100644 --- a/pkg/plugins/manager/fakes/fakes.go +++ b/pkg/plugins/manager/fakes/fakes.go @@ -444,6 +444,10 @@ func NewFakePluginFS(base string) *FakePluginFS { } } +func (f *FakePluginFS) Type() string { + return "fake" +} + func (f *FakePluginFS) Open(name string) (fs.File, error) { if f.OpenFunc != nil { return f.OpenFunc(name) diff --git a/pkg/plugins/manager/signature/manifest_test.go b/pkg/plugins/manager/signature/manifest_test.go index e83b527eaf0..18db232fd99 100644 --- a/pkg/plugins/manager/signature/manifest_test.go +++ b/pkg/plugins/manager/signature/manifest_test.go @@ -345,6 +345,10 @@ func newPathSeparatorOverrideFS(sep string, ufs plugins.FS) (fsPathSeparatorFile }, nil } +func (f fsPathSeparatorFiles) Type() string { + return f.FS.Type() +} + // Files returns LocalFS.Files(), but all path separators for the current platform (filepath.Separator) // are replaced with f.separator. func (f fsPathSeparatorFiles) Files() ([]string, error) { diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 6e118dc4b13..e5f3a2c93f2 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -456,3 +456,20 @@ type QueryCachingConfig struct { Enabled bool `json:"enabled"` TTLMS int64 `json:"TTLMs"` } + +// CloudProvisioningMethod is the method used to provision the plugin in Grafana Cloud. +type CloudProvisioningMethod string + +const ( + // CloudProvisioningMethodUnknown is used when the plugin provisioning method is unknown. + CloudProvisioningMethodUnknown CloudProvisioningMethod = "unknown" + + // CloudProvisioningMethodNone is used when the plugin is not provisioned in Grafana Cloud. + CloudProvisioningMethodNone CloudProvisioningMethod = "none" + + // CloudProvisioningMethodURL is used when the plugin is provisioned from a URL. + CloudProvisioningMethodURL CloudProvisioningMethod = "url" + + // CloudProvisioningMethodCatalog is used when the plugin is provisioned from the catalog. + CloudProvisioningMethodCatalog CloudProvisioningMethod = "catalog" +) diff --git a/pkg/plugins/test_utils.go b/pkg/plugins/test_utils.go index bdf731855ec..15545bcedbd 100644 --- a/pkg/plugins/test_utils.go +++ b/pkg/plugins/test_utils.go @@ -47,6 +47,10 @@ func (f inMemoryFS) Open(fn string) (fs.File, error) { return &inMemoryFile{path: fn, reader: bytes.NewReader(f.files[fn])}, nil } +func (f inMemoryFS) Type() string { + return "in-memory" +} + // NewFakeFS returns a new FS that always returns ErrFileNotExist when trying to Open() and empty Files(). func NewFakeFS() FS { return NewInMemoryFS(nil) diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index ff6274fe9d7..dcc97feaefb 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -536,7 +536,8 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api registryRegistry := registry2.ProvideExtSvcRegistry(cfg, extSvcAccountsService, serverLockService, featureToggles) service13 := service6.ProvideService(sqlStore, secretsService) serviceregistrationService := serviceregistration.ProvideService(cfg, featureToggles, registryRegistry, service13) - initialize := pipeline.ProvideInitializationStage(pluginManagementCfg, inMemory, providerService, processService, serviceregistrationService, acimplService, actionSetService, envVarsProvider, tracingService) + noop := provisionedplugins.NewNoop() + initialize := pipeline.ProvideInitializationStage(pluginManagementCfg, inMemory, providerService, processService, serviceregistrationService, acimplService, actionSetService, envVarsProvider, tracingService, noop) terminate, err := pipeline.ProvideTerminationStage(pluginManagementCfg, inMemory, processService) if err != nil { return nil, err @@ -663,10 +664,9 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api if err != nil { return nil, err } - noop := managedplugins.NewNoop() - provisionedpluginsNoop := provisionedplugins.NewNoop() + managedpluginsNoop := managedplugins.NewNoop() preinstallImpl := pluginchecker.ProvidePreinstall(cfg) - plugincheckerService := pluginchecker.ProvideService(noop, provisionedpluginsNoop, preinstallImpl) + plugincheckerService := pluginchecker.ProvideService(managedpluginsNoop, noop, preinstallImpl) pluginsService, err := updatemanager.ProvidePluginsService(cfg, pluginstoreService, pluginInstaller, tracingService, featureToggles, plugincheckerService) if err != nil { return nil, err @@ -715,7 +715,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api } idimplService := idimpl.ProvideService(cfg, localSigner, remoteCache, authnService, registerer, tracer) verifier := userimpl.ProvideVerifier(cfg, userService, tempuserService, notificationService, idimplService) - httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationService, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, noop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokenService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) + httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationService, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, managedpluginsNoop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokenService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) if err != nil { return nil, err } @@ -776,7 +776,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api } zanzanaReconciler := dualwrite2.ProvideZanzanaReconciler(cfg, featureToggles, zanzanaClient, sqlStore, serverLockService, folderimplService) investigationsAppProvider := investigations.RegisterApp(cfg) - checkregistryService := checkregistry.ProvideService(service15, pluginstoreService, plugincontextProvider, middlewareHandler, plugincheckerService, repoManager, preinstallImpl, noop, provisionedpluginsNoop, ssosettingsimplService, cfg, pluginerrsStore) + checkregistryService := checkregistry.ProvideService(service15, pluginstoreService, plugincontextProvider, middlewareHandler, plugincheckerService, repoManager, preinstallImpl, managedpluginsNoop, noop, ssosettingsimplService, cfg, pluginerrsStore) advisorAppProvider := advisor2.RegisterApp(checkregistryService, cfg) alertingNotificationsAppProvider := notifications2.RegisterApp(cfg, alertNG) appregistryService, err := appregistry.ProvideBuilderRunners(apiserverService, eventualRestConfigProvider, featureToggles, investigationsAppProvider, advisorAppProvider, alertingNotificationsAppProvider, cfg) @@ -1127,7 +1127,8 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac registryRegistry := registry2.ProvideExtSvcRegistry(cfg, extSvcAccountsService, serverLockService, featureToggles) service13 := service6.ProvideService(sqlStore, secretsService) serviceregistrationService := serviceregistration.ProvideService(cfg, featureToggles, registryRegistry, service13) - initialize := pipeline.ProvideInitializationStage(pluginManagementCfg, inMemory, providerService, processService, serviceregistrationService, acimplService, actionSetService, envVarsProvider, tracingService) + noop := provisionedplugins.NewNoop() + initialize := pipeline.ProvideInitializationStage(pluginManagementCfg, inMemory, providerService, processService, serviceregistrationService, acimplService, actionSetService, envVarsProvider, tracingService, noop) terminate, err := pipeline.ProvideTerminationStage(pluginManagementCfg, inMemory, processService) if err != nil { return nil, err @@ -1256,10 +1257,9 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac if err != nil { return nil, err } - noop := managedplugins.NewNoop() - provisionedpluginsNoop := provisionedplugins.NewNoop() + managedpluginsNoop := managedplugins.NewNoop() preinstallImpl := pluginchecker.ProvidePreinstall(cfg) - plugincheckerService := pluginchecker.ProvideService(noop, provisionedpluginsNoop, preinstallImpl) + plugincheckerService := pluginchecker.ProvideService(managedpluginsNoop, noop, preinstallImpl) pluginsService, err := updatemanager.ProvidePluginsService(cfg, pluginstoreService, pluginInstaller, tracingService, featureToggles, plugincheckerService) if err != nil { return nil, err @@ -1308,7 +1308,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac } idimplService := idimpl.ProvideService(cfg, localSigner, remoteCache, authnService, registerer, tracer) verifier := userimpl.ProvideVerifier(cfg, userService, tempuserService, notificationServiceMock, idimplService) - httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationServiceMock, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, noop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokentestService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) + httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service14, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service15, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationServiceMock, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service13, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, managedpluginsNoop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokentestService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) if err != nil { return nil, err } @@ -1369,7 +1369,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac } zanzanaReconciler := dualwrite2.ProvideZanzanaReconciler(cfg, featureToggles, zanzanaClient, sqlStore, serverLockService, folderimplService) investigationsAppProvider := investigations.RegisterApp(cfg) - checkregistryService := checkregistry.ProvideService(service15, pluginstoreService, plugincontextProvider, middlewareHandler, plugincheckerService, repoManager, preinstallImpl, noop, provisionedpluginsNoop, ssosettingsimplService, cfg, pluginerrsStore) + checkregistryService := checkregistry.ProvideService(service15, pluginstoreService, plugincontextProvider, middlewareHandler, plugincheckerService, repoManager, preinstallImpl, managedpluginsNoop, noop, ssosettingsimplService, cfg, pluginerrsStore) advisorAppProvider := advisor2.RegisterApp(checkregistryService, cfg) alertingNotificationsAppProvider := notifications2.RegisterApp(cfg, alertNG) appregistryService, err := appregistry.ProvideBuilderRunners(apiserverService, eventualRestConfigProvider, featureToggles, investigationsAppProvider, advisorAppProvider, alertingNotificationsAppProvider, cfg) diff --git a/pkg/services/pluginsintegration/loader/loader_test.go b/pkg/services/pluginsintegration/loader/loader_test.go index e1a2a8f8b59..05d5a1b5847 100644 --- a/pkg/services/pluginsintegration/loader/loader_test.go +++ b/pkg/services/pluginsintegration/loader/loader_test.go @@ -30,6 +30,7 @@ import ( "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs" + "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" "github.com/grafana/grafana/pkg/setting" ) @@ -1558,7 +1559,7 @@ func newLoader(t *testing.T, cfg *config.PluginManagementCfg, reg registry.Servi return ProvideService(cfg, pipeline.ProvideDiscoveryStage(cfg, reg), pipeline.ProvideBootstrapStage(cfg, signature.DefaultCalculator(cfg), assets), pipeline.ProvideValidationStage(cfg, signature.NewValidator(signature.NewUnsignedAuthorizer(cfg)), angularInspector), - pipeline.ProvideInitializationStage(cfg, reg, backendFactory, proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), fakes.NewFakePluginEnvProvider(), tracing.InitializeTracerForTest()), + pipeline.ProvideInitializationStage(cfg, reg, backendFactory, proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), fakes.NewFakePluginEnvProvider(), tracing.InitializeTracerForTest(), provisionedplugins.NewNoop()), terminate, errTracker) } @@ -1589,7 +1590,7 @@ func newLoaderWithOpts(t *testing.T, cfg *config.PluginManagementCfg, opts loade return ProvideService(cfg, pipeline.ProvideDiscoveryStage(cfg, reg), pipeline.ProvideBootstrapStage(cfg, signature.DefaultCalculator(cfg), assets), pipeline.ProvideValidationStage(cfg, signature.NewValidator(signature.NewUnsignedAuthorizer(cfg)), angularInspector), - pipeline.ProvideInitializationStage(cfg, reg, backendFactoryProvider, proc, authServiceRegistry, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), fakes.NewFakePluginEnvProvider(), tracing.InitializeTracerForTest()), + pipeline.ProvideInitializationStage(cfg, reg, backendFactoryProvider, proc, authServiceRegistry, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), fakes.NewFakePluginEnvProvider(), tracing.InitializeTracerForTest(), provisionedplugins.NewNoop()), terminate, errTracker) } diff --git a/pkg/services/pluginsintegration/pipeline/pipeline.go b/pkg/services/pluginsintegration/pipeline/pipeline.go index 1a564788287..ecc0c16c2b3 100644 --- a/pkg/services/pluginsintegration/pipeline/pipeline.go +++ b/pkg/services/pluginsintegration/pipeline/pipeline.go @@ -20,6 +20,7 @@ import ( "github.com/grafana/grafana/pkg/plugins/manager/registry" "github.com/grafana/grafana/pkg/plugins/manager/signature" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol" + "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" ) func ProvideDiscoveryStage(cfg *config.PluginManagementCfg, pr registry.Service) *discovery.Discovery { @@ -67,10 +68,9 @@ func ProvideValidationStage(cfg *config.PluginManagementCfg, sv signature.Valida func ProvideInitializationStage(cfg *config.PluginManagementCfg, pr registry.Service, bp plugins.BackendFactoryProvider, pm process.Manager, externalServiceRegistry auth.ExternalServiceRegistry, - roleRegistry pluginaccesscontrol.RoleRegistry, - actionSetRegistry pluginaccesscontrol.ActionSetRegistry, - pluginEnvProvider envvars.Provider, - tracer tracing.Tracer) *initialization.Initialize { + roleRegistry pluginaccesscontrol.RoleRegistry, actionSetRegistry pluginaccesscontrol.ActionSetRegistry, + pluginEnvProvider envvars.Provider, tracer tracing.Tracer, provisionedPluginsManager provisionedplugins.Manager, +) *initialization.Initialize { return initialization.New(cfg, initialization.Opts{ InitializeFuncs: []initialization.InitializeFunc{ ExternalServiceRegistrationStep(cfg, externalServiceRegistry, tracer), @@ -80,6 +80,8 @@ func ProvideInitializationStage(cfg *config.PluginManagementCfg, pr registry.Ser RegisterActionSetsStep(actionSetRegistry), ReportBuildMetrics, ReportTargetMetrics, + ReportFSMetrics, + ReportCloudProvisioningMetrics(provisionedPluginsManager), initialization.PluginRegistrationStep(pr), }, }) diff --git a/pkg/services/pluginsintegration/pipeline/steps.go b/pkg/services/pluginsintegration/pipeline/steps.go index 8ac3b8a5aa2..a7c9ac6dc0f 100644 --- a/pkg/services/pluginsintegration/pipeline/steps.go +++ b/pkg/services/pluginsintegration/pipeline/steps.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/plugins" @@ -20,6 +21,7 @@ import ( "github.com/grafana/grafana/pkg/plugins/manager/registry" "github.com/grafana/grafana/pkg/plugins/manager/signature" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol" + "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" ) // ExternalServiceRegistration implements an InitializeFunc for registering external services. @@ -139,6 +141,48 @@ func ReportTargetMetrics(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, return p, nil } +// ReportFSMetrics reports plugin filesystem information for all non-core plugins. +func ReportFSMetrics(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { + if p.IsCorePlugin() { + // No metrics for core plugins + return p, nil + } + + metrics.SetPluginFSInformation(p.ID, p.FS.Type()) + return p, nil +} + +// ReportCloudProvisioningMetrics reports plugin cloud provisioning information for all non-core plugins. +func ReportCloudProvisioningMetrics(ppManaged provisionedplugins.Manager) initialization.InitializeFunc { + cloudProvisioningMethod := plugins.CloudProvisioningMethodNone + pps, err := ppManaged.ProvisionedPlugins(context.Background()) + if err != nil { + cloudProvisioningMethod = plugins.CloudProvisioningMethodUnknown + logger.Warn("Failed to get provisioned plugins", "error", err) + } + + return func(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { + if p.IsCorePlugin() { + // No metrics for core plugins + return p, nil + } + + for _, pp := range pps { + if pp.ID != p.ID { + continue + } + if pp.URL == "" { + cloudProvisioningMethod = plugins.CloudProvisioningMethodCatalog + } else { + cloudProvisioningMethod = plugins.CloudProvisioningMethodURL + } + metrics.SetPluginProvisioningInformation(p.ID, string(cloudProvisioningMethod)) + } + + return p, nil + } +} + // SignatureValidation implements a ValidateFunc for validating plugin signatures. type SignatureValidation struct { signatureValidator signature.Validator diff --git a/pkg/services/pluginsintegration/pluginchecker/service.go b/pkg/services/pluginsintegration/pluginchecker/service.go index 61e519e438b..bc1c1599526 100644 --- a/pkg/services/pluginsintegration/pluginchecker/service.go +++ b/pkg/services/pluginsintegration/pluginchecker/service.go @@ -5,8 +5,8 @@ import ( "slices" "github.com/Masterminds/semver" - "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" @@ -51,11 +51,14 @@ func (s *Service) isManaged(ctx context.Context, pluginID string) bool { func (s *Service) isProvisioned(ctx context.Context, pluginID string) bool { if s.provisionedPlugins == nil { - var err error - s.provisionedPlugins, err = s.provisionedPluginsManager.ProvisionedPlugins(ctx) + pps, err := s.provisionedPluginsManager.ProvisionedPlugins(ctx) if err != nil { return false } + s.provisionedPlugins = make([]string, len(pps)) + for _, pp := range pps { + s.provisionedPlugins = append(s.provisionedPlugins, pp.ID) + } } return slices.Contains(s.provisionedPlugins, pluginID) } diff --git a/pkg/services/pluginsintegration/provisionedplugins/provisioned.go b/pkg/services/pluginsintegration/provisionedplugins/provisioned.go index c124f6b0203..6e8cf632c2d 100644 --- a/pkg/services/pluginsintegration/provisionedplugins/provisioned.go +++ b/pkg/services/pluginsintegration/provisionedplugins/provisioned.go @@ -3,17 +3,22 @@ package provisionedplugins import "context" type Manager interface { - ProvisionedPlugins(ctx context.Context) ([]string, error) + ProvisionedPlugins(ctx context.Context) ([]Plugin, error) } var _ Manager = (*Noop)(nil) +type Plugin struct { + ID string + URL string +} + type Noop struct{} func NewNoop() *Noop { return &Noop{} } -func (s *Noop) ProvisionedPlugins(_ context.Context) ([]string, error) { - return []string{}, nil +func (s *Noop) ProvisionedPlugins(_ context.Context) ([]Plugin, error) { + return []Plugin{}, nil } diff --git a/pkg/services/pluginsintegration/test_helper.go b/pkg/services/pluginsintegration/test_helper.go index bde0039e359..8fec1925eb3 100644 --- a/pkg/services/pluginsintegration/test_helper.go +++ b/pkg/services/pluginsintegration/test_helper.go @@ -33,6 +33,7 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" + "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" "github.com/grafana/grafana/pkg/setting" ) @@ -54,7 +55,7 @@ func CreateIntegrationTestCtx(t *testing.T, cfg *setting.Cfg, coreRegistry *core disc := pipeline.ProvideDiscoveryStage(pCfg, reg) boot := pipeline.ProvideBootstrapStage(pCfg, signature.ProvideService(pCfg, statickey.New()), assetpath.ProvideService(pCfg, cdn, pluginassets.ProvideService())) valid := pipeline.ProvideValidationStage(pCfg, signature.NewValidator(signature.NewUnsignedAuthorizer(pCfg)), angularInspector) - init := pipeline.ProvideInitializationStage(pCfg, reg, provider.ProvideService(coreRegistry), proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), nil, tracing.InitializeTracerForTest()) + init := pipeline.ProvideInitializationStage(pCfg, reg, provider.ProvideService(coreRegistry), proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), nil, tracing.InitializeTracerForTest(), provisionedplugins.NewNoop()) term, err := pipeline.ProvideTerminationStage(pCfg, reg, proc) require.NoError(t, err) @@ -100,7 +101,7 @@ func CreateTestLoader(t *testing.T, cfg *pluginsCfg.PluginManagementCfg, opts Lo if opts.Initializer == nil { reg := registry.ProvideService() coreRegistry := coreplugin.NewRegistry(make(map[string]backendplugin.PluginFactoryFunc)) - opts.Initializer = pipeline.ProvideInitializationStage(cfg, reg, provider.ProvideService(coreRegistry), process.ProvideService(), &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), nil, tracing.InitializeTracerForTest()) + opts.Initializer = pipeline.ProvideInitializationStage(cfg, reg, provider.ProvideService(coreRegistry), process.ProvideService(), &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), fakes.NewFakeActionSetRegistry(), nil, tracing.InitializeTracerForTest(), provisionedplugins.NewNoop()) } if opts.Terminator == nil {