From ce95343c2fdcc98891b9f536062b69192dc49b13 Mon Sep 17 00:00:00 2001 From: victor Date: Mon, 15 Oct 2018 11:07:15 +0800 Subject: [PATCH] remove all remote code and providers except json --- go.mod | 6 +- go.sum | 8 - remote/remote.go | 105 -------- viper.go | 319 +--------------------- viper_test.go | 680 ----------------------------------------------- 5 files changed, 3 insertions(+), 1115 deletions(-) delete mode 100644 remote/remote.go diff --git a/go.mod b/go.mod index 3f4e1c2..d3387a5 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,12 @@ -module github.com/spf13/viper +module github.com/xurwxj/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 ) diff --git a/go.sum b/go.sum index 3e3b874..6763604 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,8 @@ 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= @@ -22,5 +16,3 @@ golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5h 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= diff --git a/remote/remote.go b/remote/remote.go deleted file mode 100644 index 810d070..0000000 --- a/remote/remote.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// Package remote integrates the remote features of Viper. -package remote - -import ( - "bytes" - "io" - "os" - - "github.com/spf13/viper" - crypt "github.com/xordataexchange/crypt/config" -) - -type remoteConfigProvider struct{} - -func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) { - cm, err := getConfigManager(rp) - if err != nil { - return nil, err - } - b, err := cm.Get(rp.Path()) - if err != nil { - return nil, err - } - return bytes.NewReader(b), nil -} - -func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) { - cm, err := getConfigManager(rp) - if err != nil { - return nil, err - } - resp, err := cm.Get(rp.Path()) - if err != nil { - return nil, err - } - - return bytes.NewReader(resp), nil -} - -func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) { - cm, err := getConfigManager(rp) - if err != nil { - return nil, nil - } - quit := make(chan bool) - quitwc := make(chan bool) - viperResponsCh := make(chan *viper.RemoteResponse) - cryptoResponseCh := cm.Watch(rp.Path(), quit) - // need this function to convert the Channel response form crypt.Response to viper.Response - go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) { - for { - select { - case <-quitwc: - quit <- true - return - case resp := <-cr: - vr <- &viper.RemoteResponse{ - Error: resp.Error, - Value: resp.Value, - } - - } - - } - }(cryptoResponseCh, viperResponsCh, quitwc, quit) - - return viperResponsCh, quitwc -} - -func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) { - var cm crypt.ConfigManager - var err error - - if rp.SecretKeyring() != "" { - kr, err := os.Open(rp.SecretKeyring()) - defer kr.Close() - if err != nil { - return nil, err - } - if rp.Provider() == "etcd" { - cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr) - } else { - cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr) - } - } else { - if rp.Provider() == "etcd" { - cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()}) - } else { - cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()}) - } - } - if err != nil { - return nil, err - } - return cm, nil -} - -func init() { - viper.RemoteConfig = &remoteConfigProvider{} -} diff --git a/viper.go b/viper.go index a32ab73..edbb4ac 100644 --- a/viper.go +++ b/viper.go @@ -33,14 +33,9 @@ import ( "sync" "time" - yaml "gopkg.in/yaml.v2" - "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/spf13/afero" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" @@ -59,24 +54,10 @@ func (e ConfigMarshalError) Error() string { var v *Viper -type RemoteResponse struct { - Value []byte - Error error -} - func init() { v = New() } -type remoteConfigFactory interface { - Get(rp RemoteProvider) (io.Reader, error) - Watch(rp RemoteProvider) (io.Reader, error) - WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) -} - -// RemoteConfig is optional, see the remote package -var RemoteConfig remoteConfigFactory - // UnsupportedConfigError denotes encountering an unsupported // configuration filetype. type UnsupportedConfigError string @@ -86,24 +67,6 @@ func (str UnsupportedConfigError) Error() string { return fmt.Sprintf("Unsupported Config Type %q", string(str)) } -// UnsupportedRemoteProviderError denotes encountering an unsupported remote -// provider. Currently only etcd and Consul are supported. -type UnsupportedRemoteProviderError string - -// Error returns the formatted remote provider error. -func (str UnsupportedRemoteProviderError) Error() string { - return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str)) -} - -// RemoteConfigError denotes encountering an error while trying to -// pull the configuration from the remote provider. -type RemoteConfigError string - -// Error returns the formatted remote provider error -func (rce RemoteConfigError) Error() string { - return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) -} - // ConfigFileNotFoundError denotes failing to find configuration file. type ConfigFileNotFoundError struct { name, locations string @@ -176,9 +139,6 @@ type Viper struct { // The filesystem to read config from. fs afero.Fs - // A set of remote providers to search for the configuration - remoteProviders []*defaultRemoteProvider - // Name of file to look for inside the path configName string configFile string @@ -227,49 +187,11 @@ func New() *Viper { // can use it in their testing as well. func Reset() { v = New() - SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} - SupportedRemoteProviders = []string{"etcd", "consul"} -} - -type defaultRemoteProvider struct { - provider string - endpoint string - path string - secretKeyring string -} - -func (rp defaultRemoteProvider) Provider() string { - return rp.provider -} - -func (rp defaultRemoteProvider) Endpoint() string { - return rp.endpoint -} - -func (rp defaultRemoteProvider) Path() string { - return rp.path -} - -func (rp defaultRemoteProvider) SecretKeyring() string { - return rp.secretKeyring -} - -// RemoteProvider stores the configuration necessary -// to connect to a remote key/value store. -// Optional secretKeyring to unencrypt encrypted values -// can be provided. -type RemoteProvider interface { - Provider() string - Endpoint() string - Path() string - SecretKeyring() string + SupportedExts = []string{"json"} } // SupportedExts are universally supported extensions. -var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} - -// SupportedRemoteProviders are universally supported remote providers. -var SupportedRemoteProviders = []string{"etcd", "consul"} +var SupportedExts = []string{"json"} func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { @@ -404,77 +326,6 @@ func (v *Viper) AddConfigPath(in string) { } } -// AddRemoteProvider adds a remote configuration source. -// Remote Providers are searched in the order they are added. -// provider is a string value, "etcd" or "consul" are currently supported. -// endpoint is the url. etcd requires http://ip:port consul requires ip:port -// path is the path in the k/v store to retrieve configuration -// To retrieve a config file called myapp.json from /configs/myapp.json -// you should set path to /configs and set config name (SetConfigName()) to -// "myapp" -func AddRemoteProvider(provider, endpoint, path string) error { - return v.AddRemoteProvider(provider, endpoint, path) -} -func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { - if !stringInSlice(provider, SupportedRemoteProviders) { - return UnsupportedRemoteProviderError(provider) - } - if provider != "" && endpoint != "" { - jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) - rp := &defaultRemoteProvider{ - endpoint: endpoint, - provider: provider, - path: path, - } - if !v.providerPathExists(rp) { - v.remoteProviders = append(v.remoteProviders, rp) - } - } - return nil -} - -// AddSecureRemoteProvider adds a remote configuration source. -// Secure Remote Providers are searched in the order they are added. -// provider is a string value, "etcd" or "consul" are currently supported. -// endpoint is the url. etcd requires http://ip:port consul requires ip:port -// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg -// path is the path in the k/v store to retrieve configuration -// To retrieve a config file called myapp.json from /configs/myapp.json -// you should set path to /configs and set config name (SetConfigName()) to -// "myapp" -// Secure Remote Providers are implemented with github.com/xordataexchange/crypt -func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { - return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) -} - -func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { - if !stringInSlice(provider, SupportedRemoteProviders) { - return UnsupportedRemoteProviderError(provider) - } - if provider != "" && endpoint != "" { - jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) - rp := &defaultRemoteProvider{ - endpoint: endpoint, - provider: provider, - path: path, - secretKeyring: secretkeyring, - } - if !v.providerPathExists(rp) { - v.remoteProviders = append(v.remoteProviders, rp) - } - } - return nil -} - -func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool { - for _, y := range v.remoteProviders { - if reflect.DeepEqual(y, p) { - return true - } - } - return false -} - // searchMap recursively searches for a value for path in source map. // Returns nil if not found. // Note: This assumes that the path entries and map keys are lower cased. @@ -1332,50 +1183,11 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { buf.ReadFrom(in) switch strings.ToLower(v.getConfigType()) { - case "yaml", "yml": - if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil { - return ConfigParseError{err} - } case "json": if err := json.Unmarshal(buf.Bytes(), &c); err != nil { return ConfigParseError{err} } - - case "hcl": - obj, err := hcl.Parse(string(buf.Bytes())) - if err != nil { - return ConfigParseError{err} - } - if err = hcl.DecodeObject(&c, obj); err != nil { - return ConfigParseError{err} - } - - case "toml": - tree, err := toml.LoadReader(buf) - if err != nil { - return ConfigParseError{err} - } - tmap := tree.ToMap() - for k, v := range tmap { - c[k] = v - } - - case "properties", "props", "prop": - v.properties = properties.NewProperties() - var err error - if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil { - return ConfigParseError{err} - } - for _, key := range v.properties.Keys() { - value, _ := v.properties.Get(key) - // recursively build nested maps - path := strings.Split(key, ".") - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(c, path[0:len(path)-1]) - // set innermost value - deepestMap[lastKey] = value - } } insensitiviseMap(c) @@ -1398,52 +1210,6 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error { if err != nil { return ConfigMarshalError{err} } - - case "hcl": - b, err := json.Marshal(c) - ast, err := hcl.Parse(string(b)) - if err != nil { - return ConfigMarshalError{err} - } - err = printer.Fprint(f, ast.Node) - if err != nil { - return ConfigMarshalError{err} - } - - case "prop", "props", "properties": - if v.properties == nil { - v.properties = properties.NewProperties() - } - p := v.properties - for _, key := range v.AllKeys() { - _, _, err := p.Set(key, v.GetString(key)) - if err != nil { - return ConfigMarshalError{err} - } - } - _, err := p.WriteComment(f, "#", properties.UTF8) - if err != nil { - return ConfigMarshalError{err} - } - - case "toml": - t, err := toml.TreeFromMap(c) - if err != nil { - return ConfigMarshalError{err} - } - s := t.String() - if _, err := f.WriteString(s); err != nil { - return ConfigMarshalError{err} - } - - case "yaml", "yml": - b, err := yaml.Marshal(c) - if err != nil { - return ConfigMarshalError{err} - } - if _, err = f.WriteString(string(b)); err != nil { - return ConfigMarshalError{err} - } } return nil } @@ -1544,22 +1310,6 @@ func mergeMaps( } } -// ReadRemoteConfig attempts to get configuration from a remote source -// and read it in the remote configuration registry. -func ReadRemoteConfig() error { return v.ReadRemoteConfig() } -func (v *Viper) ReadRemoteConfig() error { - return v.getKeyValueConfig() -} - -func WatchRemoteConfig() error { return v.WatchRemoteConfig() } -func (v *Viper) WatchRemoteConfig() error { - return v.watchKeyValueConfig() -} - -func (v *Viper) WatchRemoteConfigOnChannel() error { - return v.watchKeyValueConfigOnChannel() -} - func (v *Viper) insensitiviseMaps() { insensitiviseMap(v.config) insensitiviseMap(v.defaults) @@ -1567,71 +1317,6 @@ func (v *Viper) insensitiviseMaps() { insensitiviseMap(v.kvstore) } -// Retrieve the first found remote configuration. -func (v *Viper) getKeyValueConfig() error { - if RemoteConfig == nil { - return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'") - } - - for _, rp := range v.remoteProviders { - val, err := v.getRemoteConfig(rp) - if err != nil { - continue - } - v.kvstore = val - return nil - } - return RemoteConfigError("No Files Found") -} - -func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { - reader, err := RemoteConfig.Get(provider) - if err != nil { - return nil, err - } - err = v.unmarshalReader(reader, v.kvstore) - return v.kvstore, err -} - -// Retrieve the first found remote configuration. -func (v *Viper) watchKeyValueConfigOnChannel() error { - for _, rp := range v.remoteProviders { - respc, _ := RemoteConfig.WatchChannel(rp) - //Todo: Add quit channel - go func(rc <-chan *RemoteResponse) { - for { - b := <-rc - reader := bytes.NewReader(b.Value) - v.unmarshalReader(reader, v.kvstore) - } - }(respc) - return nil - } - return RemoteConfigError("No Files Found") -} - -// Retrieve the first found remote configuration. -func (v *Viper) watchKeyValueConfig() error { - for _, rp := range v.remoteProviders { - val, err := v.watchRemoteConfig(rp) - if err != nil { - continue - } - v.kvstore = val - return nil - } - return RemoteConfigError("No Files Found") -} - -func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { - reader, err := RemoteConfig.Watch(provider) - if err != nil { - return nil, err - } - err = v.unmarshalReader(reader, v.kvstore) - return v.kvstore, err -} - // AllKeys returns all keys holding a value, regardless of where they are set. // Nested keys are returned with a v.keyDelim (= ".") separator func AllKeys() []string { return v.AllKeys() } diff --git a/viper_test.go b/viper_test.go index c8fa1f4..2b2f431 100644 --- a/viper_test.go +++ b/viper_test.go @@ -12,58 +12,25 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path" "reflect" - "runtime" "sort" "strings" - "sync" "testing" "time" - "github.com/fsnotify/fsnotify" "github.com/mitchellh/mapstructure" "github.com/spf13/afero" "github.com/spf13/cast" "github.com/spf13/pflag" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -var yamlExample = []byte(`Hacker: true -name: steve -hobbies: -- skateboarding -- snowboarding -- go -clothing: - jacket: leather - trousers: denim - pants: - size: large -age: 35 -eyes : brown -beard: true -`) - -var yamlExampleWithExtras = []byte(`Existing: true -Bogus: true -`) - type testUnmarshalExtra struct { Existing bool } -var tomlExample = []byte(` -title = "TOML Example" - -[owner] -organization = "MongoDB" -Bio = "MongoDB Chief Developer Advocate & Hacker at Large" -dob = 1979-05-27T07:32:00Z # First class dates? Why not?`) - var jsonExample = []byte(`{ "id": "0001", "type": "donut", @@ -79,66 +46,13 @@ var jsonExample = []byte(`{ } }`) -var hclExample = []byte(` -id = "0001" -type = "donut" -name = "Cake" -ppu = 0.55 -foos { - foo { - key = 1 - } - foo { - key = 2 - } - foo { - key = 3 - } - foo { - key = 4 - } -}`) - -var propertiesExample = []byte(` -p_id: 0001 -p_type: donut -p_name: Cake -p_ppu: 0.55 -p_batters.batter.type: Regular -`) - -var remoteExample = []byte(`{ -"id":"0002", -"type":"cronut", -"newkey":"remote" -}`) - func initConfigs() { Reset() var r io.Reader - SetConfigType("yaml") - r = bytes.NewReader(yamlExample) - unmarshalReader(r, v.config) SetConfigType("json") r = bytes.NewReader(jsonExample) unmarshalReader(r, v.config) - - SetConfigType("hcl") - r = bytes.NewReader(hclExample) - unmarshalReader(r, v.config) - - SetConfigType("properties") - r = bytes.NewReader(propertiesExample) - unmarshalReader(r, v.config) - - SetConfigType("toml") - r = bytes.NewReader(tomlExample) - unmarshalReader(r, v.config) - - SetConfigType("json") - remote := bytes.NewReader(remoteExample) - unmarshalReader(remote, v.kvstore) } func initConfig(typ, config string) { @@ -151,10 +65,6 @@ func initConfig(typ, config string) { } } -func initYAML() { - initConfig("yaml", string(yamlExample)) -} - func initJSON() { Reset() SetConfigType("json") @@ -163,30 +73,6 @@ func initJSON() { unmarshalReader(r, v.config) } -func initProperties() { - Reset() - SetConfigType("properties") - r := bytes.NewReader(propertiesExample) - - unmarshalReader(r, v.config) -} - -func initTOML() { - Reset() - SetConfigType("toml") - r := bytes.NewReader(tomlExample) - - unmarshalReader(r, v.config) -} - -func initHcl() { - Reset() - SetConfigType("hcl") - r := bytes.NewReader(hclExample) - - unmarshalReader(r, v.config) -} - // make directories for testing func initDirs(t *testing.T) (string, string, func()) { @@ -249,52 +135,6 @@ func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } -func TestBasics(t *testing.T) { - SetConfigFile("/tmp/config.yaml") - filename, err := v.getConfigFile() - assert.Equal(t, "/tmp/config.yaml", filename) - assert.NoError(t, err) -} - -func TestDefault(t *testing.T) { - SetDefault("age", 45) - assert.Equal(t, 45, Get("age")) - - SetDefault("clothing.jacket", "slacks") - assert.Equal(t, "slacks", Get("clothing.jacket")) - - SetConfigType("yaml") - err := ReadConfig(bytes.NewBuffer(yamlExample)) - - assert.NoError(t, err) - assert.Equal(t, "leather", Get("clothing.jacket")) -} - -func TestUnmarshaling(t *testing.T) { - SetConfigType("yaml") - r := bytes.NewReader(yamlExample) - - unmarshalReader(r, v.config) - assert.True(t, InConfig("name")) - assert.False(t, InConfig("state")) - assert.Equal(t, "steve", Get("name")) - assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies")) - assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing")) - assert.Equal(t, 35, Get("age")) -} - -func TestUnmarshalExact(t *testing.T) { - vip := New() - target := &testUnmarshalExtra{} - vip.SetConfigType("yaml") - r := bytes.NewReader(yamlExampleWithExtras) - vip.ReadConfig(r) - err := vip.UnmarshalExact(target) - if err == nil { - t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields") - } -} - func TestOverrides(t *testing.T) { Set("age", 40) assert.Equal(t, 40, Get("age")) @@ -322,52 +162,11 @@ func TestAliasInConfigFile(t *testing.T) { assert.Equal(t, false, Get("beard")) } -func TestYML(t *testing.T) { - initYAML() - assert.Equal(t, "steve", Get("name")) -} - func TestJSON(t *testing.T) { initJSON() assert.Equal(t, "0001", Get("id")) } -func TestProperties(t *testing.T) { - initProperties() - assert.Equal(t, "0001", Get("p_id")) -} - -func TestTOML(t *testing.T) { - initTOML() - assert.Equal(t, "TOML Example", Get("title")) -} - -func TestHCL(t *testing.T) { - initHcl() - assert.Equal(t, "0001", Get("id")) - assert.Equal(t, 0.55, Get("ppu")) - assert.Equal(t, "donut", Get("type")) - assert.Equal(t, "Cake", Get("name")) - Set("id", "0002") - assert.Equal(t, "0002", Get("id")) - assert.NotEqual(t, "cronut", Get("type")) -} - -func TestRemotePrecedence(t *testing.T) { - initJSON() - - remote := bytes.NewReader(remoteExample) - assert.Equal(t, "0001", Get("id")) - unmarshalReader(remote, v.kvstore) - assert.Equal(t, "0001", Get("id")) - assert.NotEqual(t, "cronut", Get("type")) - assert.Equal(t, "remote", Get("newkey")) - Set("newkey", "newvalue") - assert.NotEqual(t, "remote", Get("newkey")) - assert.Equal(t, "newvalue", Get("newkey")) - Set("newkey", "remote") -} - func TestEnv(t *testing.T) { initJSON() @@ -786,31 +585,6 @@ func TestFindsNestedKeys(t *testing.T) { } -func TestReadBufConfig(t *testing.T) { - v := New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(yamlExample)) - t.Log(v.AllKeys()) - - assert.True(t, v.InConfig("name")) - assert.False(t, v.InConfig("state")) - assert.Equal(t, "steve", v.Get("name")) - assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies")) - assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing")) - assert.Equal(t, 35, v.Get("age")) -} - -func TestIsSet(t *testing.T) { - v := New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(yamlExample)) - assert.True(t, v.IsSet("clothing.jacket")) - assert.False(t, v.IsSet("clothing.jackets")) - assert.False(t, v.IsSet("helloworld")) - v.Set("helloworld", "fubar") - assert.True(t, v.IsSet("helloworld")) -} - func TestDirsSearch(t *testing.T) { root, config, cleanup := initDirs(t) @@ -873,70 +647,6 @@ func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { assert.Equal(t, `default`, v.GetString(`key`)) } -func TestSub(t *testing.T) { - v := New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(yamlExample)) - - subv := v.Sub("clothing") - assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) - - subv = v.Sub("clothing.pants") - assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) - - subv = v.Sub("clothing.pants.size") - assert.Equal(t, (*Viper)(nil), subv) - - subv = v.Sub("missing.key") - assert.Equal(t, (*Viper)(nil), subv) -} - -var hclWriteExpected = []byte(`"foos" = { - "foo" = { - "key" = 1 - } - - "foo" = { - "key" = 2 - } - - "foo" = { - "key" = 3 - } - - "foo" = { - "key" = 4 - } -} - -"id" = "0001" - -"name" = "Cake" - -"ppu" = 0.55 - -"type" = "donut"`) - -func TestWriteConfigHCL(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("hcl") - err := v.ReadConfig(bytes.NewBuffer(hclExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.hcl"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.hcl") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, hclWriteExpected, read) -} - var jsonWriteExpected = []byte(`{ "batters": { "batter": [ @@ -987,211 +697,6 @@ p_ppu = 0.55 p_batters.batter.type = Regular `) -func TestWriteConfigProperties(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("properties") - err := v.ReadConfig(bytes.NewBuffer(propertiesExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.properties"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.properties") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, propertiesWriteExpected, read) -} - -func TestWriteConfigTOML(t *testing.T) { - fs := afero.NewMemMapFs() - v := New() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("toml") - err := v.ReadConfig(bytes.NewBuffer(tomlExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.toml"); err != nil { - t.Fatal(err) - } - - // The TOML String method does not order the contents. - // Therefore, we must read the generated file and compare the data. - v2 := New() - v2.SetFs(fs) - v2.SetConfigName("c") - v2.SetConfigType("toml") - v2.SetConfigFile("c.toml") - err = v2.ReadInConfig() - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, v.GetString("title"), v2.GetString("title")) - assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio")) - assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob")) - assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization")) -} - -var yamlWriteExpected = []byte(`age: 35 -beard: true -clothing: - jacket: leather - pants: - size: large - trousers: denim -eyes: brown -hacker: true -hobbies: -- skateboarding -- snowboarding -- go -name: steve -`) - -func TestWriteConfigYAML(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("yaml") - err := v.ReadConfig(bytes.NewBuffer(yamlExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.yaml"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.yaml") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, yamlWriteExpected, read) -} - -var yamlMergeExampleTgt = []byte(` -hello: - pop: 37890 - lagrenum: 765432101234567 - world: - - us - - uk - - fr - - de -`) - -var yamlMergeExampleSrc = []byte(` -hello: - pop: 45000 - lagrenum: 7654321001234567 - universe: - - mw - - ad -fu: bar -`) - -func TestMergeConfig(t *testing.T) { - v := New() - v.SetConfigType("yml") - if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { - t.Fatal(err) - } - - if pop := v.GetInt("hello.pop"); pop != 37890 { - t.Fatalf("pop != 37890, = %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) { - t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) - } - - if world := v.GetStringSlice("hello.world"); len(world) != 4 { - t.Fatalf("len(world) != 4, = %d", len(world)) - } - - if fu := v.GetString("fu"); fu != "" { - t.Fatalf("fu != \"\", = %s", fu) - } - - if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { - t.Fatal(err) - } - - if pop := v.GetInt("hello.pop"); pop != 45000 { - t.Fatalf("pop != 45000, = %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) { - t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) - } - - if world := v.GetStringSlice("hello.world"); len(world) != 4 { - t.Fatalf("len(world) != 4, = %d", len(world)) - } - - if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { - t.Fatalf("len(universe) != 2, = %d", len(universe)) - } - - if fu := v.GetString("fu"); fu != "bar" { - t.Fatalf("fu != \"bar\", = %s", fu) - } -} - -func TestMergeConfigNoMerge(t *testing.T) { - v := New() - v.SetConfigType("yml") - if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { - t.Fatal(err) - } - - if pop := v.GetInt("hello.pop"); pop != 37890 { - t.Fatalf("pop != 37890, = %d", pop) - } - - if world := v.GetStringSlice("hello.world"); len(world) != 4 { - t.Fatalf("len(world) != 4, = %d", len(world)) - } - - if fu := v.GetString("fu"); fu != "" { - t.Fatalf("fu != \"\", = %s", fu) - } - - if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { - t.Fatal(err) - } - - if pop := v.GetInt("hello.pop"); pop != 45000 { - t.Fatalf("pop != 45000, = %d", pop) - } - - if world := v.GetStringSlice("hello.world"); len(world) != 0 { - t.Fatalf("len(world) != 0, = %d", len(world)) - } - - if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { - t.Fatalf("len(universe) != 2, = %d", len(universe)) - } - - if fu := v.GetString("fu"); fu != "bar" { - t.Fatalf("fu != \"bar\", = %s", fu) - } -} - func TestUnmarshalingWithAliases(t *testing.T) { v := New() v.SetDefault("ID", 1) @@ -1217,16 +722,6 @@ func TestUnmarshalingWithAliases(t *testing.T) { assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) } -func TestSetConfigNameClearsFileCache(t *testing.T) { - SetConfigFile("/tmp/config.yaml") - SetConfigName("default") - f, err := v.getConfigFile() - if err == nil { - t.Fatalf("config file cache should have been cleared") - } - assert.Empty(t, f) -} - func TestShadowedNestedValue(t *testing.T) { config := `name: steve @@ -1264,51 +759,6 @@ func TestDotParameter(t *testing.T) { assert.Equal(t, expected, actual) } -func TestCaseInsensitive(t *testing.T) { - for _, config := range []struct { - typ string - content string - }{ - {"yaml", ` -aBcD: 1 -eF: - gH: 2 - iJk: 3 - Lm: - nO: 4 - P: - Q: 5 - R: 6 -`}, - {"json", `{ - "aBcD": 1, - "eF": { - "iJk": 3, - "Lm": { - "P": { - "Q": 5, - "R": 6 - }, - "nO": 4 - }, - "gH": 2 - } -}`}, - {"toml", `aBcD = 1 -[eF] -gH = 2 -iJk = 3 -[eF.Lm] -nO = 4 -[eF.Lm.P] -Q = 5 -R = 6 -`}, - } { - doTestCaseInsensitive(t, config.typ, config.content) - } -} - func TestCaseInsensitiveSet(t *testing.T) { Reset() m1 := map[string]interface{}{ @@ -1368,35 +818,6 @@ func TestCaseInsensitiveSet(t *testing.T) { } } -func TestParseNested(t *testing.T) { - type duration struct { - Delay time.Duration - } - - type item struct { - Name string - Delay time.Duration - Nested duration - } - - config := `[[parent]] - delay="100ms" - [parent.nested] - delay="200ms" -` - initConfig("toml", config) - - var items []item - err := v.UnmarshalKey("parent", &items) - if err != nil { - t.Fatalf("unable to decode into struct, %v", err) - } - - assert.Equal(t, 1, len(items)) - assert.Equal(t, 100*time.Millisecond, items[0].Delay) - assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) -} - func doTestCaseInsensitive(t *testing.T, typ, config string) { initConfig(typ, config) Set("RfD", true) @@ -1411,107 +832,6 @@ func doTestCaseInsensitive(t *testing.T, typ, config string) { } -func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) { - watchDir, err := ioutil.TempDir("", "") - require.Nil(t, err) - configFile := path.Join(watchDir, "config.yaml") - err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640) - require.Nil(t, err) - cleanup := func() { - os.RemoveAll(watchDir) - } - v := New() - v.SetConfigFile(configFile) - err = v.ReadInConfig() - require.Nil(t, err) - require.Equal(t, "bar", v.Get("foo")) - return v, configFile, cleanup -} - -func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) { - watchDir, err := ioutil.TempDir("", "") - require.Nil(t, err) - dataDir1 := path.Join(watchDir, "data1") - err = os.Mkdir(dataDir1, 0777) - require.Nil(t, err) - realConfigFile := path.Join(dataDir1, "config.yaml") - t.Logf("Real config file location: %s\n", realConfigFile) - err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640) - require.Nil(t, err) - cleanup := func() { - os.RemoveAll(watchDir) - } - // now, symlink the tm `data1` dir to `data` in the baseDir - os.Symlink(dataDir1, path.Join(watchDir, "data")) - // and link the `/datadir1/config.yaml` to `/config.yaml` - configFile := path.Join(watchDir, "config.yaml") - os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile) - t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml")) - // init Viper - v := New() - v.SetConfigFile(configFile) - err = v.ReadInConfig() - require.Nil(t, err) - require.Equal(t, "bar", v.Get("foo")) - return v, watchDir, configFile, cleanup -} - -func TestWatchFile(t *testing.T) { - - t.Run("file content changed", func(t *testing.T) { - // given a `config.yaml` file being watched - v, configFile, cleanup := newViperWithConfigFile(t) - defer cleanup() - _, err := os.Stat(configFile) - require.NoError(t, err) - t.Logf("test config file: %s\n", configFile) - wg := sync.WaitGroup{} - wg.Add(1) - v.OnConfigChange(func(in fsnotify.Event) { - t.Logf("config file changed") - wg.Done() - }) - v.WatchConfig() - // when overwriting the file and waiting for the custom change notification handler to be triggered - err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640) - wg.Wait() - // then the config value should have changed - require.Nil(t, err) - assert.Equal(t, "baz", v.Get("foo")) - }) - - t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) { - // skip if not executed on Linux - if runtime.GOOS != "linux" { - t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...") - } - v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t) - // defer cleanup() - wg := sync.WaitGroup{} - v.WatchConfig() - v.OnConfigChange(func(in fsnotify.Event) { - t.Logf("config file changed") - wg.Done() - }) - wg.Add(1) - // when link to another `config.yaml` file - dataDir2 := path.Join(watchDir, "data2") - err := os.Mkdir(dataDir2, 0777) - require.Nil(t, err) - configFile2 := path.Join(dataDir2, "config.yaml") - err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640) - require.Nil(t, err) - // change the symlink using the `ln -sfn` command - err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run() - require.Nil(t, err) - wg.Wait() - // then - require.Nil(t, err) - assert.Equal(t, "baz", v.Get("foo")) - }) - -} - func BenchmarkGetBool(b *testing.B) { key := "BenchmarkGetBool" v = New()