mirror of https://github.com/spf13/viper.git
Merge branch 'master' into Issue284_Kubernetes_config
This commit is contained in:
commit
e12d3d32d1
|
@ -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:
|
||||||
|
|
24
README.md
24
README.md
|
@ -191,7 +191,7 @@ _When working with ENV variables, it’s 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 it’s not found. To check if a given key exists, the `IsSet()` method
|
value if it’s 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
|
||||||
|
|
65
viper.go
65
viper.go
|
@ -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 != "" {
|
cf, err := v.findConfigFile()
|
||||||
return v.configFile, nil
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
v.configFile = cf
|
||||||
}
|
}
|
||||||
|
return v.configFile, nil
|
||||||
cf, err := v.findConfigFile()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
v.configFile = cf
|
|
||||||
return v.getConfigFile()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) searchInPath(in string) (filename string) {
|
func (v *Viper) searchInPath(in string) (filename string) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue