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