Plugins: Do not fail bootstrap stage if single decorate step fails (#73147)

* don't fail all if decorate step fails

* fix casing

* include err too

* cover pluginsintegration too
This commit is contained in:
Will Browne 2023-08-10 14:46:38 +02:00 committed by GitHub
parent 67de18ff06
commit c5e9a82ccb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 75 additions and 69 deletions

View File

@ -76,7 +76,7 @@ func (p *grpcPlugin) Start(_ context.Context) error {
elevated, err := process.IsRunningWithElevatedPrivileges()
if err != nil {
p.logger.Debug("Error checking plugin process execution privilege", "err", err)
p.logger.Debug("Error checking plugin process execution privilege", "error", err)
}
if elevated {
p.logger.Warn("Plugin process is running with elevated privileges. This is not recommended")

View File

@ -30,7 +30,7 @@ func (s *Service) File(ctx context.Context, pluginID, filename string) (*plugins
defer func() {
err = f.Close()
if err != nil {
s.log.Error("Could not close plugin file", "pluginID", p.ID, "file", filename)
s.log.Error("Could not close plugin file", "pluginId", p.ID, "file", filename)
}
}()

View File

@ -128,7 +128,7 @@ func (m *PluginInstaller) Add(ctx context.Context, pluginID, version string, opt
_, err = m.pluginLoader.Load(ctx, sources.NewLocalSource(plugins.ClassExternal, pathsToScan))
if err != nil {
m.log.Error("Could not load plugins", "paths", pathsToScan, "err", err)
m.log.Error("Could not load plugins", "paths", pathsToScan, "error", err)
return err
}

View File

@ -49,7 +49,7 @@ func (l *Local) Find(ctx context.Context, src plugins.PluginSource) ([]*plugins.
for _, path := range pluginURIs {
exists, err := fs.Exists(path)
if err != nil {
l.log.Warn("Skipping finding plugins as an error occurred", "path", path, "err", err)
l.log.Warn("Skipping finding plugins as an error occurred", "path", path, "error", err)
continue
}
if !exists {
@ -69,13 +69,13 @@ func (l *Local) Find(ctx context.Context, src plugins.PluginSource) ([]*plugins.
for _, pluginJSONPath := range pluginJSONPaths {
plugin, err := l.readPluginJSON(pluginJSONPath)
if err != nil {
l.log.Warn("Skipping plugin loading as its plugin.json could not be read", "path", pluginJSONPath, "err", err)
l.log.Warn("Skipping plugin loading as its plugin.json could not be read", "path", pluginJSONPath, "error", err)
continue
}
pluginJSONAbsPath, err := filepath.Abs(pluginJSONPath)
if err != nil {
l.log.Warn("Skipping plugin loading as absolute plugin.json path could not be calculated", "pluginID", plugin.ID, "err", err)
l.log.Warn("Skipping plugin loading as absolute plugin.json path could not be calculated", "pluginId", plugin.ID, "error", err)
continue
}
@ -138,16 +138,16 @@ func (l *Local) readPluginJSON(pluginJSONPath string) (plugins.JSONData, error)
return
}
if err = reader.Close(); err != nil {
l.log.Warn("Failed to close plugin JSON file", "path", pluginJSONPath, "err", err)
l.log.Warn("Failed to close plugin JSON file", "path", pluginJSONPath, "error", err)
}
}()
if err != nil {
l.log.Warn("Skipping plugin loading as its plugin.json could not be read", "path", pluginJSONPath, "err", err)
l.log.Warn("Skipping plugin loading as its plugin.json could not be read", "path", pluginJSONPath, "error", err)
return plugins.JSONData{}, err
}
plugin, err := plugins.ReadPluginJSON(reader)
if err != nil {
l.log.Warn("Skipping plugin loading as its plugin.json could not be read", "path", pluginJSONPath, "err", err)
l.log.Warn("Skipping plugin loading as its plugin.json could not be read", "path", pluginJSONPath, "error", err)
return plugins.JSONData{}, err
}
@ -167,11 +167,11 @@ func (l *Local) getAbsPluginJSONPaths(path string) ([]string, error) {
func(currentPath string, fi os.FileInfo, err error) error {
if err != nil {
if errors.Is(err, os.ErrNotExist) {
l.log.Error("Couldn't scan directory since it doesn't exist", "pluginDir", path, "err", err)
l.log.Error("Couldn't scan directory since it doesn't exist", "pluginDir", path, "error", err)
return nil
}
if errors.Is(err, os.ErrPermission) {
l.log.Error("Couldn't scan directory due to lack of permissions", "pluginDir", path, "err", err)
l.log.Error("Couldn't scan directory due to lack of permissions", "pluginDir", path, "error", err)
return nil
}

View File

@ -44,12 +44,12 @@ func (l *Loader) Load(ctx context.Context, src plugins.PluginSource) ([]*plugins
return nil, err
}
verifiedPlugins, err := l.validation.Validate(ctx, bootstrappedPlugins)
validatedPlugins, err := l.validation.Validate(ctx, bootstrappedPlugins)
if err != nil {
return nil, err
}
initializedPlugins, err := l.initializer.Initialize(ctx, verifiedPlugins)
initializedPlugins, err := l.initializer.Initialize(ctx, validatedPlugins)
if err != nil {
return nil, err
}

View File

@ -69,14 +69,22 @@ func (b *Bootstrap) Bootstrap(ctx context.Context, src plugins.PluginSource, fou
return ps, nil
}
bootstrappedPlugins := make([]*plugins.Plugin, 0, len(ps))
for _, p := range ps {
for _, decorator := range b.decorateSteps {
p, err = decorator(ctx, p)
var ip *plugins.Plugin
stepFailed := false
for _, decorate := range b.decorateSteps {
ip, err = decorate(ctx, p)
if err != nil {
return nil, err
stepFailed = true
b.log.Error("Could not decorate plugin", "pluginId", p.ID, "error", err)
break
}
}
if !stepFailed {
bootstrappedPlugins = append(bootstrappedPlugins, ip)
}
}
return ps, nil
return bootstrappedPlugins, nil
}

View File

@ -49,12 +49,12 @@ func (c *DefaultConstructor) Construct(ctx context.Context, src plugins.PluginSo
for _, bundle := range bundles {
sig, err := c.signatureCalculator.Calculate(ctx, src, bundle.Primary)
if err != nil {
c.log.Warn("Could not calculate plugin signature state", "pluginID", bundle.Primary.JSONData.ID, "err", err)
c.log.Warn("Could not calculate plugin signature state", "pluginId", bundle.Primary.JSONData.ID, "error", err)
continue
}
plugin, err := c.pluginFactoryFunc(bundle.Primary, src.PluginClass(ctx), sig)
if err != nil {
c.log.Error("Could not create primary plugin base", "pluginID", bundle.Primary.JSONData.ID, "err", err)
c.log.Error("Could not create primary plugin base", "pluginId", bundle.Primary.JSONData.ID, "error", err)
continue
}
res = append(res, plugin)
@ -63,7 +63,7 @@ func (c *DefaultConstructor) Construct(ctx context.Context, src plugins.PluginSo
for _, child := range bundle.Children {
cp, err := c.pluginFactoryFunc(*child, plugin.Class, sig)
if err != nil {
c.log.Error("Could not create child plugin base", "pluginID", child.JSONData.ID, "err", err)
c.log.Error("Could not create child plugin base", "pluginId", child.JSONData.ID, "error", err)
continue
}
cp.Parent = plugin

View File

@ -58,17 +58,17 @@ func New(cfg *config.Cfg, opts Opts) *Discovery {
// Discover will execute the Find and Filter steps of the Discovery stage.
func (d *Discovery) Discover(ctx context.Context, src plugins.PluginSource) ([]*plugins.FoundBundle, error) {
found, err := d.findStep(ctx, src)
discoveredPlugins, err := d.findStep(ctx, src)
if err != nil {
return nil, err
}
for _, filterStep := range d.findFilterSteps {
found, err = filterStep(ctx, src.PluginClass(ctx), found)
for _, filter := range d.findFilterSteps {
discoveredPlugins, err = filter(ctx, src.PluginClass(ctx), discoveredPlugins)
if err != nil {
return nil, err
}
}
return found, nil
return discoveredPlugins, nil
}

View File

@ -37,14 +37,14 @@ func (d *DuplicatePluginValidation) Filter(ctx context.Context, bundles []*plugi
for _, b := range bundles {
_, exists := d.registry.Plugin(ctx, b.Primary.JSONData.ID)
if exists {
d.log.Warn("Skipping loading of plugin as it's a duplicate", "pluginID", b.Primary.JSONData.ID)
d.log.Warn("Skipping loading of plugin as it's a duplicate", "pluginId", b.Primary.JSONData.ID)
continue
}
for _, child := range b.Children {
_, exists = d.registry.Plugin(ctx, child.JSONData.ID)
if exists {
d.log.Warn("Skipping loading of child plugin as it's a duplicate", "pluginID", child.JSONData.ID)
d.log.Warn("Skipping loading of child plugin as it's a duplicate", "pluginId", child.JSONData.ID)
continue
}
}

View File

@ -54,7 +54,7 @@ func (i *Initialize) Initialize(ctx context.Context, ps []*plugins.Plugin) ([]*p
ip, err = init(ctx, p)
if err != nil {
stepFailed = true
i.log.Error("Could not initialize plugin", "pluginId", p.ID, "err", err)
i.log.Error("Could not initialize plugin", "pluginId", p.ID, "error", err)
break
}
}

View File

@ -80,7 +80,7 @@ func newBackendProcessStarter(processManager process.Service) *BackendClientStar
// Start will start the backend plugin process.
func (b *BackendClientStarter) Start(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
if err := b.processManager.Start(ctx, p.ID); err != nil {
b.log.Error("Could not start plugin", "pluginId", p.ID, "err", err)
b.log.Error("Could not start plugin", "pluginId", p.ID, "error", err)
return nil, err
}
return p, nil
@ -107,11 +107,11 @@ func newPluginRegistration(pluginRegistry registry.Service) *PluginRegistration
// 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")
r.log.Error("Could not register plugin", "pluginId", p.ID, "error", err)
return nil, err
}
if !p.IsCorePlugin() {
r.log.Info("Plugin registered", "pluginID", p.ID)
r.log.Info("Plugin registered", "pluginId", p.ID)
}
return p, nil

View File

@ -60,12 +60,12 @@ func (v *ModuleJSValidator) Validate(_ context.Context, p *plugins.Plugin) error
f, err := p.FS.Open("module.js")
if err != nil {
if errors.Is(err, plugins.ErrFileNotExist) {
v.log.Warn("Plugin missing module.js", "pluginID", p.ID,
v.log.Warn("Plugin missing module.js", "pluginId", p.ID,
"warning", "Missing module.js, If you loaded this plugin from git, make sure to compile it.")
}
} else if f != nil {
if err = f.Close(); err != nil {
v.log.Warn("Could not close module.js", "pluginID", p.ID, "err", err)
v.log.Warn("Could not close module.js", "pluginId", p.ID, "error", err)
}
}
}
@ -99,12 +99,12 @@ func (a *AngularDetector) Validate(ctx context.Context, p *plugins.Plugin) error
canc()
if err != nil {
a.log.Warn("Could not inspect plugin for angular", "pluginID", p.ID, "err", err)
a.log.Warn("Could not inspect plugin for angular", "pluginId", p.ID, "error", err)
}
// Do not initialize plugins if they're using Angular and Angular support is disabled
if p.AngularDetected && !a.cfg.AngularSupportEnabled {
a.log.Error("Refusing to initialize plugin because it's using Angular, which has been disabled", "pluginID", p.ID)
a.log.Error("Refusing to initialize plugin because it's using Angular, which has been disabled", "pluginId", p.ID)
return errors.New("angular plugins are not supported")
}
}

View File

@ -2,7 +2,6 @@ package validation
import (
"context"
"errors"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
@ -41,27 +40,26 @@ func New(cfg *config.Cfg, opts Opts) *Validate {
}
// Validate will execute the Validate steps of the Validation stage.
func (t *Validate) Validate(ctx context.Context, ps []*plugins.Plugin) ([]*plugins.Plugin, error) {
if len(t.validateSteps) == 0 {
func (v *Validate) Validate(ctx context.Context, ps []*plugins.Plugin) ([]*plugins.Plugin, error) {
if len(v.validateSteps) == 0 {
return ps, nil
}
var err error
verifiedPlugins := make([]*plugins.Plugin, 0, len(ps))
validatedPlugins := make([]*plugins.Plugin, 0, len(ps))
for _, p := range ps {
stepFailed := false
for _, validate := range t.validateSteps {
err = validate(ctx, p)
if err != nil && !errors.Is(err, nil) {
for _, validate := range v.validateSteps {
err := validate(ctx, p)
if err != nil {
stepFailed = true
t.log.Error("Plugin verification failed", "pluginID", p.ID, "err", err)
v.log.Error("Plugin validation failed", "pluginId", p.ID, "error", err)
break
}
}
if !stepFailed {
verifiedPlugins = append(verifiedPlugins, p)
validatedPlugins = append(validatedPlugins, p)
}
}
return verifiedPlugins, nil
return validatedPlugins, nil
}

View File

@ -64,7 +64,7 @@ func (m *Manager) Stop(ctx context.Context, pluginID string) error {
if !exists {
return backendplugin.ErrPluginNotRegistered
}
m.log.Debug("Stopping plugin process", "pluginID", p.ID)
m.log.Debug("Stopping plugin process", "pluginId", p.ID)
m.mu.Lock()
defer m.mu.Unlock()

View File

@ -116,7 +116,7 @@ func (s *Signature) Calculate(ctx context.Context, src plugins.PluginSource, plu
return plugins.Signature{}, fmt.Errorf("files: %w", err)
}
if len(fsFiles) == 0 {
s.log.Warn("No plugin file information in directory", "pluginID", plugin.JSONData.ID)
s.log.Warn("No plugin file information in directory", "pluginId", plugin.JSONData.ID)
return plugins.Signature{
Status: plugins.SignatureStatusInvalid,
}, nil
@ -125,13 +125,13 @@ func (s *Signature) Calculate(ctx context.Context, src plugins.PluginSource, plu
f, err := plugin.FS.Open("MANIFEST.txt")
if err != nil {
if errors.Is(err, plugins.ErrFileNotExist) {
s.log.Debug("Could not find a MANIFEST.txt", "id", plugin.JSONData.ID, "err", err)
s.log.Debug("Could not find a MANIFEST.txt", "id", plugin.JSONData.ID, "error", err)
return plugins.Signature{
Status: plugins.SignatureStatusUnsigned,
}, nil
}
s.log.Debug("Could not open MANIFEST.txt", "id", plugin.JSONData.ID, "err", err)
s.log.Debug("Could not open MANIFEST.txt", "id", plugin.JSONData.ID, "error", err)
return plugins.Signature{
Status: plugins.SignatureStatusInvalid,
}, nil
@ -141,7 +141,7 @@ func (s *Signature) Calculate(ctx context.Context, src plugins.PluginSource, plu
return
}
if err = f.Close(); err != nil {
s.log.Warn("Failed to close plugin MANIFEST file", "err", err)
s.log.Warn("Failed to close plugin MANIFEST file", "error", err)
}
}()
@ -155,7 +155,7 @@ func (s *Signature) Calculate(ctx context.Context, src plugins.PluginSource, plu
manifest, err := s.readPluginManifest(ctx, byteValue)
if err != nil {
s.log.Warn("Plugin signature invalid", "id", plugin.JSONData.ID, "err", err)
s.log.Warn("Plugin signature invalid", "id", plugin.JSONData.ID, "error", err)
return plugins.Signature{
Status: plugins.SignatureStatusInvalid,
}, nil
@ -253,7 +253,7 @@ func verifyHash(mlog log.Logger, plugin plugins.FoundPlugin, path, hash string)
}
defer func() {
if err := f.Close(); err != nil {
mlog.Warn("Failed to close plugin file", "path", path, "err", err)
mlog.Warn("Failed to close plugin file", "path", path, "error", err)
}
}()

View File

@ -56,28 +56,28 @@ func (s *Validation) ValidateSignature(plugin *plugins.Plugin) error {
switch plugin.Signature {
case plugins.SignatureStatusUnsigned:
if authorized := s.authorizer.CanLoadPlugin(plugin); !authorized {
s.log.Debug("Plugin is unsigned", "pluginID", plugin.ID)
s.log.Debug("Plugin is unsigned", "pluginId", plugin.ID)
return &plugins.SignatureError{
PluginID: plugin.ID,
SignatureStatus: plugins.SignatureStatusUnsigned,
}
}
s.log.Warn("Permitting unsigned plugin. This is not recommended", "pluginID", plugin.ID)
s.log.Warn("Permitting unsigned plugin. This is not recommended", "pluginId", plugin.ID)
return nil
case plugins.SignatureStatusInvalid:
s.log.Debug("Plugin has an invalid signature", "pluginID", plugin.ID)
s.log.Debug("Plugin has an invalid signature", "pluginId", plugin.ID)
return &plugins.SignatureError{
PluginID: plugin.ID,
SignatureStatus: plugins.SignatureStatusInvalid,
}
case plugins.SignatureStatusModified:
s.log.Debug("Plugin has a modified signature", "pluginID", plugin.ID)
s.log.Debug("Plugin has a modified signature", "pluginId", plugin.ID)
return &plugins.SignatureError{
PluginID: plugin.ID,
SignatureStatus: plugins.SignatureStatusModified,
}
default:
s.log.Debug("Plugin has an unrecognized plugin signature state", "pluginID", plugin.ID, "signature",
s.log.Debug("Plugin has an unrecognized plugin signature state", "pluginId", plugin.ID, "signature",
plugin.Signature)
return &plugins.SignatureError{
PluginID: plugin.ID,

View File

@ -43,7 +43,7 @@ func (c *Client) Download(_ context.Context, pluginZipURL, checksum string, comp
}
defer func() {
if err := os.Remove(tmpFile.Name()); err != nil {
c.log.Warn("Failed to remove temporary file", "file", tmpFile.Name(), "err", err)
c.log.Warn("Failed to remove temporary file", "file", tmpFile.Name(), "error", err)
}
}()
@ -52,7 +52,7 @@ func (c *Client) Download(_ context.Context, pluginZipURL, checksum string, comp
err = c.downloadFile(tmpFile, pluginZipURL, checksum, compatOpts)
if err != nil {
if err := tmpFile.Close(); err != nil {
c.log.Warn("Failed to close file", "err", err)
c.log.Warn("Failed to close file", "error", err)
}
return nil, fmt.Errorf("failed to download plugin archive: %w", err)
}
@ -81,7 +81,7 @@ func (c *Client) SendReq(url *url.URL, compatOpts CompatOpts) ([]byte, error) {
}
defer func() {
if err = bodyReader.Close(); err != nil {
c.log.Warn("Failed to close stream", "err", err)
c.log.Warn("Failed to close stream", "error", err)
}
}()
return io.ReadAll(bodyReader)
@ -100,7 +100,7 @@ func (c *Client) downloadFile(tmpFile *os.File, pluginURL, checksum string, comp
}
defer func() {
if err := f.Close(); err != nil {
c.log.Warn("Failed to close file", "err", err)
c.log.Warn("Failed to close file", "error", err)
}
}()
_, err = io.Copy(tmpFile, f)
@ -151,7 +151,7 @@ func (c *Client) downloadFile(tmpFile *os.File, pluginURL, checksum string, comp
}
defer func() {
if err := bodyReader.Close(); err != nil {
c.log.Warn("Failed to close body", "err", err)
c.log.Warn("Failed to close body", "error", err)
}
}()
@ -209,7 +209,7 @@ func (c *Client) handleResp(res *http.Response, compatOpts CompatOpts) (io.ReadC
body, err := io.ReadAll(res.Body)
defer func() {
if err := res.Body.Close(); err != nil {
c.log.Warn("Failed to close response body", "err", err)
c.log.Warn("Failed to close response body", "error", err)
}
}()
if err != nil || len(body) == 0 {

View File

@ -79,7 +79,7 @@ func (fs *FS) extractFiles(_ context.Context, pluginArchive *zip.ReadCloser, plu
defer func() {
if err := pluginArchive.Close(); err != nil {
fs.log.Warn("failed to close zip file", "err", err)
fs.log.Warn("failed to close zip file", "error", err)
}
}()
@ -121,7 +121,7 @@ func (fs *FS) extractFiles(_ context.Context, pluginArchive *zip.ReadCloser, plu
if isSymlink(zf) {
if err := extractSymlink(installDir, zf, dstPath); err != nil {
fs.log.Warn("failed to extract symlink", "err", err)
fs.log.Warn("failed to extract symlink", "error", err)
continue
}
continue

View File

@ -41,7 +41,7 @@ func (r *ExternalServiceRegistration) Register(ctx context.Context, p *plugins.P
if p.ExternalServiceRegistration != nil && r.cfg.Features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
s, err := r.externalServiceRegistry.RegisterExternalService(ctx, p.ID, p.ExternalServiceRegistration)
if err != nil {
r.log.Error("Could not register an external service. Initialization skipped", "pluginID", p.ID, "err", err)
r.log.Error("Could not register an external service. Initialization skipped", "pluginId", p.ID, "error", err)
return nil, err
}
p.ExternalService = s
@ -70,7 +70,7 @@ func newRegisterPluginRoles(registry plugins.RoleRegistry) *RegisterPluginRoles
// Register registers the plugin roles with the role registry.
func (r *RegisterPluginRoles) Register(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
if err := r.roleRegistry.DeclarePluginRoles(ctx, p.ID, p.Name, p.Roles); err != nil {
r.log.Warn("Declare plugin roles failed.", "pluginID", p.ID, "err", err)
r.log.Warn("Declare plugin roles failed.", "pluginId", p.ID, "error", err)
}
return p, nil
}
@ -109,7 +109,7 @@ func (v *SignatureValidation) Validate(ctx context.Context, p *plugins.Plugin) e
var sigErr *plugins.SignatureError
if errors.As(err, &sigErr) {
v.log.Warn("Skipping loading plugin due to problem with signature",
"pluginID", p.ID, "status", sigErr.SignatureStatus)
"pluginId", p.ID, "status", sigErr.SignatureStatus)
p.SignatureError = sigErr
v.errs.Record(ctx, sigErr)
}