From 1d4667aa28062ade38eb9326ea1190de0a97c8a0 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Tue, 30 Apr 2024 11:41:50 +0200 Subject: [PATCH] Add function to resolve environment variables in config values. --- config.go | 32 +++++++++++++++++++++++++++++++- config_test.go | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index c7d316c..a1964f4 100644 --- a/config.go +++ b/config.go @@ -23,10 +23,40 @@ package signaling import ( "errors" + "os" + "regexp" "github.com/dlintw/goconf" ) +var ( + searchVarsRegexp = regexp.MustCompile(`\$\([A-Za-z][A-Za-z0-9]*\)`) +) + +func replaceEnvVars(s string) string { + return searchVarsRegexp.ReplaceAllStringFunc(s, func(name string) string { + name = name[2 : len(name)-1] + value, found := os.LookupEnv(name) + if !found { + return name + } + + return value + }) +} + +// GetStringOptionWithEnv will get the string option and resolve any environment +// variable references in the form "$(VAR)". +func GetStringOptionWithEnv(config *goconf.ConfigFile, section string, option string) (string, error) { + value, err := config.GetString(section, option) + if err != nil { + return "", err + } + + value = replaceEnvVars(value) + return value, nil +} + func GetStringOptions(config *goconf.ConfigFile, section string, ignoreErrors bool) (map[string]string, error) { options, _ := config.GetOptions(section) if len(options) == 0 { @@ -35,7 +65,7 @@ func GetStringOptions(config *goconf.ConfigFile, section string, ignoreErrors bo result := make(map[string]string) for _, option := range options { - value, err := config.GetString(section, option) + value, err := GetStringOptionWithEnv(config, section, option) if err != nil { if ignoreErrors { continue diff --git a/config_test.go b/config_test.go index d14ba94..ed83823 100644 --- a/config_test.go +++ b/config_test.go @@ -29,13 +29,19 @@ import ( ) func TestStringOptions(t *testing.T) { + t.Setenv("FOO", "foo") expected := map[string]string{ "one": "1", "two": "2", + "foo": "http://foo/1", } config := goconf.NewConfigFile() for k, v := range expected { - config.AddOption("foo", k, v) + if k == "foo" { + config.AddOption("foo", k, "http://$(FOO)/1") + } else { + config.AddOption("foo", k, v) + } } config.AddOption("default", "three", "3") @@ -48,3 +54,36 @@ func TestStringOptions(t *testing.T) { t.Errorf("expected %+v, got %+v", expected, options) } } + +func TestStringOptionWithEnv(t *testing.T) { + t.Setenv("FOO", "foo") + t.Setenv("BAR", "") + + config := goconf.NewConfigFile() + config.AddOption("test", "foo", "http://$(FOO)/1") + config.AddOption("test", "bar", "http://$(BAR)/2") + config.AddOption("test", "baz", "http://$(BAZ)/3") + config.AddOption("test", "inv1", "http://$(FOO") + config.AddOption("test", "inv2", "http://$FOO)") + config.AddOption("test", "inv3", "http://$((FOO)") + config.AddOption("test", "inv4", "http://$(F.OO)") + + expected := map[string]string{ + "foo": "http://foo/1", + "bar": "http:///2", + "baz": "http://BAZ/3", + "inv1": "http://$(FOO", + "inv2": "http://$FOO)", + "inv3": "http://$((FOO)", + "inv4": "http://$(F.OO)", + } + for k, v := range expected { + value, err := GetStringOptionWithEnv(config, "test", k) + if err != nil { + t.Errorf("expected value for %s, got %s", k, err) + } else if value != v { + t.Errorf("expected value %s for %s, got %s", v, k, value) + } + } + +}