diff --git a/pkg/api/plugin_resource_test.go b/pkg/api/plugin_resource_test.go index 6cc39d0bd06..6dc1b9315e2 100644 --- a/pkg/api/plugin_resource_test.go +++ b/pkg/api/plugin_resource_test.go @@ -71,11 +71,12 @@ func TestCallResource(t *testing.T) { reg := registry.ProvideService() angularInspector, err := angularinspector.NewStaticInspector() require.NoError(t, err) + proc := process.NewManager(reg) discovery := pipeline.ProvideDiscoveryStage(pCfg, finder.NewLocalFinder(pCfg.DevMode), reg) bootstrap := pipeline.ProvideBootstrapStage(pCfg, signature.ProvideService(pCfg, statickey.New()), assetpath.ProvideService(pluginscdn.ProvideService(pCfg))) - initialize := pipeline.ProvideInitializationStage(pCfg, reg, fakes.NewFakeLicensingService(), provider.ProvideService(coreRegistry)) - terminate, err := pipeline.ProvideTerminationStage(pCfg, reg, process.NewManager(reg)) + initialize := pipeline.ProvideInitializationStage(pCfg, reg, fakes.NewFakeLicensingService(), provider.ProvideService(coreRegistry), proc, &fakes.FakeOauthService{}, fakes.NewFakeRoleRegistry()) + terminate, err := pipeline.ProvideTerminationStage(pCfg, reg, proc) require.NoError(t, err) l := loader.ProvideService(pCfg, signature.NewUnsignedAuthorizer(pCfg), diff --git a/pkg/plugins/manager/loader/loader.go b/pkg/plugins/manager/loader/loader.go index 9daa2438473..5fa6ac8e679 100644 --- a/pkg/plugins/manager/loader/loader.go +++ b/pkg/plugins/manager/loader/loader.go @@ -5,7 +5,6 @@ import ( "errors" "time" - "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/config" "github.com/grafana/grafana/pkg/plugins/log" @@ -19,7 +18,6 @@ import ( "github.com/grafana/grafana/pkg/plugins/manager/registry" "github.com/grafana/grafana/pkg/plugins/manager/signature" "github.com/grafana/grafana/pkg/plugins/oauth" - "github.com/grafana/grafana/pkg/services/featuremgmt" ) var _ plugins.ErrorResolver = (*Loader)(nil) @@ -153,32 +151,6 @@ func (l *Loader) Load(ctx context.Context, src plugins.PluginSource) ([]*plugins } // - // - for _, p := range initializedPlugins { - if err = l.processManager.Start(ctx, p.ID); err != nil { - l.log.Error("Could not start plugin", "pluginId", p.ID, "err", err) - continue - } - - if p.ExternalServiceRegistration != nil && l.cfg.Features.IsEnabled(featuremgmt.FlagExternalServiceAuth) { - s, err := l.externalServiceRegistry.RegisterExternalService(ctx, p.ID, p.ExternalServiceRegistration) - if err != nil { - l.log.Error("Could not register an external service. Initialization skipped", "pluginID", p.ID, "err", err) - continue - } - p.ExternalService = s - } - - if err = l.roleRegistry.DeclarePluginRoles(ctx, p.ID, p.Name, p.Roles); err != nil { - l.log.Warn("Declare plugin roles failed.", "pluginID", p.ID, "err", err) - } - - if !p.IsCorePlugin() && !p.IsBundledPlugin() { - metrics.SetPluginBuildInformation(p.ID, string(p.Type), p.Info.Version, string(p.Signature)) - } - } - // - return initializedPlugins, nil } diff --git a/pkg/plugins/manager/manager_integration_test.go b/pkg/plugins/manager/manager_integration_test.go index 1c965c64edf..4dba9b74cef 100644 --- a/pkg/plugins/manager/manager_integration_test.go +++ b/pkg/plugins/manager/manager_integration_test.go @@ -122,11 +122,12 @@ func TestIntegrationPluginManager(t *testing.T) { lic := plicensing.ProvideLicensing(cfg, &licensing.OSSLicensingService{Cfg: cfg}) angularInspector, err := angularinspector.NewStaticInspector() require.NoError(t, err) + proc := process.NewManager(reg) discovery := pipeline.ProvideDiscoveryStage(pCfg, finder.NewLocalFinder(pCfg.DevMode), reg) bootstrap := pipeline.ProvideBootstrapStage(pCfg, signature.ProvideService(pCfg, statickey.New()), assetpath.ProvideService(pluginscdn.ProvideService(pCfg))) - initialize := pipeline.ProvideInitializationStage(pCfg, reg, lic, provider.ProvideService(coreRegistry)) - terminate, err := pipeline.ProvideTerminationStage(pCfg, reg, process.NewManager(reg)) + initialize := pipeline.ProvideInitializationStage(pCfg, reg, lic, provider.ProvideService(coreRegistry), proc, &fakes.FakeOauthService{}, fakes.NewFakeRoleRegistry()) + terminate, err := pipeline.ProvideTerminationStage(pCfg, reg, proc) require.NoError(t, err) l := loader.ProvideService(pCfg, signature.NewUnsignedAuthorizer(pCfg), diff --git a/pkg/plugins/manager/pipeline/initialization/steps.go b/pkg/plugins/manager/pipeline/initialization/steps.go index 72b9e4bc14b..730c9cf12fd 100644 --- a/pkg/plugins/manager/pipeline/initialization/steps.go +++ b/pkg/plugins/manager/pipeline/initialization/steps.go @@ -7,6 +7,7 @@ import ( "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/envvars" "github.com/grafana/grafana/pkg/plugins/log" + "github.com/grafana/grafana/pkg/plugins/manager/process" "github.com/grafana/grafana/pkg/plugins/manager/registry" ) @@ -15,7 +16,7 @@ import ( // It uses the envvars.Provider to retrieve the environment variables required for the plugin and the plugins.BackendFactoryProvider // to get fetch backend factory, which is used to form a connection to the backend plugin process. // -// Note: This step does not start the backend plugin process. +// Note: This step does not start the backend plugin process. Please see BackendClientStarter for starting the backend plugin process. type BackendClientInit struct { envVarProvider envvars.Provider backendProvider plugins.BackendFactoryProvider @@ -58,6 +59,33 @@ func (b *BackendClientInit) Initialize(ctx context.Context, p *plugins.Plugin) ( return p, nil } +// BackendClientStarter implements an InitializeFunc for starting a backend plugin process. +type BackendClientStarter struct { + processManager process.Service + log log.Logger +} + +// BackendProcessStartStep returns a new InitializeFunc for starting a backend plugin process. +func BackendProcessStartStep(processManager process.Service) InitializeFunc { + return newBackendProcessStarter(processManager).Start +} + +func newBackendProcessStarter(processManager process.Service) *BackendClientStarter { + return &BackendClientStarter{ + processManager: processManager, + log: log.New("plugins.backend.start"), + } +} + +// Start will start the backend plugin process. +func (b *BackendClientStarter) Start(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { + if err := b.processManager.Start(ctx, p.ID); err != nil { + b.log.Error("Could not start plugin", "pluginId", p.ID, "err", err) + return nil, err + } + return p, nil +} + // PluginRegistration implements an InitializeFunc for registering a plugin with the plugin registry. type PluginRegistration struct { pluginRegistry registry.Service diff --git a/pkg/services/pluginsintegration/loader/loader_test.go b/pkg/services/pluginsintegration/loader/loader_test.go index 90b72e12a57..d8a887f3a29 100644 --- a/pkg/services/pluginsintegration/loader/loader_test.go +++ b/pkg/services/pluginsintegration/loader/loader_test.go @@ -1327,7 +1327,7 @@ func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process angularInspector, &fakes.FakeOauthService{}, pipeline.ProvideDiscoveryStage(cfg, finder.NewLocalFinder(false), reg), pipeline.ProvideBootstrapStage(cfg, signature.DefaultCalculator(cfg), assets), - pipeline.ProvideInitializationStage(cfg, reg, lic, backendFactory), + pipeline.ProvideInitializationStage(cfg, reg, lic, backendFactory, proc, &fakes.FakeOauthService{}, fakes.NewFakeRoleRegistry()), terminate) } diff --git a/pkg/services/pluginsintegration/pipeline/pipeline.go b/pkg/services/pluginsintegration/pipeline/pipeline.go index bd37c47998d..05f3660fdb3 100644 --- a/pkg/services/pluginsintegration/pipeline/pipeline.go +++ b/pkg/services/pluginsintegration/pipeline/pipeline.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/plugins/manager/pipeline/termination" "github.com/grafana/grafana/pkg/plugins/manager/process" "github.com/grafana/grafana/pkg/plugins/manager/registry" + "github.com/grafana/grafana/pkg/plugins/oauth" ) func ProvideDiscoveryStage(cfg *config.Cfg, pf finder.Finder, pr registry.Service) *discovery.Discovery { @@ -36,11 +37,17 @@ func ProvideBootstrapStage(cfg *config.Cfg, sc plugins.SignatureCalculator, a *a }) } -func ProvideInitializationStage(cfg *config.Cfg, pr registry.Service, l plugins.Licensing, bp plugins.BackendFactoryProvider) *initialization.Initialize { +func ProvideInitializationStage(cfg *config.Cfg, pr registry.Service, l plugins.Licensing, + bp plugins.BackendFactoryProvider, pm process.Service, externalServiceRegistry oauth.ExternalServiceRegistry, + roleRegistry plugins.RoleRegistry) *initialization.Initialize { return initialization.New(cfg, initialization.Opts{ InitializeFuncs: []initialization.InitializeFunc{ initialization.BackendClientInitStep(envvars.NewProvider(cfg, l), bp), initialization.PluginRegistrationStep(pr), + initialization.BackendProcessStartStep(pm), + ExternalServiceRegistrationStep(cfg, externalServiceRegistry), + RegisterPluginRolesStep(roleRegistry), + ReportBuildMetrics, }, }) } diff --git a/pkg/services/pluginsintegration/pipeline/steps.go b/pkg/services/pluginsintegration/pipeline/steps.go new file mode 100644 index 00000000000..7df828b5be9 --- /dev/null +++ b/pkg/services/pluginsintegration/pipeline/steps.go @@ -0,0 +1,80 @@ +package pipeline + +import ( + "context" + + "github.com/grafana/grafana/pkg/infra/metrics" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/config" + "github.com/grafana/grafana/pkg/plugins/log" + "github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization" + "github.com/grafana/grafana/pkg/plugins/oauth" + "github.com/grafana/grafana/pkg/services/featuremgmt" +) + +// ExternalServiceRegistration implements an InitializeFunc for registering external services. +type ExternalServiceRegistration struct { + cfg *config.Cfg + externalServiceRegistry oauth.ExternalServiceRegistry + log log.Logger +} + +// ExternalServiceRegistrationStep returns an InitializeFunc for registering external services. +func ExternalServiceRegistrationStep(cfg *config.Cfg, externalServiceRegistry oauth.ExternalServiceRegistry) initialization.InitializeFunc { + return newExternalServiceRegistration(cfg, externalServiceRegistry).Register +} + +func newExternalServiceRegistration(cfg *config.Cfg, serviceRegistry oauth.ExternalServiceRegistry) *ExternalServiceRegistration { + return &ExternalServiceRegistration{ + cfg: cfg, + externalServiceRegistry: serviceRegistry, + log: log.New("plugins.external.registration"), + } +} + +// Register registers the external service with the external service registry, if the feature is enabled. +func (r *ExternalServiceRegistration) Register(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { + if p.ExternalServiceRegistration != nil && r.cfg.Features.IsEnabled(featuremgmt.FlagExternalServiceAuth) { + s, err := r.externalServiceRegistry.RegisterExternalService(ctx, p.ID, p.ExternalServiceRegistration) + if err != nil { + r.log.Error("Could not register an external service. Initialization skipped", "pluginID", p.ID, "err", err) + return nil, err + } + p.ExternalService = s + } + return p, nil +} + +// RegisterPluginRoles implements an InitializeFunc for registering plugin roles. +type RegisterPluginRoles struct { + log log.Logger + roleRegistry plugins.RoleRegistry +} + +// RegisterPluginRolesStep returns a new InitializeFunc for registering plugin roles. +func RegisterPluginRolesStep(roleRegistry plugins.RoleRegistry) initialization.InitializeFunc { + return newRegisterPluginRoles(roleRegistry).Register +} + +func newRegisterPluginRoles(registry plugins.RoleRegistry) *RegisterPluginRoles { + return &RegisterPluginRoles{ + log: log.New("plugins.roles.registration"), + roleRegistry: registry, + } +} + +// Register registers the plugin roles with the role registry. +func (r *RegisterPluginRoles) Register(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { + if err := r.roleRegistry.DeclarePluginRoles(ctx, p.ID, p.Name, p.Roles); err != nil { + r.log.Warn("Declare plugin roles failed.", "pluginID", p.ID, "err", err) + } + return p, nil +} + +// ReportBuildMetrics reports build information for all plugins, except core and bundled plugins. +func ReportBuildMetrics(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { + if !p.IsCorePlugin() && !p.IsBundledPlugin() { + metrics.SetPluginBuildInformation(p.ID, string(p.Type), p.Info.Version, string(p.Signature)) + } + return p, nil +}