mirror of
				https://github.com/grafana/grafana.git
				synced 2025-02-25 18:55:37 -06:00 
			
		
		
		
	Plugins: Add forward_host_env_vars setting (#79333)
* Plugins: Add forward_host_env_vars_plugins setting * Renamed forward_host_env_vars_plugins to forward_host_env_vars * Add readPluginIDsList * refactoring * lint * Use util.SplitString
This commit is contained in:
		@@ -1535,6 +1535,8 @@ disable_plugins =
 | 
			
		||||
install_token =
 | 
			
		||||
# Comma separated list of plugin ids for which angular deprecation UI should be disabled
 | 
			
		||||
hide_angular_deprecation =
 | 
			
		||||
# Comma separated list of plugin ids for which environment variables should be forwarded. Used only when feature flag pluginsSkipHostEnvVars is enabled.
 | 
			
		||||
forward_host_env_vars =
 | 
			
		||||
 | 
			
		||||
#################################### Grafana Live ##########################################
 | 
			
		||||
[live]
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ type Cfg struct {
 | 
			
		||||
	PluginSettings       setting.PluginSettings
 | 
			
		||||
	PluginsAllowUnsigned []string
 | 
			
		||||
	DisablePlugins       []string
 | 
			
		||||
	ForwardHostEnvVars   []string
 | 
			
		||||
 | 
			
		||||
	// AWS Plugin Auth
 | 
			
		||||
	AWSAllowedAuthProviders []string
 | 
			
		||||
@@ -52,7 +53,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, disablePlugins []string, hideAngularDeprecation []string) *Cfg {
 | 
			
		||||
	grafanaComURL string, disablePlugins []string, hideAngularDeprecation []string, forwardHostEnvVars []string) *Cfg {
 | 
			
		||||
	return &Cfg{
 | 
			
		||||
		log:                     log.New("plugin.cfg"),
 | 
			
		||||
		PluginsPath:             pluginsPath,
 | 
			
		||||
@@ -75,5 +76,6 @@ func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSetti
 | 
			
		||||
		Features:                features,
 | 
			
		||||
		AngularSupportEnabled:   angularSupportEnabled,
 | 
			
		||||
		HideAngularDeprecation:  hideAngularDeprecation,
 | 
			
		||||
		ForwardHostEnvVars:      forwardHostEnvVars,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package bootstrap
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"path"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/grafana/grafana/pkg/infra/slugify"
 | 
			
		||||
@@ -157,12 +158,12 @@ func configureAppChildPlugin(cfg *config.Cfg, parent *plugins.Plugin, child *plu
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SkipHostEnvVarsDecorateFunc returns a DecorateFunc that configures the SkipHostEnvVars field of the plugin.
 | 
			
		||||
// It will be set to true if the FlagPluginsSkipHostEnvVars feature flag is set, and the plugin does not have
 | 
			
		||||
// forward_host_env_vars = true in its plugin settings.
 | 
			
		||||
// It will be set to true if the FlagPluginsSkipHostEnvVars feature flag is set, and the plugin is not present in the
 | 
			
		||||
// ForwardHostEnvVars plugin ids list.
 | 
			
		||||
func SkipHostEnvVarsDecorateFunc(cfg *config.Cfg) DecorateFunc {
 | 
			
		||||
	return func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
 | 
			
		||||
		p.SkipHostEnvVars = cfg.Features.IsEnabledGlobally(featuremgmt.FlagPluginsSkipHostEnvVars) &&
 | 
			
		||||
			cfg.PluginSettings[p.ID]["forward_host_env_vars"] != "true"
 | 
			
		||||
			!slices.Contains(cfg.ForwardHostEnvVars, p.ID)
 | 
			
		||||
		return p, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ import (
 | 
			
		||||
	"github.com/grafana/grafana/pkg/plugins/log"
 | 
			
		||||
	"github.com/grafana/grafana/pkg/plugins/manager/fakes"
 | 
			
		||||
	"github.com/grafana/grafana/pkg/services/featuremgmt"
 | 
			
		||||
	"github.com/grafana/grafana/pkg/setting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSetDefaultNavURL(t *testing.T) {
 | 
			
		||||
@@ -172,34 +171,34 @@ func TestSkipEnvVarsDecorateFunc(t *testing.T) {
 | 
			
		||||
		t.Run("plugin setting", func(t *testing.T) {
 | 
			
		||||
			for _, tc := range []struct {
 | 
			
		||||
				name               string
 | 
			
		||||
				pluginSettings     setting.PluginSettings
 | 
			
		||||
				forwardHostEnvVars []string
 | 
			
		||||
				expSkipHostEnvVars bool
 | 
			
		||||
			}{
 | 
			
		||||
				{
 | 
			
		||||
					name:               "forward_host_env_vars = false should set SkipHostEnvVars to true",
 | 
			
		||||
					pluginSettings:     setting.PluginSettings{pluginID: map[string]string{"forward_host_env_vars": "false"}},
 | 
			
		||||
					name:               "plugin id not present in forwardHostEnvVars should set SkipHostEnvVars to true (empty)",
 | 
			
		||||
					forwardHostEnvVars: []string{},
 | 
			
		||||
					expSkipHostEnvVars: true,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name:               "forward_host_env_vars = true should set SkipHostEnvVars to false",
 | 
			
		||||
					pluginSettings:     setting.PluginSettings{pluginID: map[string]string{"forward_host_env_vars": "true"}},
 | 
			
		||||
					name:               "plugin id not present in forwardHostEnvVars should set SkipHostEnvVars to true (other id)",
 | 
			
		||||
					forwardHostEnvVars: []string{"other-id", "yet-another-id"},
 | 
			
		||||
					expSkipHostEnvVars: true,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name:               "plugin id in forwardHostEnvVars should set SkipHostEnvVars to false (only)",
 | 
			
		||||
					forwardHostEnvVars: []string{pluginID},
 | 
			
		||||
					expSkipHostEnvVars: false,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name:               "invalid forward_host_env_vars should set SkipHostEnvVars to true",
 | 
			
		||||
					pluginSettings:     setting.PluginSettings{pluginID: map[string]string{"forward_host_env_vars": "grilled cheese sandwich with bacon"}},
 | 
			
		||||
					expSkipHostEnvVars: true,
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					name:               "forward_host_env_vars absent should set SkipHostEnvVars to true",
 | 
			
		||||
					pluginSettings:     setting.PluginSettings{pluginID: nil},
 | 
			
		||||
					expSkipHostEnvVars: true,
 | 
			
		||||
					name:               "plugin id in forwardHostEnvVars should set SkipHostEnvVars to false (with other)",
 | 
			
		||||
					forwardHostEnvVars: []string{"a-plugin", pluginID, "other-id"},
 | 
			
		||||
					expSkipHostEnvVars: false,
 | 
			
		||||
				},
 | 
			
		||||
			} {
 | 
			
		||||
				t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
					f := SkipHostEnvVarsDecorateFunc(&config.Cfg{
 | 
			
		||||
						Features:       featuremgmt.WithFeatures(featuremgmt.FlagPluginsSkipHostEnvVars),
 | 
			
		||||
						PluginSettings: tc.pluginSettings,
 | 
			
		||||
						Features:           featuremgmt.WithFeatures(featuremgmt.FlagPluginsSkipHostEnvVars),
 | 
			
		||||
						ForwardHostEnvVars: tc.forwardHostEnvVars,
 | 
			
		||||
					})
 | 
			
		||||
					p, err := f(context.Background(), &plugins.Plugin{JSONData: plugins.JSONData{ID: pluginID}})
 | 
			
		||||
					require.NoError(t, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@ func ProvideConfig(settingProvider setting.Provider, grafanaCfg *setting.Cfg, fe
 | 
			
		||||
		grafanaCfg.GrafanaComURL,
 | 
			
		||||
		grafanaCfg.DisablePlugins,
 | 
			
		||||
		grafanaCfg.HideAngularDeprecation,
 | 
			
		||||
		grafanaCfg.ForwardHostEnvVars,
 | 
			
		||||
	), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -248,6 +248,7 @@ type Cfg struct {
 | 
			
		||||
	DisablePlugins                   []string
 | 
			
		||||
	HideAngularDeprecation           []string
 | 
			
		||||
	PluginInstallToken               string
 | 
			
		||||
	ForwardHostEnvVars               []string
 | 
			
		||||
 | 
			
		||||
	PluginsCDNURLTemplate    string
 | 
			
		||||
	PluginLogBackendRequests bool
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/ini.v1"
 | 
			
		||||
 | 
			
		||||
	"github.com/grafana/grafana/pkg/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PluginSettings maps plugin id to map of key/value settings.
 | 
			
		||||
@@ -33,40 +35,17 @@ func (cfg *Cfg) readPluginSettings(iniFile *ini.File) error {
 | 
			
		||||
	cfg.PluginSkipPublicKeyDownload = pluginsSection.Key("public_key_retrieval_disabled").MustBool(false)
 | 
			
		||||
	cfg.PluginForcePublicKeyDownload = pluginsSection.Key("public_key_retrieval_on_startup").MustBool(false)
 | 
			
		||||
 | 
			
		||||
	pluginsAllowUnsigned := pluginsSection.Key("allow_loading_unsigned_plugins").MustString("")
 | 
			
		||||
 | 
			
		||||
	for _, plug := range strings.Split(pluginsAllowUnsigned, ",") {
 | 
			
		||||
		plug = strings.TrimSpace(plug)
 | 
			
		||||
		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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hideAngularDeprecation := pluginsSection.Key("hide_angular_deprecation").MustString("")
 | 
			
		||||
	for _, id := range strings.Split(hideAngularDeprecation, ",") {
 | 
			
		||||
		id = strings.TrimSpace(id)
 | 
			
		||||
		if id == "" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		cfg.HideAngularDeprecation = append(cfg.HideAngularDeprecation, id)
 | 
			
		||||
	}
 | 
			
		||||
	cfg.PluginsAllowUnsigned = util.SplitString(pluginsSection.Key("allow_loading_unsigned_plugins").MustString(""))
 | 
			
		||||
	cfg.DisablePlugins = util.SplitString(pluginsSection.Key("disable_plugins").MustString(""))
 | 
			
		||||
	cfg.HideAngularDeprecation = util.SplitString(pluginsSection.Key("hide_angular_deprecation").MustString(""))
 | 
			
		||||
	cfg.ForwardHostEnvVars = util.SplitString(pluginsSection.Key("forward_host_env_vars").MustString(""))
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
	catalogHiddenPlugins := pluginsSection.Key("plugin_catalog_hidden_plugins").MustString("")
 | 
			
		||||
	cfg.PluginCatalogHiddenPlugins = util.SplitString(pluginsSection.Key("plugin_catalog_hidden_plugins").MustString(""))
 | 
			
		||||
 | 
			
		||||
	for _, plug := range strings.Split(catalogHiddenPlugins, ",") {
 | 
			
		||||
		plug = strings.TrimSpace(plug)
 | 
			
		||||
		cfg.PluginCatalogHiddenPlugins = append(cfg.PluginCatalogHiddenPlugins, plug)
 | 
			
		||||
	}
 | 
			
		||||
	// Pull disablep plugins from the catalog
 | 
			
		||||
	// Pull disabled plugins from the catalog
 | 
			
		||||
	cfg.PluginCatalogHiddenPlugins = append(cfg.PluginCatalogHiddenPlugins, cfg.DisablePlugins...)
 | 
			
		||||
 | 
			
		||||
	// Plugins CDN settings
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package setting
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
@@ -43,23 +44,55 @@ func TestPluginSettings(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_readPluginSettings(t *testing.T) {
 | 
			
		||||
	t.Run("should parse plugin ids", 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)
 | 
			
		||||
	t.Run("should parse separated plugin ids", func(t *testing.T) {
 | 
			
		||||
		for _, tc := range []struct {
 | 
			
		||||
			name string
 | 
			
		||||
			f    func(ids ...string) string
 | 
			
		||||
		}{
 | 
			
		||||
			{
 | 
			
		||||
				name: "commas",
 | 
			
		||||
				f: func(ids ...string) string {
 | 
			
		||||
					return strings.Join(ids, ",")
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: "commas and a space",
 | 
			
		||||
				f: func(ids ...string) string {
 | 
			
		||||
					return strings.Join(ids, ", ")
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: "spaces",
 | 
			
		||||
				f: func(ids ...string) string {
 | 
			
		||||
					return strings.Join(ids, " ")
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: "json-like",
 | 
			
		||||
				f: func(ids ...string) string {
 | 
			
		||||
					return `["` + strings.Join(ids, `","`) + `"]`
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		} {
 | 
			
		||||
			t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
				cfg := NewCfg()
 | 
			
		||||
				sec, err := cfg.Raw.NewSection("plugins")
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
				_, err = sec.NewKey("disable_plugins", tc.f("plugin1", "plugin2"))
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
		_, err = sec.NewKey("plugin_catalog_hidden_plugins", "plugin3")
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
				_, err = sec.NewKey("plugin_catalog_hidden_plugins", tc.f("plugin3"))
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
		_, err = sec.NewKey("hide_angular_deprecation", "a,b,c")
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
				_, err = sec.NewKey("hide_angular_deprecation", tc.f("a", "b", "c"))
 | 
			
		||||
				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)
 | 
			
		||||
		require.Equal(t, []string{"a", "b", "c"}, cfg.HideAngularDeprecation)
 | 
			
		||||
				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)
 | 
			
		||||
				require.Equal(t, []string{"a", "b", "c"}, cfg.HideAngularDeprecation)
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user