From 673ccdc448a8de0f741f86369e58afde2d476d1d Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 10 Jan 2020 15:33:54 +0100 Subject: [PATCH] Settings: Env override support for dynamic settings (#21439) * Settings: supports env overrrides for dynamic settings * Settings: makes it possible to explicitly get env override support for dynamic settings * Make linter happy --- pkg/setting/dynamic_settings_test.go | 35 +++++++++++++++++++++ pkg/setting/setting.go | 47 +++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 pkg/setting/dynamic_settings_test.go diff --git a/pkg/setting/dynamic_settings_test.go b/pkg/setting/dynamic_settings_test.go new file mode 100644 index 00000000000..b580f5914df --- /dev/null +++ b/pkg/setting/dynamic_settings_test.go @@ -0,0 +1,35 @@ +package setting + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDynamicSettingsSupport_Override(t *testing.T) { + cfg := NewCfg() + + envKey := "GF_FOO_BAR" + sectionName := "foo" + keyName := "bar" + expected := "dynamic value" + + os.Setenv(envKey, expected) + defer func() { os.Unsetenv(envKey) }() + + value := cfg.SectionWithEnvOverrides(sectionName).Key(keyName).MustString("default value") + require.Equal(t, expected, value) +} + +func TestDynamicSettingsSupport_NoOverride(t *testing.T) { + cfg := NewCfg() + sectionName := "foo" + keyName := "bar" + expected := "default value" + + _, err := cfg.Raw.Section(sectionName).NewKey(keyName, expected) + require.NoError(t, err) + value := cfg.SectionWithEnvOverrides(sectionName).Key(keyName).String() + require.Equal(t, expected, value) +} diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 1df1c0f5151..ae8ab9bd87a 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -32,6 +32,7 @@ const ( HTTP2 Scheme = "h2" SOCKET Scheme = "socket" DEFAULT_HTTP_ADDR string = "0.0.0.0" + REDACTED_PASSWORD string = "*********" ) const ( @@ -327,15 +328,13 @@ func applyEnvVariableOverrides(file *ini.File) error { appliedEnvOverrides = make([]string, 0) for _, section := range file.Sections() { for _, key := range section.Keys() { - sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1)) - keyName := strings.ToUpper(strings.Replace(key.Name(), ".", "_", -1)) - envKey := fmt.Sprintf("GF_%s_%s", sectionName, keyName) + envKey := envKey(section.Name(), key.Name()) envValue := os.Getenv(envKey) if len(envValue) > 0 { key.SetValue(envValue) if shouldRedactKey(envKey) { - envValue = "*********" + envValue = REDACTED_PASSWORD } if shouldRedactURLKey(envKey) { u, err := url.Parse(envValue) @@ -359,6 +358,13 @@ func applyEnvVariableOverrides(file *ini.File) error { return nil } +func envKey(sectionName string, keyName string) string { + sN := strings.ToUpper(strings.Replace(sectionName, ".", "_", -1)) + kN := strings.ToUpper(strings.Replace(keyName, ".", "_", -1)) + envKey := fmt.Sprintf("GF_%s_%s", sN, kN) + return envKey +} + func applyCommandLineDefaultProperties(props map[string]string, file *ini.File) { appliedCommandLineProperties = make([]string, 0) for _, section := range file.Sections() { @@ -368,7 +374,7 @@ func applyCommandLineDefaultProperties(props map[string]string, file *ini.File) if exists { key.SetValue(value) if shouldRedactKey(keyString) { - value = "*********" + value = REDACTED_PASSWORD } appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value)) } @@ -1114,6 +1120,37 @@ func (cfg *Cfg) LogConfigSources() { cfg.Logger.Info("App mode " + Env) } +type DynamicSection struct { + section *ini.Section + Logger log.Logger +} + +// Key dynamically overrides keys with environment variables. +// As a side effect, the value of the setting key will be updated if an environment variable is present. +func (s *DynamicSection) Key(k string) *ini.Key { + envKey := envKey(s.section.Name(), k) + envValue := os.Getenv(envKey) + key := s.section.Key(k) + + if len(envValue) == 0 { + return key + } + + key.SetValue(envValue) + if shouldRedactKey(envKey) { + envValue = REDACTED_PASSWORD + } + s.Logger.Info("Config overridden from Environment variable", "var", fmt.Sprintf("%s=%s", envKey, envValue)) + + return key +} + +// SectionWithEnvOverrides dynamically overrides keys with environment variables. +// As a side effect, the value of the setting key will be updated if an environment variable is present. +func (cfg *Cfg) SectionWithEnvOverrides(s string) *DynamicSection { + return &DynamicSection{cfg.Raw.Section(s), cfg.Logger} +} + func IsExpressionsEnabled() bool { v, ok := FeatureToggles["expressions"] if !ok {