Suppose I want to have a nested variable with the key foo.bar and I want viper to automatically take the value from an environment variable.
Since foo.bar or FOO.BAR is not a valid environment variable name due to the dot, I have no clue what environment variable I need to set.
To put the question differently: How does viper substitute the . in nested variables when mapped to environment variables?
To show what I mean, here's an example code snippet:
package main_test
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func ExampleSimpleEnvVar() {
os.Setenv("FOO", "bar")
viper.AutomaticEnv()
fmt.Printf("foo=%v", viper.Get("foo"))
// Output: foo=bar
}
func ExampleNestedValue() {
os.Setenv("FOO_BAR", "blabla")
viper.SetDefault("foo.bar", "default")
viper.AutomaticEnv()
fmt.Printf("foo.bar=%v", viper.Get("foo.bar"))
// Output: foo.bar=blabla
}
The ExampleSimpleEnvVar() is there to proof that my approach works at least for non-nested environment variables. But the ExampleNestedValue() function fails:
$ go test -v
=== RUN ExampleSimpleEnvVar
--- PASS: ExampleSimpleEnvVar (0.00s)
=== RUN ExampleNestedValue
--- FAIL: ExampleNestedValue (0.00s)
got:
foo.bar=default
want:
foo.bar=blabla
FAIL
exit status 1
FAIL github.com/kwk/viper-test 0.005s
Viper version: a78f70b5b977efe08e313a9e2341c3f5457abdaf
Go version: go version go1.7 linux/amd64
Okay, I could use
os.Setenv("FOO.BAR", "blabla")
but in the real world this implies that whatever system I'm using supports dots in the identifier of a variable.
For example, export doesn't work:
$ export FOO.BAR=42
bash: export: `FOO.BAR=42': not a valid identifier
With env I can set FOO.BAR but I cannot read it again.
$ env "FOO.BAR=42" echo "${FOO.BAR}"
bash: ${FOO.BAR}: bad substitution
Using the viper.SetEnvKeyReplacer(replacer) worked for me:
package main_test
import (
"fmt"
"os"
"strings"
"github.com/spf13/viper"
)
func ExampleSimpleEnvVar() {
os.Setenv("FOO", "bar")
viper.AutomaticEnv()
fmt.Printf("foo=%v", viper.Get("foo"))
// Output: foo=bar
}
func ExampleNestedValue() {
os.Setenv("FOO_BAR", "blabla")
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
viper.SetDefault("foo.bar", "default")
viper.AutomaticEnv()
fmt.Printf("foo.bar=%v", viper.Get("foo.bar"))
// Output: foo.bar=blabla
}
$ go test -v
=== RUN ExampleSimpleEnvVar
--- PASS: ExampleSimpleEnvVar (0.00s)
=== RUN ExampleNestedValue
--- PASS: ExampleNestedValue (0.00s)
PASS
ok github.com/kwk/viper-test 0.005s
Great!
I believe this does not work with viper.Sub("foo").GetString("bar")?
Not a big deal but would be nice to be able to pass sub config to other layer :-D
Does anyone know if it is possible to achieve?
@tinhtooaung It should work by calling SetEnvPrefix and AutomaticEnv on sub manually.
Most helpful comment
Problem solved!
Using the
viper.SetEnvKeyReplacer(replacer)worked for me:Proof