Merge branch 'master' into Issue284_Kubernetes_config

This commit is contained in:
Xavier Coulon 2018-08-06 08:18:53 +02:00
commit e12d3d32d1
5 changed files with 116 additions and 23 deletions

View file

@ -2,9 +2,8 @@ go_import_path: github.com/spf13/viper
language: go language: go
go: go:
- 1.7.x
- 1.8.x
- 1.9.x - 1.9.x
- 1.10.x
- tip - tip
os: os:

View file

@ -191,7 +191,7 @@ _When working with ENV variables, its important to recognize that Viper
treats ENV variables as case sensitive._ treats ENV variables as case sensitive._
Viper provides a mechanism to try to ensure that ENV variables are unique. By Viper provides a mechanism to try to ensure that ENV variables are unique. By
using `SetEnvPrefix`, you can tell Viper to use add a prefix while reading from using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from
the environment variables. Both `BindEnv` and `AutomaticEnv` will use this the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
prefix. prefix.
@ -437,6 +437,7 @@ The following functions and methods exist:
* `GetTime(key string) : time.Time` * `GetTime(key string) : time.Time`
* `GetDuration(key string) : time.Duration` * `GetDuration(key string) : time.Duration`
* `IsSet(key string) : bool` * `IsSet(key string) : bool`
* `AllSettings() : map[string]interface{}`
One important thing to recognize is that each Get function will return a zero One important thing to recognize is that each Get function will return a zero
value if its not found. To check if a given key exists, the `IsSet()` method value if its not found. To check if a given key exists, the `IsSet()` method
@ -590,6 +591,27 @@ if err != nil {
} }
``` ```
### Marshalling to string
You may need to marhsal all the settings held in viper into a string rather than write them to a file.
You can use your favorite format's marshaller with the config returned by `AllSettings()`.
```go
import (
yaml "gopkg.in/yaml.v2"
// ...
)
func yamlStringSettings() string {
c := viper.AllSettings()
bs, err := yaml.Marshal(c)
if err != nil {
t.Fatalf("unable to marshal config to YAML: %v", err)
}
return string(bs)
}
```
## Viper or Vipers? ## Viper or Vipers?
Viper comes ready to use out of the box. There is no configuration or Viper comes ready to use out of the box. There is no configuration or

View file

@ -1 +0,0 @@
QProcess::start: Process is already running

View file

@ -114,6 +114,23 @@ func (fnfe ConfigFileNotFoundError) Error() string {
return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations)
} }
// A DecoderConfigOption can be passed to viper.Unmarshal to configure
// mapstructure.DecoderConfig options
type DecoderConfigOption func(*mapstructure.DecoderConfig)
// DecodeHook returns a DecoderConfigOption which overrides the default
// DecoderConfig.DecodeHook value, the default is:
//
// mapstructure.ComposeDecodeHookFunc(
// mapstructure.StringToTimeDurationHookFunc(),
// mapstructure.StringToSliceHookFunc(","),
// )
func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
return func(c *mapstructure.DecoderConfig) {
c.DecodeHook = hook
}
}
// Viper is a prioritized configuration registry. It // Viper is a prioritized configuration registry. It
// maintains a set of configuration sources, fetches // maintains a set of configuration sources, fetches
// values to populate those, and provides them according // values to populate those, and provides them according
@ -709,6 +726,12 @@ func (v *Viper) GetInt(key string) int {
return cast.ToInt(v.Get(key)) return cast.ToInt(v.Get(key))
} }
// GetInt32 returns the value associated with the key as an integer.
func GetInt32(key string) int32 { return v.GetInt32(key) }
func (v *Viper) GetInt32(key string) int32 {
return cast.ToInt32(v.Get(key))
}
// GetInt64 returns the value associated with the key as an integer. // GetInt64 returns the value associated with the key as an integer.
func GetInt64(key string) int64 { return v.GetInt64(key) } func GetInt64(key string) int64 { return v.GetInt64(key) }
func (v *Viper) GetInt64(key string) int64 { func (v *Viper) GetInt64(key string) int64 {
@ -766,9 +789,11 @@ func (v *Viper) GetSizeInBytes(key string) uint {
} }
// UnmarshalKey takes a single key and unmarshals it into a Struct. // UnmarshalKey takes a single key and unmarshals it into a Struct.
func UnmarshalKey(key string, rawVal interface{}) error { return v.UnmarshalKey(key, rawVal) } func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error { return v.UnmarshalKey(key, rawVal, opts...)
err := decode(v.Get(key), defaultDecoderConfig(rawVal)) }
func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
err := decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
if err != nil { if err != nil {
return err return err
@ -781,9 +806,11 @@ func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error {
// Unmarshal unmarshals the config into a Struct. Make sure that the tags // Unmarshal unmarshals the config into a Struct. Make sure that the tags
// on the fields of the structure are properly set. // on the fields of the structure are properly set.
func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) } func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
func (v *Viper) Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal, opts...)
err := decode(v.AllSettings(), defaultDecoderConfig(rawVal)) }
func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
err := decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
if err != nil { if err != nil {
return err return err
@ -796,8 +823,8 @@ func (v *Viper) Unmarshal(rawVal interface{}) error {
// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot // defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot
// of time.Duration values & string slices // of time.Duration values & string slices
func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig { func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
return &mapstructure.DecoderConfig{ c := &mapstructure.DecoderConfig{
Metadata: nil, Metadata: nil,
Result: output, Result: output,
WeaklyTypedInput: true, WeaklyTypedInput: true,
@ -806,6 +833,10 @@ func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig {
mapstructure.StringToSliceHookFunc(","), mapstructure.StringToSliceHookFunc(","),
), ),
} }
for _, opt := range opts {
opt(c)
}
return c
} }
// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality // A wrapper around mapstructure.Decode that mimics the WeakDecode functionality
@ -1747,18 +1778,14 @@ func (v *Viper) getConfigType() string {
} }
func (v *Viper) getConfigFile() (string, error) { func (v *Viper) getConfigFile() (string, error) {
// if explicitly set, then use it if v.configFile == "" {
if v.configFile != "" {
return v.configFile, nil
}
cf, err := v.findConfigFile() cf, err := v.findConfigFile()
if err != nil { if err != nil {
return "", err return "", err
} }
v.configFile = cf v.configFile = cf
return v.getConfigFile() }
return v.configFile, nil
} }
func (v *Viper) searchInPath(in string) (filename string) { func (v *Viper) searchInPath(in string) (filename string) {

View file

@ -7,6 +7,7 @@ package viper
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -22,6 +23,7 @@ import (
"time" "time"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/mitchellh/mapstructure"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cast" "github.com/spf13/cast"
@ -508,6 +510,42 @@ func TestUnmarshal(t *testing.T) {
assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C) assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C)
} }
func TestUnmarshalWithDecoderOptions(t *testing.T) {
Set("credentials", "{\"foo\":\"bar\"}")
opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
// Custom Decode Hook Function
func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
if rf != reflect.String || rt != reflect.Map {
return data, nil
}
m := map[string]string{}
raw := data.(string)
if raw == "" {
return m, nil
}
return m, json.Unmarshal([]byte(raw), &m)
},
))
type config struct {
Credentials map[string]string
}
var C config
err := Unmarshal(&C, opt)
if err != nil {
t.Fatalf("unable to decode into struct, %v", err)
}
assert.Equal(t, &config{
Credentials: map[string]string{"foo": "bar"},
}, &C)
}
func TestBindPFlags(t *testing.T) { func TestBindPFlags(t *testing.T) {
v := New() // create independent Viper object v := New() // create independent Viper object
flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
@ -1073,6 +1111,10 @@ func TestMergeConfig(t *testing.T) {
t.Fatalf("lagrenum != 765432101234567, = %d", pop) t.Fatalf("lagrenum != 765432101234567, = %d", pop)
} }
if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
t.Fatalf("pop != 37890, = %d", pop)
}
if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) {
t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop)
} }
@ -1097,6 +1139,10 @@ func TestMergeConfig(t *testing.T) {
t.Fatalf("lagrenum != 7654321001234567, = %d", pop) t.Fatalf("lagrenum != 7654321001234567, = %d", pop)
} }
if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
t.Fatalf("pop != 45000, = %d", pop)
}
if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) {
t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop)
} }