diff --git a/viper.go b/viper.go index 1875627..78ee144 100644 --- a/viper.go +++ b/viper.go @@ -33,19 +33,19 @@ import ( "sync" "time" - yaml "gopkg.in/yaml.v2" + "github.com/ghodss/yaml" - "github.com/fsnotify/fsnotify" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/printer" "github.com/magiconair/properties" "github.com/mitchellh/mapstructure" - toml "github.com/pelletier/go-toml" + "github.com/nwingert/fsnotify" + "github.com/pelletier/go-toml" "github.com/spf13/afero" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/pflag" -) + ) // ConfigMarshalError happens when failing to marshal the configuration. type ConfigMarshalError struct { @@ -203,6 +203,8 @@ type Viper struct { properties *properties.Properties onConfigChange func(fsnotify.Event) + + sync.Mutex } // New returns an initialized Viper instance. @@ -952,6 +954,8 @@ func (v *Viper) BindEnv(input ...string) error { // Viper will check to see if an alias exists first. // Note: this assumes a lower-cased key given. func (v *Viper) find(lcaseKey string) interface{} { + v.Lock() + defer v.Unlock() var ( val interface{} @@ -1222,8 +1226,9 @@ func (v *Viper) ReadInConfig() error { if err != nil { return err } - + v.Lock() v.config = config + v.Unlock() return nil } @@ -1647,6 +1652,9 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface // Nested keys are returned with a v.keyDelim (= ".") separator func AllKeys() []string { return v.AllKeys() } func (v *Viper) AllKeys() []string { + v.Lock() + defer v.Unlock() + m := map[string]bool{} // add all paths, by order of descending priority to ensure correct shadowing m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "") @@ -1656,7 +1664,6 @@ func (v *Viper) AllKeys() []string { m = v.flattenAndMergeMap(m, v.config, "") m = v.flattenAndMergeMap(m, v.kvstore, "") m = v.flattenAndMergeMap(m, v.defaults, "") - // convert set of paths to list a := []string{} for x := range m { @@ -1731,6 +1738,11 @@ func (v *Viper) AllSettings() map[string]interface{} { // start from the list of keys, and construct the map one value at a time for _, k := range v.AllKeys() { value := v.Get(k) + // ensure that marshalling to json will be supported + switch v := value.(type) { + case map[interface{}]interface{}: + value = castToMapStringInterface(v) + } if value == nil { // should not happen, since AllKeys() returns only keys holding a value, // check just in case anything changes