mirror of https://github.com/grafana/grafana
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>pull/72894/head
parent
7bc6d32eb9
commit
60b4a0b2a4
@ -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 |
||||
@ -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 |
||||
} |
||||
@ -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 |
||||
} |
||||
Loading…
Reference in new issue