Plugins: Add termination stage to plugin loader pipeline (#72822)

* add termination stage

* uid -> pluginID (for now)

* also fix fakes

* add simple test

* Fix logger name

Co-authored-by: Giuseppe Guerra <giuseppe.guerra@grafana.com>

* inline stop func call

Co-authored-by: Giuseppe Guerra <giuseppe.guerra@grafana.com>

---------

Co-authored-by: Giuseppe Guerra <giuseppe.guerra@grafana.com>
This commit is contained in:
Will Browne 2023-08-04 11:57:49 +02:00 committed by GitHub
parent 7bc6d32eb9
commit 60b4a0b2a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 305 additions and 62 deletions

View File

@ -25,6 +25,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/manager/loader"
"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/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
"github.com/grafana/grafana/pkg/plugins/manager/signature/statickey"
@ -74,11 +75,13 @@ func TestCallResource(t *testing.T) {
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))
require.NoError(t, err)
l := loader.ProvideService(pCfg, signature.NewUnsignedAuthorizer(pCfg),
reg, fakes.NewFakeRoleRegistry(),
assetpath.ProvideService(pluginscdn.ProvideService(pCfg)),
angularInspector, &fakes.FakeOauthService{}, discovery, bootstrap, initialize)
angularInspector, &fakes.FakeOauthService{}, discovery, bootstrap, initialize, terminate)
srcs := sources.ProvideService(cfg, pCfg)
ps, err := store.ProvideService(reg, srcs, l)
require.NoError(t, err)

View File

@ -496,3 +496,14 @@ func (f *FakeInitializer) Initialize(ctx context.Context, ps []*plugins.Plugin)
}
return []*plugins.Plugin{}, nil
}
type FakeTerminator struct {
TerminateFunc func(ctx context.Context, pluginID string) error
}
func (f *FakeTerminator) Terminate(ctx context.Context, pluginID string) error {
if f.TerminateFunc != nil {
return f.TerminateFunc(ctx, pluginID)
}
return nil
}

View File

@ -14,6 +14,7 @@ import (
"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/pipeline/termination"
"github.com/grafana/grafana/pkg/plugins/manager/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
@ -27,6 +28,7 @@ type Loader struct {
discovery discovery.Discoverer
bootstrap bootstrap.Bootstrapper
initializer initialization.Initializer
termination termination.Terminator
processManager process.Service
pluginRegistry registry.Service
@ -45,15 +47,17 @@ type Loader struct {
func ProvideService(cfg *config.Cfg, authorizer plugins.PluginLoaderAuthorizer,
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 {
discovery discovery.Discoverer, bootstrap bootstrap.Bootstrapper, initializer initialization.Initializer,
termination termination.Terminator) *Loader {
return New(cfg, authorizer, pluginRegistry, process.NewManager(pluginRegistry), roleRegistry, assetPath,
angularInspector, externalServiceRegistry, discovery, bootstrap, initializer)
angularInspector, externalServiceRegistry, discovery, bootstrap, initializer, termination)
}
func New(cfg *config.Cfg, authorizer plugins.PluginLoaderAuthorizer, pluginRegistry registry.Service,
processManager process.Service, roleRegistry plugins.RoleRegistry, assetPath *assetpath.Service,
angularInspector angularinspector.Inspector, externalServiceRegistry oauth.ExternalServiceRegistry,
discovery discovery.Discoverer, bootstrap bootstrap.Bootstrapper, initializer initialization.Initializer) *Loader {
discovery discovery.Discoverer, bootstrap bootstrap.Bootstrapper, initializer initialization.Initializer,
termination termination.Terminator) *Loader {
return &Loader{
pluginRegistry: pluginRegistry,
signatureValidator: signature.NewValidator(authorizer),
@ -68,6 +72,7 @@ func New(cfg *config.Cfg, authorizer plugins.PluginLoaderAuthorizer, pluginRegis
discovery: discovery,
bootstrap: bootstrap,
initializer: initializer,
termination: termination,
}
}
@ -178,40 +183,7 @@ func (l *Loader) Load(ctx context.Context, src plugins.PluginSource) ([]*plugins
}
func (l *Loader) Unload(ctx context.Context, pluginID string) error {
plugin, exists := l.pluginRegistry.Plugin(ctx, pluginID)
if !exists {
return plugins.ErrPluginNotInstalled
}
if plugin.IsCorePlugin() || plugin.IsBundledPlugin() {
return plugins.ErrUninstallCorePlugin
}
if err := l.unload(ctx, plugin); err != nil {
return err
}
return nil
}
func (l *Loader) unload(ctx context.Context, p *plugins.Plugin) error {
l.log.Debug("Stopping plugin process", "pluginId", p.ID)
if err := l.processManager.Stop(ctx, p.ID); err != nil {
return err
}
if err := l.pluginRegistry.Remove(ctx, p.ID); err != nil {
return err
}
l.log.Debug("Plugin unregistered", "pluginId", p.ID)
if remover, ok := p.FS.(plugins.FSRemover); ok {
if err := remover.Remove(); err != nil {
return err
}
}
return nil
return l.termination.Terminate(ctx, pluginID)
}
func (l *Loader) PluginErrors() []*plugins.Error {

View File

@ -2,6 +2,7 @@ package loader
import (
"context"
"errors"
"path/filepath"
"sort"
"testing"
@ -18,6 +19,7 @@ import (
"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/pipeline/termination"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
"github.com/grafana/grafana/pkg/plugins/manager/sources"
"github.com/grafana/grafana/pkg/plugins/pluginscdn"
@ -435,11 +437,15 @@ func TestLoader_Load(t *testing.T) {
angularInspector, err := angularinspector.NewStaticInspector()
require.NoError(t, err)
terminationStage, err := termination.New(tt.cfg, termination.Opts{})
require.NoError(t, err)
l := New(tt.cfg, signature.NewUnsignedAuthorizer(tt.cfg), fakes.NewFakePluginRegistry(),
fakes.NewFakeProcessManager(), fakes.NewFakeRoleRegistry(),
assetpath.ProvideService(pluginscdn.ProvideService(tt.cfg)), angularInspector, &fakes.FakeOauthService{},
discovery.New(tt.cfg, discovery.Opts{}), bootstrap.New(tt.cfg, bootstrap.Opts{}),
initialization.New(tt.cfg, initialization.Opts{}))
initialization.New(tt.cfg, initialization.Opts{}),
terminationStage)
t.Run(tt.name, func(t *testing.T) {
got, err := l.Load(context.Background(), sources.NewLocalSource(tt.class, tt.pluginPaths))
@ -506,7 +512,7 @@ func TestLoader_Load(t *testing.T) {
steps = append(steps, "initialize")
return ps, nil
},
})
}, &fakes.FakeTerminator{})
got, err := l.Load(context.Background(), src)
require.NoError(t, err)
@ -516,6 +522,41 @@ func TestLoader_Load(t *testing.T) {
})
}
func TestLoader_Unload(t *testing.T) {
t.Run("Termination stage error is returned from Unload", func(t *testing.T) {
pluginID := "grafana-test-panel"
cfg := &config.Cfg{}
angularInspector, err := angularinspector.NewStaticInspector()
require.NoError(t, err)
tcs := []struct {
expectedErr error
}{
{
expectedErr: errors.New("plugin not found"),
},
{
expectedErr: nil,
},
}
for _, tc := range tcs {
l := New(cfg, signature.NewUnsignedAuthorizer(cfg), fakes.NewFakePluginRegistry(), fakes.NewFakeProcessManager(),
fakes.NewFakeRoleRegistry(), assetpath.ProvideService(pluginscdn.ProvideService(cfg)), angularInspector,
&fakes.FakeOauthService{}, &fakes.FakeDiscoverer{}, &fakes.FakeBootstrapper{}, &fakes.FakeInitializer{},
&fakes.FakeTerminator{
TerminateFunc: func(ctx context.Context, pID string) error {
require.Equal(t, pluginID, pID)
return tc.expectedErr
},
})
err = l.Unload(context.Background(), pluginID)
require.ErrorIs(t, err, tc.expectedErr)
}
})
}
func mustNewStaticFSForTests(t *testing.T, dir string) plugins.FS {
sfs, err := plugins.NewStaticFS(plugins.NewLocalFS(dir))
require.NoError(t, err)

View File

@ -24,6 +24,7 @@ import (
"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/loader/finder"
"github.com/grafana/grafana/pkg/plugins/manager/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
"github.com/grafana/grafana/pkg/plugins/manager/signature/statickey"
@ -125,10 +126,13 @@ func TestIntegrationPluginManager(t *testing.T) {
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))
require.NoError(t, err)
l := loader.ProvideService(pCfg, signature.NewUnsignedAuthorizer(pCfg),
reg, fakes.NewFakeRoleRegistry(),
assetpath.ProvideService(pluginscdn.ProvideService(pCfg)),
angularInspector, &fakes.FakeOauthService{}, discovery, bootstrap, initialize)
angularInspector, &fakes.FakeOauthService{}, discovery, bootstrap, initialize, terminate)
srcs := sources.ProvideService(cfg, pCfg)
ps, err := store.ProvideService(reg, srcs, l)
require.NoError(t, err)

View File

@ -65,6 +65,10 @@ func (b *Bootstrap) Bootstrap(ctx context.Context, src plugins.PluginSource, fou
return nil, err
}
if len(b.decorateSteps) == 0 {
return ps, nil
}
for _, p := range ps {
for _, decorator := range b.decorateSteps {
p, err = decorator(ctx, p)

View File

@ -1,4 +1,4 @@
// Package bootstrap defines the second stage of the plugin loader pipeline.
// Package bootstrap defines the Bootstrap stage of the plugin loader pipeline.
//
// The Bootstrap stage must implement the Bootstrapper interface.
// - Bootstrap(ctx context.Context, src plugins.PluginSource, bundles []*plugins.FoundBundle) ([]*plugins.Plugin, error)

View File

@ -1,4 +1,4 @@
// Package discovery defines the first stage of the plugin loader pipeline.
// Package discovery defines the Discovery stage of the plugin loader pipeline.
// The Discovery stage must implement the Discoverer interface.
// - Discover(ctx context.Context, src plugins.PluginSource) ([]*plugins.FoundBundle, error)

View File

@ -5,7 +5,7 @@
// Discovery: Find plugins (e.g. from disk, remote, etc.), and [optionally] filter the results based on some criteria.
// Bootstrap: Create the plugins found in the discovery stage and enrich them with metadata.
// Verification: Verify the plugins based on some criteria (e.g. signature validation, angular detection, etc.)
// Initialization: Initialize the plugin for use (e.g. register with Grafana, etc.)
// Post-Initialization: Perform any post-initialization tasks (e.g. start the backend process, declare RBAC roles etc.)
// Initialization: Initialize the plugin for use (e.g. register with Grafana, start the backend process, declare RBAC roles etc.)
// - Termination: Terminate the plugin (e.g. stop the backend process, cleanup, etc.)
package pipeline

View File

@ -1,4 +1,4 @@
// Package initialization defines the fourth stage of the plugin loader pipeline.
// Package initialization defines the Initialization stage of the plugin loader pipeline.
//
// The Initialization stage must implement the Initializer interface.
// - Initialize(ctx context.Context, ps []*plugins.Plugin) ([]*plugins.Plugin, error)

View File

@ -22,8 +22,8 @@ type BackendClientInit struct {
log log.Logger
}
// NewBackendClientInitStep returns a new InitializeFunc for registering a backend plugin process.
func NewBackendClientInitStep(envVarProvider envvars.Provider,
// BackendClientInitStep returns a new InitializeFunc for registering a backend plugin process.
func BackendClientInitStep(envVarProvider envvars.Provider,
backendProvider plugins.BackendFactoryProvider) InitializeFunc {
return newBackendProcessRegistration(envVarProvider, backendProvider).Initialize
}
@ -64,8 +64,8 @@ type PluginRegistration struct {
log log.Logger
}
// NewPluginRegistrationStep returns a new InitializeFunc for registering a plugin with the plugin registry.
func NewPluginRegistrationStep(pluginRegistry registry.Service) InitializeFunc {
// PluginRegistrationStep returns a new InitializeFunc for registering a plugin with the plugin registry.
func PluginRegistrationStep(pluginRegistry registry.Service) InitializeFunc {
return newPluginRegistration(pluginRegistry).Initialize
}

View File

@ -28,7 +28,7 @@ func TestInitializer_Initialize(t *testing.T) {
Class: plugins.ClassCore,
}
stepFunc := NewBackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
var err error
p, err = stepFunc(context.Background(), p)
@ -52,7 +52,7 @@ func TestInitializer_Initialize(t *testing.T) {
Class: plugins.ClassExternal,
}
stepFunc := NewBackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
var err error
p, err = stepFunc(context.Background(), p)
@ -76,7 +76,7 @@ func TestInitializer_Initialize(t *testing.T) {
Class: plugins.ClassExternal,
}
stepFunc := NewBackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
stepFunc := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{plugin: p})
var err error
p, err = stepFunc(context.Background(), p)
@ -94,7 +94,7 @@ func TestInitializer_Initialize(t *testing.T) {
},
}
i := NewBackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{
i := BackendClientInitStep(&fakeEnvVarsProvider{}, &fakeBackendProvider{
plugin: p,
})

View File

@ -0,0 +1,5 @@
// Package termination defines the Termination stage of the plugin loader pipeline.
//
// The Termination stage must implement the Terminator interface.
// - Terminate(ctx context.Context, pluginID string) error
package termination

View File

@ -0,0 +1,107 @@
package termination
import (
"context"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/manager/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
)
// TerminablePluginResolver implements a ResolveFunc for resolving a plugin that can be terminated.
type TerminablePluginResolver struct {
pluginRegistry registry.Service
log log.Logger
}
// TerminablePluginResolverStep returns a new ResolveFunc for resolving a plugin that can be terminated.
func TerminablePluginResolverStep(pluginRegistry registry.Service) ResolveFunc {
return newTerminablePluginResolver(pluginRegistry).Resolve
}
func newTerminablePluginResolver(pluginRegistry registry.Service) *TerminablePluginResolver {
return &TerminablePluginResolver{
pluginRegistry: pluginRegistry,
log: log.New("plugins.resolver"),
}
}
// Resolve returns a plugin that can be terminated.
func (r *TerminablePluginResolver) Resolve(ctx context.Context, pluginID string) (*plugins.Plugin, error) {
p, exists := r.pluginRegistry.Plugin(ctx, pluginID)
if !exists {
return nil, plugins.ErrPluginNotInstalled
}
// core plugins and bundled plugins cannot be terminated
if p.IsCorePlugin() || p.IsBundledPlugin() {
return nil, plugins.ErrUninstallCorePlugin
}
return p, nil
}
// BackendProcessTerminator implements a TerminateFunc for stopping a backend plugin process.
//
// It uses the process.Service to stop the backend plugin process.
type BackendProcessTerminator struct {
processManager process.Service
log log.Logger
}
// BackendProcessTerminatorStep returns a new TerminateFunc for stopping a backend plugin process.
func BackendProcessTerminatorStep(processManager process.Service) TerminateFunc {
return newBackendProcessTerminator(processManager).Terminate
}
func newBackendProcessTerminator(processManager process.Service) *BackendProcessTerminator {
return &BackendProcessTerminator{
processManager: processManager,
log: log.New("plugins.backend.termination"),
}
}
// Terminate stops a backend plugin process.
func (t *BackendProcessTerminator) Terminate(ctx context.Context, p *plugins.Plugin) error {
t.log.Debug("Stopping plugin process", "pluginId", p.ID)
return t.processManager.Stop(ctx, p.ID)
}
// Deregister implements a TerminateFunc for removing a plugin from the plugin registry.
type Deregister struct {
pluginRegistry registry.Service
log log.Logger
}
// DeregisterStep returns a new TerminateFunc for removing a plugin from the plugin registry.
func DeregisterStep(pluginRegistry registry.Service) TerminateFunc {
return newDeregister(pluginRegistry).Deregister
}
func newDeregister(pluginRegistry registry.Service) *Deregister {
return &Deregister{
pluginRegistry: pluginRegistry,
log: log.New("plugins.deregister"),
}
}
// Deregister removes a plugin from the plugin registry.
func (d *Deregister) Deregister(ctx context.Context, p *plugins.Plugin) error {
if err := d.pluginRegistry.Remove(ctx, p.ID); err != nil {
return err
}
d.log.Debug("Plugin unregistered", "pluginId", p.ID)
return nil
}
// FSRemoval implements a TerminateFunc for removing plugin files from the filesystem.
func FSRemoval(_ context.Context, p *plugins.Plugin) error {
if remover, ok := p.FS.(plugins.FSRemover); ok {
if err := remover.Remove(); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,67 @@
package termination
import (
"context"
"errors"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/log"
)
// Terminator is responsible for the Termination stage of the plugin loader pipeline.
type Terminator interface {
Terminate(ctx context.Context, pluginID string) error
}
// ResolveFunc is the function used for the Resolve step of the Termination stage.
type ResolveFunc func(ctx context.Context, pluginID string) (*plugins.Plugin, error)
// TerminateFunc is the function used for the Terminate step of the Termination stage.
type TerminateFunc func(ctx context.Context, p *plugins.Plugin) error
type Terminate struct {
cfg *config.Cfg
resolveStep ResolveFunc
terminateSteps []TerminateFunc
log log.Logger
}
type Opts struct {
ResolveFunc ResolveFunc
TerminateFuncs []TerminateFunc
}
// New returns a new Termination stage.
func New(cfg *config.Cfg, opts Opts) (*Terminate, error) {
// without a resolve function, we can't do anything so return an error
if opts.ResolveFunc == nil && opts.TerminateFuncs != nil {
return nil, errors.New("resolve function is required")
}
if opts.TerminateFuncs == nil {
opts.TerminateFuncs = []TerminateFunc{}
}
return &Terminate{
cfg: cfg,
resolveStep: opts.ResolveFunc,
terminateSteps: opts.TerminateFuncs,
log: log.New("plugins.termination"),
}, nil
}
// Terminate will execute the Terminate steps of the Termination stage.
func (t *Terminate) Terminate(ctx context.Context, pluginID string) error {
p, err := t.resolveStep(ctx, pluginID)
if err != nil {
return err
}
for _, terminate := range t.terminateSteps {
if err = terminate(ctx, p); err != nil {
return err
}
}
return nil
}

View File

@ -11,6 +11,7 @@ import (
"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/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"
@ -27,10 +28,11 @@ func ProvideService(cfg *config.Cfg, authorizer plugins.PluginLoaderAuthorizer,
pluginRegistry registry.Service, roleRegistry plugins.RoleRegistry, assetPath *assetpath.Service,
angularInspector angularinspector.Inspector, externalServiceRegistry oauth.ExternalServiceRegistry,
discovery discovery.Discoverer, bootstrap bootstrap.Bootstrapper, initializer initialization.Initializer,
termination termination.Terminator,
) *Loader {
return &Loader{
loader: pluginsLoader.New(cfg, authorizer, pluginRegistry, processManager, roleRegistry, assetPath,
angularInspector, externalServiceRegistry, discovery, bootstrap, initializer),
angularInspector, externalServiceRegistry, discovery, bootstrap, initializer, termination),
}
}

View File

@ -22,6 +22,7 @@ import (
"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/pipeline/termination"
"github.com/grafana/grafana/pkg/plugins/manager/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
@ -976,7 +977,7 @@ func TestLoader_AngularClass(t *testing.T) {
},
}
// if angularDetected = true, it means that the detection has run
l := newLoaderWithAngularInspector(&config.Cfg{AngularSupportEnabled: true}, angularinspector.AlwaysAngularFakeInspector)
l := newLoaderWithAngularInspector(t, &config.Cfg{AngularSupportEnabled: true}, angularinspector.AlwaysAngularFakeInspector)
p, err := l.Load(context.Background(), fakePluginSource)
require.NoError(t, err)
require.Len(t, p, 1, "should load 1 plugin")
@ -1025,7 +1026,7 @@ func TestLoader_Load_Angular(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
l := newLoaderWithAngularInspector(cfgTc.cfg, tc.angularInspector)
l := newLoaderWithAngularInspector(t, cfgTc.cfg, tc.angularInspector)
p, err := l.Load(context.Background(), fakePluginSource)
require.NoError(t, err)
if tc.shouldLoad {
@ -1318,19 +1319,29 @@ func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process
lic := fakes.NewFakeLicensingService()
angularInspector, err := angularinspector.NewStaticInspector()
require.NoError(t, err)
terminate, err := pipeline.ProvideTerminationStage(cfg, reg, proc)
require.NoError(t, err)
return ProvideService(cfg, signature.NewUnsignedAuthorizer(cfg), proc, reg, fakes.NewFakeRoleRegistry(), assets,
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),
terminate)
}
func newLoaderWithAngularInspector(cfg *config.Cfg, angularInspector angularinspector.Inspector) *Loader {
func newLoaderWithAngularInspector(t *testing.T, cfg *config.Cfg, angularInspector angularinspector.Inspector) *Loader {
reg := fakes.NewFakePluginRegistry()
terminationStage, err := termination.New(cfg, termination.Opts{})
require.NoError(t, err)
return ProvideService(cfg, signature.NewUnsignedAuthorizer(cfg), process.ProvideService(reg), reg,
fakes.NewFakeRoleRegistry(), assetpath.ProvideService(pluginscdn.ProvideService(cfg)),
angularInspector, &fakes.FakeOauthService{},
discovery.New(cfg, discovery.Opts{}), bootstrap.New(cfg, bootstrap.Opts{}), initialization.New(cfg, initialization.Opts{}))
discovery.New(cfg, discovery.Opts{}), bootstrap.New(cfg, bootstrap.Opts{}),
initialization.New(cfg, initialization.Opts{}), terminationStage)
}
func verifyState(t *testing.T, ps []*plugins.Plugin, reg registry.Service,

View File

@ -11,6 +11,8 @@ import (
"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/pipeline/termination"
"github.com/grafana/grafana/pkg/plugins/manager/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
)
@ -37,8 +39,19 @@ 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 {
return initialization.New(cfg, initialization.Opts{
InitializeFuncs: []initialization.InitializeFunc{
initialization.NewBackendClientInitStep(envvars.NewProvider(cfg, l), bp),
initialization.NewPluginRegistrationStep(pr),
initialization.BackendClientInitStep(envvars.NewProvider(cfg, l), bp),
initialization.PluginRegistrationStep(pr),
},
})
}
func ProvideTerminationStage(cfg *config.Cfg, pr registry.Service, pm process.Service) (*termination.Terminate, error) {
return termination.New(cfg, termination.Opts{
ResolveFunc: termination.TerminablePluginResolverStep(pr),
TerminateFuncs: []termination.TerminateFunc{
termination.BackendProcessTerminatorStep(pm),
termination.DeregisterStep(pr),
termination.FSRemoval,
},
})
}

View File

@ -18,6 +18,7 @@ import (
"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/pipeline/termination"
"github.com/grafana/grafana/pkg/plugins/manager/process"
"github.com/grafana/grafana/pkg/plugins/manager/registry"
"github.com/grafana/grafana/pkg/plugins/manager/signature"
@ -69,6 +70,8 @@ var WireSet = wire.NewSet(
wire.Bind(new(bootstrap.Bootstrapper), new(*bootstrap.Bootstrap)),
pipeline.ProvideInitializationStage,
wire.Bind(new(initialization.Initializer), new(*initialization.Initialize)),
pipeline.ProvideTerminationStage,
wire.Bind(new(termination.Terminator), new(*termination.Terminate)),
angularpatternsstore.ProvideService,
angulardetectorsprovider.ProvideDynamic,