diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index 923429cb38b..37da41d19d3 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -241,7 +241,7 @@ func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err erro } if f.Name() == "plugin.json" { - err := scanner.loadPluginJson(currentPath) + err := scanner.loadPlugin(currentPath) if err != nil { scanner.log.Error("Failed to load plugin", "error", err, "pluginPath", filepath.Dir(currentPath)) scanner.errors = append(scanner.errors, err) @@ -250,7 +250,7 @@ func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err erro return nil } -func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { +func (scanner *PluginScanner) loadPlugin(pluginJsonFilePath string) error { currentDir := filepath.Dir(pluginJsonFilePath) reader, err := os.Open(pluginJsonFilePath) if err != nil { @@ -269,6 +269,16 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { return errors.New("did not find type or id properties in plugin.json") } + // The expressions feature toggle corresponds to transform plug-ins. + if pluginCommon.Type == "transform" { + isEnabled := scanner.cfg.IsExpressionsEnabled() + if !isEnabled { + scanner.log.Debug("Transform plugin is disabled since the expressions feature toggle is not enabled", + "pluginID", pluginCommon.Id) + return nil + } + } + pluginCommon.PluginDir = filepath.Dir(pluginJsonFilePath) // For the time being, we choose to only require back-end plugins to be signed diff --git a/pkg/plugins/plugins_test.go b/pkg/plugins/plugins_test.go index 8d33c628273..96eeaefe6e9 100644 --- a/pkg/plugins/plugins_test.go +++ b/pkg/plugins/plugins_test.go @@ -77,7 +77,7 @@ func TestPluginManager_Init(t *testing.T) { Cfg: &setting.Cfg{ PluginsAllowUnsigned: []string{"test"}, }, - BackendPluginManager: fakeBackendPluginManager{}, + BackendPluginManager: &fakeBackendPluginManager{}, } err := pm.Init() require.NoError(t, err) @@ -100,6 +100,46 @@ func TestPluginManager_Init(t *testing.T) { assert.Equal(t, []error{fmt.Errorf(`plugin "test" has an invalid signature`)}, pm.scanningErrors) }) + + t.Run("Transform plugins should be ignored when expressions feature is off", func(t *testing.T) { + origPluginsPath := setting.PluginsPath + t.Cleanup(func() { + setting.PluginsPath = origPluginsPath + }) + setting.PluginsPath = "testdata/behind-feature-flag" + + fm := fakeBackendPluginManager{} + pm := &PluginManager{ + Cfg: &setting.Cfg{}, + BackendPluginManager: &fm, + } + err := pm.Init() + require.NoError(t, err) + + assert.Empty(t, pm.scanningErrors) + assert.Equal(t, 0, fm.registerCount) + }) + + t.Run("Transform plugins should be loaded when expressions feature is on", func(t *testing.T) { + origPluginsPath := setting.PluginsPath + t.Cleanup(func() { + setting.PluginsPath = origPluginsPath + }) + setting.PluginsPath = "testdata/behind-feature-flag" + + pm := &PluginManager{ + Cfg: &setting.Cfg{ + FeatureToggles: map[string]bool{ + "expressions": true, + }, + }, + BackendPluginManager: &fakeBackendPluginManager{}, + } + err := pm.Init() + require.NoError(t, err) + + assert.Equal(t, []error{fmt.Errorf(`plugin "gel" is unsigned`)}, pm.scanningErrors) + }) } func TestPluginManager_IsBackendOnlyPlugin(t *testing.T) { @@ -123,23 +163,25 @@ func TestPluginManager_IsBackendOnlyPlugin(t *testing.T) { } type fakeBackendPluginManager struct { + registerCount int } -func (f fakeBackendPluginManager) Register(descriptor backendplugin.PluginDescriptor) error { +func (f *fakeBackendPluginManager) Register(descriptor backendplugin.PluginDescriptor) error { + f.registerCount++ return nil } -func (f fakeBackendPluginManager) StartPlugin(ctx context.Context, pluginID string) error { +func (f *fakeBackendPluginManager) StartPlugin(ctx context.Context, pluginID string) error { return nil } -func (f fakeBackendPluginManager) CollectMetrics(ctx context.Context, pluginID string) (*backendplugin.CollectMetricsResult, error) { +func (f *fakeBackendPluginManager) CollectMetrics(ctx context.Context, pluginID string) (*backendplugin.CollectMetricsResult, error) { return nil, nil } -func (f fakeBackendPluginManager) CheckHealth(ctx context.Context, pCtx backend.PluginContext) (*backendplugin.CheckHealthResult, error) { +func (f *fakeBackendPluginManager) CheckHealth(ctx context.Context, pCtx backend.PluginContext) (*backendplugin.CheckHealthResult, error) { return nil, nil } -func (f fakeBackendPluginManager) CallResource(pluginConfig backend.PluginContext, ctx *models.ReqContext, path string) { +func (f *fakeBackendPluginManager) CallResource(pluginConfig backend.PluginContext, ctx *models.ReqContext, path string) { } diff --git a/pkg/plugins/testdata/behind-feature-flag/gel/plugin.json b/pkg/plugins/testdata/behind-feature-flag/gel/plugin.json new file mode 100644 index 00000000000..e6c92ae166d --- /dev/null +++ b/pkg/plugins/testdata/behind-feature-flag/gel/plugin.json @@ -0,0 +1,13 @@ +{ + "type": "transform", + "name": "GEL", + "id": "gel", + "backend": true, + "info": { + "description": "Test", + "author": { + "name": "Grafana Labs", + "url": "https://grafana.com" + } + } +} diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 896800681fb..743f9456463 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -289,6 +289,11 @@ type Cfg struct { FeatureToggles map[string]bool } +// IsExpressionsEnabled returns whether the expressions feature is enabled. +func (c Cfg) IsExpressionsEnabled() bool { + return c.FeatureToggles["expressions"] +} + type CommandLineArgs struct { Config string HomePath string