Compare commits

..
Author SHA1 Message Date
Andrew Stuart a1b8372762 Fix nil pointer on watch function (#568) 2018-09-28 01:07:09 -07:00
6 changed files with 18 additions and 173 deletions
+3 -2
View File
@@ -2,8 +2,9 @@ go_import_path: github.com/spf13/viper
language: go language: go
go: go:
- 1.10.x - 1.7.x
- 1.11.x - 1.8.x
- 1.9.x
- tip - tip
os: os:
+1 -44
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 a prefix while reading from using `SetEnvPrefix`, you can tell Viper to use add 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.
@@ -373,33 +373,12 @@ how to use Consul.
### Remote Key/Value Store Example - Unencrypted ### Remote Key/Value Store Example - Unencrypted
#### etcd
```go ```go
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop" viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
err := viper.ReadRemoteConfig() err := viper.ReadRemoteConfig()
``` ```
#### Consul
You need to set a key to Consul key/value storage with JSON value containing your desired config.
For example, create a Consul key/value store key `MY_CONSUL_KEY` with value:
```json
{
"port": 8080,
"hostname": "myhostname.com"
}
```
```go
viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
viper.SetConfigType("json") // Need to explicitly set this to json
err := viper.ReadRemoteConfig()
fmt.Println(viper.Get("port")) // 8080
fmt.Println(viper.Get("hostname")) // myhostname.com
```
### Remote Key/Value Store Example - Encrypted ### Remote Key/Value Store Example - Encrypted
```go ```go
@@ -458,7 +437,6 @@ 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
@@ -612,27 +590,6 @@ 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
-16
View File
@@ -1,16 +0,0 @@
module github.com/spf13/viper
require (
github.com/fsnotify/fsnotify v1.4.7
github.com/hashicorp/hcl v1.0.0
github.com/magiconair/properties v1.8.0
github.com/mitchellh/mapstructure v1.0.0
github.com/pelletier/go-toml v1.2.0
github.com/spf13/afero v1.1.2
github.com/spf13/cast v1.2.0
github.com/spf13/jwalterweatherman v1.0.0
github.com/spf13/pflag v1.0.2
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 // indirect
golang.org/x/text v0.3.0 // indirect
gopkg.in/yaml.v2 v2.2.1
)
-26
View File
@@ -1,26 +0,0 @@
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg=
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+10 -43
View File
@@ -113,23 +113,6 @@ 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
@@ -650,10 +633,8 @@ func (v *Viper) Get(key string) interface{} {
return cast.ToBool(val) return cast.ToBool(val)
case string: case string:
return cast.ToString(val) return cast.ToString(val)
case int32, int16, int8, int: case int64, int32, int16, int8, int:
return cast.ToInt(val) return cast.ToInt(val)
case int64:
return cast.ToInt64(val)
case float64, float32: case float64, float32:
return cast.ToFloat64(val) return cast.ToFloat64(val)
case time.Time: case time.Time:
@@ -703,12 +684,6 @@ 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,11 +741,9 @@ 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{}, opts ...DecoderConfigOption) error { func UnmarshalKey(key string, rawVal interface{}) error { return v.UnmarshalKey(key, rawVal) }
return v.UnmarshalKey(key, rawVal, opts...) func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error {
} 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
@@ -783,11 +756,9 @@ func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConf
// 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{}, opts ...DecoderConfigOption) error { func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) }
return v.Unmarshal(rawVal, opts...) func (v *Viper) Unmarshal(rawVal interface{}) error {
} 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
@@ -800,8 +771,8 @@ func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) 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{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig {
c := &mapstructure.DecoderConfig{ return &mapstructure.DecoderConfig{
Metadata: nil, Metadata: nil,
Result: output, Result: output,
WeaklyTypedInput: true, WeaklyTypedInput: true,
@@ -810,10 +781,6 @@ func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *maps
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
@@ -1143,7 +1110,7 @@ func (v *Viper) SetDefault(key string, value interface{}) {
deepestMap[lastKey] = value deepestMap[lastKey] = value
} }
// Set sets the value for the key in the override register. // Set sets the value for the key in the override regiser.
// Set is case-insensitive for a key. // Set is case-insensitive for a key.
// Will be used instead of values obtained via // Will be used instead of values obtained via
// flags, config file, ENV, default, or key/value store. // flags, config file, ENV, default, or key/value store.
+4 -42
View File
@@ -7,7 +7,6 @@ package viper
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@@ -19,7 +18,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/mitchellh/mapstructure"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cast" "github.com/spf13/cast"
@@ -505,42 +503,6 @@ 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)
@@ -1102,8 +1064,8 @@ func TestMergeConfig(t *testing.T) {
t.Fatalf("pop != 37890, = %d", pop) t.Fatalf("pop != 37890, = %d", pop)
} }
if pop := v.GetInt32("hello.pop"); pop != int32(37890) { if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 {
t.Fatalf("pop != 37890, = %d", pop) t.Fatalf("lagrenum != 765432101234567, = %d", pop)
} }
if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) {
@@ -1126,8 +1088,8 @@ func TestMergeConfig(t *testing.T) {
t.Fatalf("pop != 45000, = %d", pop) t.Fatalf("pop != 45000, = %d", pop)
} }
if pop := v.GetInt32("hello.pop"); pop != int32(45000) { if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 {
t.Fatalf("pop != 45000, = %d", pop) t.Fatalf("lagrenum != 7654321001234567, = %d", pop)
} }
if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) {