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