mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
7bc6d32eb9
commit
60b4a0b2a4
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
})
|
||||
|
||||
|
5
pkg/plugins/manager/pipeline/termination/doc.go
Normal file
5
pkg/plugins/manager/pipeline/termination/doc.go
Normal 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
|
107
pkg/plugins/manager/pipeline/termination/steps.go
Normal file
107
pkg/plugins/manager/pipeline/termination/steps.go
Normal 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
|
||||
}
|
67
pkg/plugins/manager/pipeline/termination/termination.go
Normal file
67
pkg/plugins/manager/pipeline/termination/termination.go
Normal 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
|
||||
}
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user