mirror of https://github.com/grafana/grafana
Plugins: Add inititialization stage to plugin loader pipeline (#72667)
* first pass * migrate tests * simplify * fix comments * fix linter * nil checks * remove commentpull/72752/head
parent
77e7ae2a1b
commit
ad2705fa0b
@ -1,43 +0,0 @@ |
||||
package initializer |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
|
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/plugins/config" |
||||
"github.com/grafana/grafana/pkg/plugins/envvars" |
||||
) |
||||
|
||||
type Initializer struct { |
||||
envVarProvider envvars.Provider |
||||
backendProvider plugins.BackendFactoryProvider |
||||
} |
||||
|
||||
func New(cfg *config.Cfg, backendProvider plugins.BackendFactoryProvider, license plugins.Licensing) Initializer { |
||||
return Initializer{ |
||||
envVarProvider: envvars.NewProvider(cfg, license), |
||||
backendProvider: backendProvider, |
||||
} |
||||
} |
||||
|
||||
func (i *Initializer) Initialize(ctx context.Context, p *plugins.Plugin) error { |
||||
if p.Backend { |
||||
backendFactory := i.backendProvider.BackendFactory(ctx, p) |
||||
if backendFactory == nil { |
||||
return errors.New("could not find backend factory for plugin") |
||||
} |
||||
|
||||
env, err := i.envVarProvider.Get(ctx, p) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if backendClient, err := backendFactory(p.ID, p.Logger(), env); err != nil { |
||||
return err |
||||
} else { |
||||
p.RegisterClient(backendClient) |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@ |
||||
// Package initialization defines the fourth stage of the plugin loader pipeline.
|
||||
//
|
||||
// The Initialization stage must implement the Initializer interface.
|
||||
// - Initialize(ctx context.Context, ps []*plugins.Plugin) ([]*plugins.Plugin, error)
|
||||
|
||||
package initialization |
||||
@ -0,0 +1,67 @@ |
||||
package initialization |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/plugins/config" |
||||
"github.com/grafana/grafana/pkg/plugins/log" |
||||
) |
||||
|
||||
// Initializer is responsible for the Initialization stage of the plugin loader pipeline.
|
||||
type Initializer interface { |
||||
Initialize(ctx context.Context, ps []*plugins.Plugin) ([]*plugins.Plugin, error) |
||||
} |
||||
|
||||
// InitializeFunc is the function used for the Initialize step of the Initialization stage.
|
||||
type InitializeFunc func(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) |
||||
|
||||
type Initialize struct { |
||||
cfg *config.Cfg |
||||
initializeSteps []InitializeFunc |
||||
log log.Logger |
||||
} |
||||
|
||||
type Opts struct { |
||||
InitializeFuncs []InitializeFunc |
||||
} |
||||
|
||||
// New returns a new Initialization stage.
|
||||
func New(cfg *config.Cfg, opts Opts) *Initialize { |
||||
if opts.InitializeFuncs == nil { |
||||
opts.InitializeFuncs = []InitializeFunc{} |
||||
} |
||||
|
||||
return &Initialize{ |
||||
cfg: cfg, |
||||
initializeSteps: opts.InitializeFuncs, |
||||
log: log.New("plugins.initialization"), |
||||
} |
||||
} |
||||
|
||||
// Initialize will execute the Initialize steps of the Initialization stage.
|
||||
func (i *Initialize) Initialize(ctx context.Context, ps []*plugins.Plugin) ([]*plugins.Plugin, error) { |
||||
if len(i.initializeSteps) == 0 { |
||||
return ps, nil |
||||
} |
||||
|
||||
var err error |
||||
initializedPlugins := make([]*plugins.Plugin, 0, len(ps)) |
||||
for _, p := range ps { |
||||
var ip *plugins.Plugin |
||||
stepFailed := false |
||||
for _, init := range i.initializeSteps { |
||||
ip, err = init(ctx, p) |
||||
if err != nil { |
||||
stepFailed = true |
||||
i.log.Error("Could not initialize plugin", "pluginId", p.ID, "err", err) |
||||
break |
||||
} |
||||
} |
||||
if !stepFailed { |
||||
initializedPlugins = append(initializedPlugins, ip) |
||||
} |
||||
} |
||||
|
||||
return initializedPlugins, nil |
||||
} |
||||
@ -0,0 +1,90 @@ |
||||
package initialization |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
|
||||
"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/registry" |
||||
) |
||||
|
||||
// BackendClientInit implements an InitializeFunc for initializing a backend plugin process.
|
||||
//
|
||||
// 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.
|
||||
type BackendClientInit struct { |
||||
envVarProvider envvars.Provider |
||||
backendProvider plugins.BackendFactoryProvider |
||||
log log.Logger |
||||
} |
||||
|
||||
// NewBackendClientInitStep returns a new InitializeFunc for registering a backend plugin process.
|
||||
func NewBackendClientInitStep(envVarProvider envvars.Provider, |
||||
backendProvider plugins.BackendFactoryProvider) InitializeFunc { |
||||
return newBackendProcessRegistration(envVarProvider, backendProvider).Initialize |
||||
} |
||||
|
||||
func newBackendProcessRegistration(envVarProvider envvars.Provider, |
||||
backendProvider plugins.BackendFactoryProvider) *BackendClientInit { |
||||
return &BackendClientInit{ |
||||
backendProvider: backendProvider, |
||||
envVarProvider: envVarProvider, |
||||
log: log.New("plugins.backend.registration"), |
||||
} |
||||
} |
||||
|
||||
// Initialize will initialize a backend plugin client, if the plugin is a backend plugin.
|
||||
func (b *BackendClientInit) Initialize(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { |
||||
if p.Backend { |
||||
backendFactory := b.backendProvider.BackendFactory(ctx, p) |
||||
if backendFactory == nil { |
||||
return nil, errors.New("could not find backend factory for plugin") |
||||
} |
||||
|
||||
env, err := b.envVarProvider.Get(ctx, p) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if backendClient, err := backendFactory(p.ID, p.Logger(), env); err != nil { |
||||
return nil, err |
||||
} else { |
||||
p.RegisterClient(backendClient) |
||||
} |
||||
} |
||||
return p, nil |
||||
} |
||||
|
||||
// PluginRegistration implements an InitializeFunc for registering a plugin with the plugin registry.
|
||||
type PluginRegistration struct { |
||||
pluginRegistry registry.Service |
||||
log log.Logger |
||||
} |
||||
|
||||
// NewPluginRegistrationStep returns a new InitializeFunc for registering a plugin with the plugin registry.
|
||||
func NewPluginRegistrationStep(pluginRegistry registry.Service) InitializeFunc { |
||||
return newPluginRegistration(pluginRegistry).Initialize |
||||
} |
||||
|
||||
func newPluginRegistration(pluginRegistry registry.Service) *PluginRegistration { |
||||
return &PluginRegistration{ |
||||
pluginRegistry: pluginRegistry, |
||||
log: log.New("plugins.registration"), |
||||
} |
||||
} |
||||
|
||||
// Initialize registers the plugin with the plugin registry.
|
||||
func (r *PluginRegistration) Initialize(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) { |
||||
if err := r.pluginRegistry.Add(ctx, p); err != nil { |
||||
r.log.Error("Could not register plugin", "pluginID", p.ID, "err", err) |
||||
return nil, errors.New("could not register plugin") |
||||
} |
||||
if !p.IsCorePlugin() { |
||||
r.log.Info("Plugin registered", "pluginID", p.ID) |
||||
} |
||||
|
||||
return p, nil |
||||
} |
||||
@ -0,0 +1,47 @@ |
||||
package loader |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/plugins/config" |
||||
pluginsLoader "github.com/grafana/grafana/pkg/plugins/manager/loader" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/angular/angularinspector" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/bootstrap" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/discovery" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/process" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry" |
||||
"github.com/grafana/grafana/pkg/plugins/oauth" |
||||
) |
||||
|
||||
var _ plugins.ErrorResolver = (*Loader)(nil) |
||||
var _ pluginsLoader.Service = (*Loader)(nil) |
||||
|
||||
type Loader struct { |
||||
loader *pluginsLoader.Loader |
||||
} |
||||
|
||||
func ProvideService(cfg *config.Cfg, authorizer plugins.PluginLoaderAuthorizer, processManager process.Service, |
||||
pluginRegistry registry.Service, roleRegistry plugins.RoleRegistry, assetPath *assetpath.Service, |
||||
angularInspector angularinspector.Inspector, externalServiceRegistry oauth.ExternalServiceRegistry, |
||||
discovery discovery.Discoverer, bootstrap bootstrap.Bootstrapper, initializer initialization.Initializer, |
||||
) *Loader { |
||||
return &Loader{ |
||||
loader: pluginsLoader.New(cfg, authorizer, pluginRegistry, processManager, roleRegistry, assetPath, |
||||
angularInspector, externalServiceRegistry, discovery, bootstrap, initializer), |
||||
} |
||||
} |
||||
|
||||
func (l *Loader) Load(ctx context.Context, src plugins.PluginSource) ([]*plugins.Plugin, error) { |
||||
return l.loader.Load(ctx, src) |
||||
} |
||||
|
||||
func (l *Loader) Unload(ctx context.Context, pluginID string) error { |
||||
return l.loader.Unload(ctx, pluginID) |
||||
} |
||||
|
||||
func (l *Loader) PluginErrors() []*plugins.Error { |
||||
return l.loader.PluginErrors() |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -1,33 +0,0 @@ |
||||
package pipeline |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/plugins/config" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/bootstrap" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/discovery" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry" |
||||
) |
||||
|
||||
func ProvideDiscoveryStage(cfg *config.Cfg, pluginFinder finder.Finder, pluginRegistry registry.Service) *discovery.Discovery { |
||||
return discovery.New(cfg, discovery.Opts{ |
||||
FindFunc: func(ctx context.Context, src plugins.PluginSource) ([]*plugins.FoundBundle, error) { |
||||
return pluginFinder.Find(ctx, src) |
||||
}, |
||||
FindFilterFuncs: []discovery.FindFilterFunc{ |
||||
func(ctx context.Context, _ plugins.Class, bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) { |
||||
return discovery.NewDuplicatePluginFilterStep(pluginRegistry).Filter(ctx, bundles) |
||||
}, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
func ProvideBootstrapStage(cfg *config.Cfg, signatureCalculator plugins.SignatureCalculator, assetPath *assetpath.Service) *bootstrap.Bootstrap { |
||||
return bootstrap.New(cfg, bootstrap.Opts{ |
||||
ConstructFunc: bootstrap.DefaultConstructFunc(signatureCalculator, assetPath), |
||||
DecorateFuncs: bootstrap.DefaultDecorateFuncs, |
||||
}) |
||||
} |
||||
@ -0,0 +1,44 @@ |
||||
package pipeline |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
"github.com/grafana/grafana/pkg/plugins/config" |
||||
"github.com/grafana/grafana/pkg/plugins/envvars" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/bootstrap" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/discovery" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization" |
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry" |
||||
) |
||||
|
||||
func ProvideDiscoveryStage(cfg *config.Cfg, pf finder.Finder, pr registry.Service) *discovery.Discovery { |
||||
return discovery.New(cfg, discovery.Opts{ |
||||
FindFunc: func(ctx context.Context, src plugins.PluginSource) ([]*plugins.FoundBundle, error) { |
||||
return pf.Find(ctx, src) |
||||
}, |
||||
FindFilterFuncs: []discovery.FindFilterFunc{ |
||||
func(ctx context.Context, _ plugins.Class, b []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) { |
||||
return discovery.NewDuplicatePluginFilterStep(pr).Filter(ctx, b) |
||||
}, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
func ProvideBootstrapStage(cfg *config.Cfg, sc plugins.SignatureCalculator, a *assetpath.Service) *bootstrap.Bootstrap { |
||||
return bootstrap.New(cfg, bootstrap.Opts{ |
||||
ConstructFunc: bootstrap.DefaultConstructFunc(sc, a), |
||||
DecorateFuncs: bootstrap.DefaultDecorateFuncs, |
||||
}) |
||||
} |
||||
|
||||
func ProvideInitializationStage(cfg *config.Cfg, pr registry.Service, l plugins.Licensing, bp plugins.BackendFactoryProvider) *initialization.Initialize { |
||||
return initialization.New(cfg, initialization.Opts{ |
||||
InitializeFuncs: []initialization.InitializeFunc{ |
||||
initialization.NewBackendClientInitStep(envvars.NewProvider(cfg, l), bp), |
||||
initialization.NewPluginRegistrationStep(pr), |
||||
}, |
||||
}) |
||||
} |
||||
Loading…
Reference in new issue