mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			251 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			251 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| package pipeline
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"slices"
 | |
| 
 | |
| 	"github.com/grafana/grafana/pkg/infra/metrics"
 | |
| 	"github.com/grafana/grafana/pkg/plugins"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/auth"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/config"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/log"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/manager/pipeline/validation"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/manager/registry"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/manager/signature"
 | |
| 	"github.com/grafana/grafana/pkg/plugins/pfs"
 | |
| 	"github.com/grafana/grafana/pkg/services/featuremgmt"
 | |
| 	"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs"
 | |
| )
 | |
| 
 | |
| // ExternalServiceRegistration implements an InitializeFunc for registering external services.
 | |
| type ExternalServiceRegistration struct {
 | |
| 	cfg                     *config.PluginManagementCfg
 | |
| 	externalServiceRegistry auth.ExternalServiceRegistry
 | |
| 	log                     log.Logger
 | |
| }
 | |
| 
 | |
| // ExternalServiceRegistrationStep returns an InitializeFunc for registering external services.
 | |
| func ExternalServiceRegistrationStep(cfg *config.PluginManagementCfg, externalServiceRegistry auth.ExternalServiceRegistry) initialization.InitializeFunc {
 | |
| 	return newExternalServiceRegistration(cfg, externalServiceRegistry).Register
 | |
| }
 | |
| 
 | |
| func newExternalServiceRegistration(cfg *config.PluginManagementCfg, serviceRegistry auth.ExternalServiceRegistry) *ExternalServiceRegistration {
 | |
| 	return &ExternalServiceRegistration{
 | |
| 		cfg:                     cfg,
 | |
| 		externalServiceRegistry: serviceRegistry,
 | |
| 		log:                     log.New("plugins.external.registration"),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Register registers the external service with the external service registry, if the feature is enabled.
 | |
| func (r *ExternalServiceRegistration) Register(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
 | |
| 	if p.IAM != nil {
 | |
| 		s, err := r.externalServiceRegistry.RegisterExternalService(ctx, p.ID, pfs.Type(p.Type), p.IAM)
 | |
| 		if err != nil {
 | |
| 			r.log.Error("Could not register an external service. Initialization skipped", "pluginId", p.ID, "error", err)
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		p.ExternalService = s
 | |
| 	}
 | |
| 	return p, nil
 | |
| }
 | |
| 
 | |
| // RegisterPluginRoles implements an InitializeFunc for registering plugin roles.
 | |
| type RegisterPluginRoles struct {
 | |
| 	log          log.Logger
 | |
| 	roleRegistry plugins.RoleRegistry
 | |
| }
 | |
| 
 | |
| // RegisterPluginRolesStep returns a new InitializeFunc for registering plugin roles.
 | |
| func RegisterPluginRolesStep(roleRegistry plugins.RoleRegistry) initialization.InitializeFunc {
 | |
| 	return newRegisterPluginRoles(roleRegistry).Register
 | |
| }
 | |
| 
 | |
| func newRegisterPluginRoles(registry plugins.RoleRegistry) *RegisterPluginRoles {
 | |
| 	return &RegisterPluginRoles{
 | |
| 		log:          log.New("plugins.roles.registration"),
 | |
| 		roleRegistry: registry,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // 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, "error", err)
 | |
| 	}
 | |
| 	return p, nil
 | |
| }
 | |
| 
 | |
| // ReportBuildMetrics reports build information for all plugins, except core and bundled plugins.
 | |
| func ReportBuildMetrics(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
 | |
| 	if !p.IsCorePlugin() && !p.IsBundledPlugin() {
 | |
| 		metrics.SetPluginBuildInformation(p.ID, string(p.Type), p.Info.Version, string(p.Signature))
 | |
| 	}
 | |
| 	return p, nil
 | |
| }
 | |
| 
 | |
| // SignatureValidation implements a ValidateFunc for validating plugin signatures.
 | |
| type SignatureValidation struct {
 | |
| 	signatureValidator signature.Validator
 | |
| 	errs               pluginerrs.SignatureErrorTracker
 | |
| 	log                log.Logger
 | |
| }
 | |
| 
 | |
| // SignatureValidationStep returns a new ValidateFunc for validating plugin signatures.
 | |
| func SignatureValidationStep(signatureValidator signature.Validator,
 | |
| 	sigErr pluginerrs.SignatureErrorTracker) validation.ValidateFunc {
 | |
| 	sv := &SignatureValidation{
 | |
| 		errs:               sigErr,
 | |
| 		signatureValidator: signatureValidator,
 | |
| 		log:                log.New("plugins.signature.validation"),
 | |
| 	}
 | |
| 	return sv.Validate
 | |
| }
 | |
| 
 | |
| // Validate validates the plugin signature. If a signature error is encountered, the error is recorded with the
 | |
| // pluginerrs.SignatureErrorTracker.
 | |
| func (v *SignatureValidation) Validate(ctx context.Context, p *plugins.Plugin) error {
 | |
| 	err := v.signatureValidator.ValidateSignature(p)
 | |
| 	if err != nil {
 | |
| 		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)
 | |
| 			p.SignatureError = sigErr
 | |
| 			v.errs.Record(ctx, sigErr)
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// clear plugin error if a pre-existing error has since been resolved
 | |
| 	v.errs.Clear(ctx, p.ID)
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // DisablePlugins is a filter step that will filter out any configured plugins
 | |
| type DisablePlugins struct {
 | |
| 	log log.Logger
 | |
| 	cfg *config.PluginManagementCfg
 | |
| }
 | |
| 
 | |
| // NewDisablePluginsStep returns a new DisablePlugins.
 | |
| func NewDisablePluginsStep(cfg *config.PluginManagementCfg) *DisablePlugins {
 | |
| 	return &DisablePlugins{
 | |
| 		cfg: cfg,
 | |
| 		log: log.New("plugins.disable"),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Filter will filter out any plugins that are marked to be disabled.
 | |
| func (c *DisablePlugins) Filter(bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) {
 | |
| 	if len(c.cfg.DisablePlugins) == 0 {
 | |
| 		return bundles, nil
 | |
| 	}
 | |
| 
 | |
| 	disablePluginsMap := make(map[string]bool)
 | |
| 	for _, pluginID := range c.cfg.DisablePlugins {
 | |
| 		disablePluginsMap[pluginID] = true
 | |
| 	}
 | |
| 
 | |
| 	res := []*plugins.FoundBundle{}
 | |
| 	for _, bundle := range bundles {
 | |
| 		if disablePluginsMap[bundle.Primary.JSONData.ID] {
 | |
| 			c.log.Debug("Disabling plugin load", "pluginID", bundle.Primary.JSONData.ID)
 | |
| 		} else {
 | |
| 			res = append(res, bundle)
 | |
| 		}
 | |
| 	}
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| // AsExternal is a filter step that will skip loading a core plugin to use an external one.
 | |
| type AsExternal struct {
 | |
| 	log log.Logger
 | |
| 	cfg *config.PluginManagementCfg
 | |
| }
 | |
| 
 | |
| // NewAsExternalStep returns a new DisablePlugins.
 | |
| func NewAsExternalStep(cfg *config.PluginManagementCfg) *AsExternal {
 | |
| 	return &AsExternal{
 | |
| 		cfg: cfg,
 | |
| 		log: log.New("plugins.asExternal"),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Filter will filter out any plugins that are marked to be disabled.
 | |
| func (c *AsExternal) Filter(cl plugins.Class, bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) {
 | |
| 	if c.cfg.Features == nil || !c.cfg.Features.IsEnabledGlobally(featuremgmt.FlagExternalCorePlugins) {
 | |
| 		return bundles, nil
 | |
| 	}
 | |
| 
 | |
| 	if cl == plugins.ClassCore {
 | |
| 		res := []*plugins.FoundBundle{}
 | |
| 		for _, bundle := range bundles {
 | |
| 			pluginCfg := c.cfg.PluginSettings[bundle.Primary.JSONData.ID]
 | |
| 			// Skip core plugins if the feature flag is enabled and the plugin is in the skip list.
 | |
| 			// It could be loaded later as an external plugin.
 | |
| 			if pluginCfg["as_external"] == "true" {
 | |
| 				c.log.Debug("Skipping the core plugin load", "pluginID", bundle.Primary.JSONData.ID)
 | |
| 			} else {
 | |
| 				res = append(res, bundle)
 | |
| 			}
 | |
| 		}
 | |
| 		return res, nil
 | |
| 	}
 | |
| 	return bundles, nil
 | |
| }
 | |
| 
 | |
| // DuplicatePluginIDValidation is a filter step that will filter out any plugins that are already registered with the same
 | |
| // plugin ID. This includes both the primary plugin and child plugins, which are matched using the plugin.json plugin
 | |
| // ID field.
 | |
| type DuplicatePluginIDValidation struct {
 | |
| 	registry registry.Service
 | |
| 	log      log.Logger
 | |
| }
 | |
| 
 | |
| // NewDuplicatePluginIDFilterStep returns a new DuplicatePluginIDValidation.
 | |
| func NewDuplicatePluginIDFilterStep(registry registry.Service) *DuplicatePluginIDValidation {
 | |
| 	return &DuplicatePluginIDValidation{
 | |
| 		registry: registry,
 | |
| 		log:      log.New("plugins.dedupe"),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Filter will filter out any plugins that have already been registered under the same plugin ID.
 | |
| func (d *DuplicatePluginIDValidation) Filter(ctx context.Context, bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) {
 | |
| 	res := make([]*plugins.FoundBundle, 0, len(bundles))
 | |
| 
 | |
| 	var matchesPluginIDFunc = func(fp plugins.FoundPlugin) func(p *plugins.Plugin) bool {
 | |
| 		return func(p *plugins.Plugin) bool {
 | |
| 			return p.ID == fp.JSONData.ID
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, b := range bundles {
 | |
| 		ps := d.registry.Plugins(ctx)
 | |
| 
 | |
| 		if slices.ContainsFunc(ps, matchesPluginIDFunc(b.Primary)) {
 | |
| 			d.log.Warn("Skipping loading of plugin as it's a duplicate", "pluginId", b.Primary.JSONData.ID)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		var nonDupeChildren []*plugins.FoundPlugin
 | |
| 		for _, child := range b.Children {
 | |
| 			if slices.ContainsFunc(ps, matchesPluginIDFunc(*child)) {
 | |
| 				d.log.Warn("Skipping loading of child plugin as it's a duplicate", "pluginId", child.JSONData.ID)
 | |
| 				continue
 | |
| 			}
 | |
| 			nonDupeChildren = append(nonDupeChildren, child)
 | |
| 		}
 | |
| 		res = append(res, &plugins.FoundBundle{
 | |
| 			Primary:  b.Primary,
 | |
| 			Children: nonDupeChildren,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return res, nil
 | |
| }
 |