diff --git a/conf/defaults.ini b/conf/defaults.ini index b5d12b62f38..bacb8195eb8 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1478,6 +1478,8 @@ public_key_retrieval_disabled = false # Force download of the public key for verifying plugin signature on startup. If disabled, the public key will be retrieved every 10 days. # Requires public_key_retrieval_disabled to be false to have any effect. public_key_retrieval_on_startup = false +# Enter a comma-separated list of plugin identifiers to avoid loading (including core plugins). These plugins will be hidden in the catalog. +disable_plugins = #################################### Grafana Live ########################################## [live] diff --git a/conf/sample.ini b/conf/sample.ini index f9c7703429c..e70d3a978a0 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1372,6 +1372,8 @@ # Force download of the public key for verifying plugin signature on startup. If disabled, the public key will be retrieved every 10 days. # Requires public_key_retrieval_disabled to be false to have any effect. ; public_key_retrieval_on_startup = false +# Enter a comma-separated list of plugin identifiers to avoid loading (including core plugins). These plugins will be hidden in the catalog. +; disable_plugins = #################################### Grafana Live ########################################## [live] diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 1477e3b510d..79ca5a1bc9d 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -2112,6 +2112,10 @@ Disable download of the public key for verifying plugin signature. The default i Force download of the public key for verifying plugin signature on startup. The default is `false`. If disabled, the public key will be retrieved every 10 days. Requires `public_key_retrieval_disabled` to be false to have any effect. +### disable_plugins + +Enter a comma-separated list of plugin identifiers to avoid loading (including core plugins). These plugins will be hidden in the catalog. +
## [live] diff --git a/pkg/plugins/backendplugin/errors.go b/pkg/plugins/backendplugin/errors.go index 59067ce29c3..b7395586c45 100644 --- a/pkg/plugins/backendplugin/errors.go +++ b/pkg/plugins/backendplugin/errors.go @@ -1,8 +1,6 @@ package backendplugin -import ( - "errors" -) +import "errors" var ( // ErrPluginNotRegistered error returned when plugin is not registered. diff --git a/pkg/plugins/config/config.go b/pkg/plugins/config/config.go index 3c849754084..905349bfb81 100644 --- a/pkg/plugins/config/config.go +++ b/pkg/plugins/config/config.go @@ -17,6 +17,7 @@ type Cfg struct { PluginSettings setting.PluginSettings PluginsAllowUnsigned []string + DisablePlugins []string // AWS Plugin Auth AWSAllowedAuthProviders []string @@ -50,7 +51,7 @@ type Cfg struct { func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string, awsAllowedAuthProviders []string, awsAssumeRoleEnabled bool, awsExternalId string, azure *azsettings.AzureSettings, secureSocksDSProxy setting.SecureSocksDSProxySettings, grafanaVersion string, logDatasourceRequests bool, pluginsCDNURLTemplate string, appURL string, appSubURL string, tracing Tracing, features plugins.FeatureToggles, angularSupportEnabled bool, - grafanaComURL string) *Cfg { + grafanaComURL string, disablePlugins []string) *Cfg { return &Cfg{ log: log.New("plugin.cfg"), PluginsPath: pluginsPath, @@ -58,6 +59,7 @@ func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSetti DevMode: devMode, PluginSettings: pluginSettings, PluginsAllowUnsigned: pluginsAllowUnsigned, + DisablePlugins: disablePlugins, AWSAllowedAuthProviders: awsAllowedAuthProviders, AWSAssumeRoleEnabled: awsAssumeRoleEnabled, AWSExternalId: awsExternalId, diff --git a/pkg/services/pluginsintegration/config/config.go b/pkg/services/pluginsintegration/config/config.go index 590b0e7826c..823e8f89ebf 100644 --- a/pkg/services/pluginsintegration/config/config.go +++ b/pkg/services/pluginsintegration/config/config.go @@ -46,6 +46,7 @@ func ProvideConfig(settingProvider setting.Provider, grafanaCfg *setting.Cfg, fe features, grafanaCfg.AngularSupportEnabled, grafanaCfg.GrafanaComURL, + grafanaCfg.DisablePlugins, ), nil } diff --git a/pkg/services/pluginsintegration/pipeline/pipeline.go b/pkg/services/pluginsintegration/pipeline/pipeline.go index 86fac1193a8..2656bf5936c 100644 --- a/pkg/services/pluginsintegration/pipeline/pipeline.go +++ b/pkg/services/pluginsintegration/pipeline/pipeline.go @@ -30,6 +30,9 @@ func ProvideDiscoveryStage(cfg *config.Cfg, pf finder.Finder, pr registry.Servic func(ctx context.Context, _ plugins.Class, b []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) { return discovery.NewDuplicatePluginFilterStep(pr).Filter(ctx, b) }, + func(_ context.Context, _ plugins.Class, b []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) { + return NewDisablePluginsStep(cfg).Filter(b) + }, }, }) } diff --git a/pkg/services/pluginsintegration/pipeline/steps.go b/pkg/services/pluginsintegration/pipeline/steps.go index 47c9b60ed04..34b4eef139b 100644 --- a/pkg/services/pluginsintegration/pipeline/steps.go +++ b/pkg/services/pluginsintegration/pipeline/steps.go @@ -121,3 +121,39 @@ func (v *SignatureValidation) Validate(ctx context.Context, p *plugins.Plugin) e return nil } + +// DisablePlugins is a filter step that will filter out any configured plugins +type DisablePlugins struct { + log log.Logger + cfg *config.Cfg +} + +// NewDisablePluginsStep returns a new DisablePlugins. +func NewDisablePluginsStep(cfg *config.Cfg) *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 +} diff --git a/pkg/services/pluginsintegration/pipeline/steps_test.go b/pkg/services/pluginsintegration/pipeline/steps_test.go new file mode 100644 index 00000000000..0b4f1b3f0b2 --- /dev/null +++ b/pkg/services/pluginsintegration/pipeline/steps_test.go @@ -0,0 +1,45 @@ +package pipeline + +import ( + "testing" + + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/plugins/config" + "github.com/stretchr/testify/require" +) + +func TestSkipPlugins(t *testing.T) { + cfg := &config.Cfg{ + DisablePlugins: []string{"plugin1", "plugin2"}, + } + s := NewDisablePluginsStep(cfg) + + bundles := []*plugins.FoundBundle{ + { + Primary: plugins.FoundPlugin{ + JSONData: plugins.JSONData{ + ID: "plugin1", + }, + }, + }, + { + Primary: plugins.FoundPlugin{ + JSONData: plugins.JSONData{ + ID: "plugin2", + }, + }, + }, + { + Primary: plugins.FoundPlugin{ + JSONData: plugins.JSONData{ + ID: "plugin3", + }, + }, + }, + } + + filtered, err := s.Filter(bundles) + require.NoError(t, err) + require.Len(t, filtered, 1) + require.Equal(t, filtered[0].Primary.JSONData.ID, "plugin3") +} diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 57cbfff3f07..d23707f852c 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -243,6 +243,7 @@ type Cfg struct { PluginAdminExternalManageEnabled bool PluginForcePublicKeyDownload bool PluginSkipPublicKeyDownload bool + DisablePlugins []string PluginsCDNURLTemplate string PluginLogBackendRequests bool diff --git a/pkg/setting/setting_plugins.go b/pkg/setting/setting_plugins.go index 8eaaf1f8f04..29b8e9102b1 100644 --- a/pkg/setting/setting_plugins.go +++ b/pkg/setting/setting_plugins.go @@ -40,6 +40,14 @@ func (cfg *Cfg) readPluginSettings(iniFile *ini.File) error { cfg.PluginsAllowUnsigned = append(cfg.PluginsAllowUnsigned, plug) } + disablePlugins := pluginsSection.Key("disable_plugins").MustString("") + for _, plug := range strings.Split(disablePlugins, ",") { + plug = strings.TrimSpace(plug) + if plug != "" { + cfg.DisablePlugins = append(cfg.DisablePlugins, plug) + } + } + cfg.PluginCatalogURL = pluginsSection.Key("plugin_catalog_url").MustString("https://grafana.com/grafana/plugins/") cfg.PluginAdminEnabled = pluginsSection.Key("plugin_admin_enabled").MustBool(true) cfg.PluginAdminExternalManageEnabled = pluginsSection.Key("plugin_admin_external_manage_enabled").MustBool(false) @@ -49,6 +57,8 @@ func (cfg *Cfg) readPluginSettings(iniFile *ini.File) error { plug = strings.TrimSpace(plug) cfg.PluginCatalogHiddenPlugins = append(cfg.PluginCatalogHiddenPlugins, plug) } + // Pull disablep plugins from the catalog + cfg.PluginCatalogHiddenPlugins = append(cfg.PluginCatalogHiddenPlugins, cfg.DisablePlugins...) // Plugins CDN settings cfg.PluginsCDNURLTemplate = strings.TrimRight(pluginsSection.Key("cdn_base_url").MustString(""), "/") diff --git a/pkg/setting/setting_plugins_test.go b/pkg/setting/setting_plugins_test.go index 7b666dea6f8..781cdfbb05e 100644 --- a/pkg/setting/setting_plugins_test.go +++ b/pkg/setting/setting_plugins_test.go @@ -41,3 +41,20 @@ func TestPluginSettings(t *testing.T) { require.Equal(t, ps["plugin2"]["key3"], "value3") require.Equal(t, ps["plugin2"]["key4"], "value4") } + +func Test_readPluginSettings(t *testing.T) { + t.Run("should parse disable_plugins", func(t *testing.T) { + cfg := NewCfg() + sec, err := cfg.Raw.NewSection("plugins") + require.NoError(t, err) + _, err = sec.NewKey("disable_plugins", "plugin1,plugin2") + require.NoError(t, err) + _, err = sec.NewKey("plugin_catalog_hidden_plugins", "plugin3") + require.NoError(t, err) + + err = cfg.readPluginSettings(cfg.Raw) + require.NoError(t, err) + require.Equal(t, []string{"plugin1", "plugin2"}, cfg.DisablePlugins) + require.Equal(t, []string{"plugin3", "plugin1", "plugin2"}, cfg.PluginCatalogHiddenPlugins) + }) +}