mirror of
https://github.com/spf13/viper
synced 2024-12-23 03:57:01 +00:00
Lookup environment variables instead of checking if the value is empty
This commit adds an `AllowEmptyEnv` option that, default off, that when set will allow set, but empty, environment variables Fixes #317
This commit is contained in:
parent
62edee3196
commit
b7a3b95476
3 changed files with 53 additions and 7 deletions
|
@ -179,13 +179,14 @@ viper.GetBool("verbose") // true
|
||||||
### Working with Environment Variables
|
### Working with Environment Variables
|
||||||
|
|
||||||
Viper has full support for environment variables. This enables 12 factor
|
Viper has full support for environment variables. This enables 12 factor
|
||||||
applications out of the box. There are four methods that exist to aid working
|
applications out of the box. There are five methods that exist to aid working
|
||||||
with ENV:
|
with ENV:
|
||||||
|
|
||||||
* `AutomaticEnv()`
|
* `AutomaticEnv()`
|
||||||
* `BindEnv(string...) : error`
|
* `BindEnv(string...) : error`
|
||||||
* `SetEnvPrefix(string)`
|
* `SetEnvPrefix(string)`
|
||||||
* `SetEnvKeyReplacer(string...) *strings.Replacer`
|
* `SetEnvKeyReplacer(string...) *strings.Replacer`
|
||||||
|
* `AllowEmptyEnvVar(bool)`
|
||||||
|
|
||||||
_When working with ENV variables, it’s important to recognize that Viper
|
_When working with ENV variables, it’s important to recognize that Viper
|
||||||
treats ENV variables as case sensitive._
|
treats ENV variables as case sensitive._
|
||||||
|
@ -217,6 +218,10 @@ keys to an extent. This is useful if you want to use `-` or something in your
|
||||||
`Get()` calls, but want your environmental variables to use `_` delimiters. An
|
`Get()` calls, but want your environmental variables to use `_` delimiters. An
|
||||||
example of using it can be found in `viper_test.go`.
|
example of using it can be found in `viper_test.go`.
|
||||||
|
|
||||||
|
By default empty environment variables are considered unset and will fall back to
|
||||||
|
the next configuration source. To treat empty environment variables as set, use
|
||||||
|
the `AllowEmptyEnv` method.
|
||||||
|
|
||||||
#### Env example
|
#### Env example
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
23
viper.go
23
viper.go
|
@ -187,6 +187,7 @@ type Viper struct {
|
||||||
|
|
||||||
automaticEnvApplied bool
|
automaticEnvApplied bool
|
||||||
envKeyReplacer *strings.Replacer
|
envKeyReplacer *strings.Replacer
|
||||||
|
allowEmptyEnv bool
|
||||||
|
|
||||||
config map[string]interface{}
|
config map[string]interface{}
|
||||||
override map[string]interface{}
|
override map[string]interface{}
|
||||||
|
@ -373,6 +374,14 @@ func (v *Viper) mergeWithEnvPrefix(in string) string {
|
||||||
return strings.ToUpper(in)
|
return strings.ToUpper(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllowEmptyEnv tells Viper to consider set,
|
||||||
|
// but empty environment variables as valid values instead of falling back.
|
||||||
|
// For backward compatibility reasons this is false by default.
|
||||||
|
func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }
|
||||||
|
func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
|
||||||
|
v.allowEmptyEnv = allowEmptyEnv
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: should getEnv logic be moved into find(). Can generalize the use of
|
// TODO: should getEnv logic be moved into find(). Can generalize the use of
|
||||||
// rewriting keys many things, Ex: Get('someKey') -> some_key
|
// rewriting keys many things, Ex: Get('someKey') -> some_key
|
||||||
// (camel case to snake case for JSON keys perhaps)
|
// (camel case to snake case for JSON keys perhaps)
|
||||||
|
@ -380,11 +389,14 @@ func (v *Viper) mergeWithEnvPrefix(in string) string {
|
||||||
// getEnv is a wrapper around os.Getenv which replaces characters in the original
|
// getEnv is a wrapper around os.Getenv which replaces characters in the original
|
||||||
// key. This allows env vars which have different keys than the config object
|
// key. This allows env vars which have different keys than the config object
|
||||||
// keys.
|
// keys.
|
||||||
func (v *Viper) getEnv(key string) string {
|
func (v *Viper) getEnv(key string) (string, bool) {
|
||||||
if v.envKeyReplacer != nil {
|
if v.envKeyReplacer != nil {
|
||||||
key = v.envKeyReplacer.Replace(key)
|
key = v.envKeyReplacer.Replace(key)
|
||||||
}
|
}
|
||||||
return os.Getenv(key)
|
|
||||||
|
val, ok := os.LookupEnv(key)
|
||||||
|
|
||||||
|
return val, ok && (v.allowEmptyEnv || val != "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigFileUsed returns the file used to populate the config registry.
|
// ConfigFileUsed returns the file used to populate the config registry.
|
||||||
|
@ -611,10 +623,9 @@ func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
|
||||||
// "foo.bar.baz" in a lower-priority map
|
// "foo.bar.baz" in a lower-priority map
|
||||||
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
||||||
var parentKey string
|
var parentKey string
|
||||||
var val string
|
|
||||||
for i := 1; i < len(path); i++ {
|
for i := 1; i < len(path); i++ {
|
||||||
parentKey = strings.Join(path[0:i], v.keyDelim)
|
parentKey = strings.Join(path[0:i], v.keyDelim)
|
||||||
if val = v.getEnv(v.mergeWithEnvPrefix(parentKey)); val != "" {
|
if _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok {
|
||||||
return parentKey
|
return parentKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -993,7 +1004,7 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
if v.automaticEnvApplied {
|
if v.automaticEnvApplied {
|
||||||
// even if it hasn't been registered, if automaticEnv is used,
|
// even if it hasn't been registered, if automaticEnv is used,
|
||||||
// check any Get request
|
// check any Get request
|
||||||
if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" {
|
if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
if nested && v.isPathShadowedInAutoEnv(path) != "" {
|
if nested && v.isPathShadowedInAutoEnv(path) != "" {
|
||||||
|
@ -1002,7 +1013,7 @@ func (v *Viper) find(lcaseKey string) interface{} {
|
||||||
}
|
}
|
||||||
envkey, exists := v.env[lcaseKey]
|
envkey, exists := v.env[lcaseKey]
|
||||||
if exists {
|
if exists {
|
||||||
if val = v.getEnv(envkey); val != "" {
|
if val, ok := v.getEnv(envkey); ok {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,6 +388,36 @@ func TestEnv(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEmptyEnv(t *testing.T) {
|
||||||
|
initJSON()
|
||||||
|
|
||||||
|
BindEnv("type") // Empty environment variable
|
||||||
|
BindEnv("name") // Bound, but not set environment variable
|
||||||
|
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
os.Setenv("TYPE", "")
|
||||||
|
|
||||||
|
assert.Equal(t, "donut", Get("type"))
|
||||||
|
assert.Equal(t, "Cake", Get("name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyEnv_Allowed(t *testing.T) {
|
||||||
|
initJSON()
|
||||||
|
|
||||||
|
AllowEmptyEnv(true)
|
||||||
|
|
||||||
|
BindEnv("type") // Empty environment variable
|
||||||
|
BindEnv("name") // Bound, but not set environment variable
|
||||||
|
|
||||||
|
os.Clearenv()
|
||||||
|
|
||||||
|
os.Setenv("TYPE", "")
|
||||||
|
|
||||||
|
assert.Equal(t, "", Get("type"))
|
||||||
|
assert.Equal(t, "Cake", Get("name"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestEnvPrefix(t *testing.T) {
|
func TestEnvPrefix(t *testing.T) {
|
||||||
initJSON()
|
initJSON()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue