From 1b560de7b7aa35596ac371460e52b5e8dfc043c6 Mon Sep 17 00:00:00 2001 From: satotake Date: Mon, 3 Apr 2017 02:11:29 +0900 Subject: [PATCH 1/3] add ChainMergeConfigFiles --- viper.go | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/viper.go b/viper.go index 22a2ed8..96b16f7 100644 --- a/viper.go +++ b/viper.go @@ -52,7 +52,7 @@ func init() { type remoteConfigFactory interface { Get(rp RemoteProvider) (io.Reader, error) Watch(rp RemoteProvider) (io.Reader, error) - WatchChannel(rp RemoteProvider)(<-chan *RemoteResponse, chan bool) + WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) } // RemoteConfig is optional, see the remote package @@ -145,10 +145,11 @@ type Viper struct { remoteProviders []*defaultRemoteProvider // Name of file to look for inside the path - configName string - configFile string - configType string - envPrefix string + configName string + configFile string + configFiles []string + configType string + envPrefix string automaticEnvApplied bool envKeyReplacer *strings.Replacer @@ -291,6 +292,15 @@ func (v *Viper) SetConfigFile(in string) { } } +// SetConfigFiles explicitly defines slice of the path, name and extension of the config files +// Viper will use these and not check any of the config paths +func SetConfigFiles(in []string) { v.SetConfigFiles(in) } +func (v *Viper) SetConfigFiles(in []string) { + if len(in) > 0 { + v.configFiles = in + } +} + // SetEnvPrefix defines a prefix that ENVIRONMENT variables will use. // E.g. if your prefix is "spf", the env registry // will look for env. variables that start with "SPF_" @@ -1131,8 +1141,29 @@ func (v *Viper) MergeInConfig() error { return v.MergeConfig(bytes.NewReader(file)) } -// ReadConfig will read a configuration file, setting existing keys to nil if the -// key does not exist in the file. +// ChainMergeConfigfiles repeatedly merge with configFiles +func ChainMergeConfigFiles() error { return v.ChainMergeConfigFiles() } +func (v *Viper) ChainMergeConfigFiles() error { + jww.INFO.Println("Attempting to chain merge with configFiles") + + for i, filename := range v.configFiles { + if i == 0 { + v.SetConfigFile(filename) + err := v.ReadInConfig() + if err != nil { + return err + } + } else { + v.SetConfigFile(filename) + err := v.MergeInConfig() + if err != nil { + return err + } + } + } + return nil +} + func ReadConfig(in io.Reader) error { return v.ReadConfig(in) } func (v *Viper) ReadConfig(in io.Reader) error { v.config = make(map[string]interface{}) From 33923af7462bc796168dbefbcd9dbcfe55788492 Mon Sep 17 00:00:00 2001 From: satotake Date: Tue, 4 Apr 2017 01:41:05 +0900 Subject: [PATCH 2/3] write test --- viper.go | 23 +++++++++++- viper_test.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/viper.go b/viper.go index 96b16f7..7c83663 100644 --- a/viper.go +++ b/viper.go @@ -1146,7 +1146,12 @@ func ChainMergeConfigFiles() error { return v.ChainMergeConfigFiles() } func (v *Viper) ChainMergeConfigFiles() error { jww.INFO.Println("Attempting to chain merge with configFiles") - for i, filename := range v.configFiles { + configFiles, err := v.getConfigFiles() + if err != nil { + return err + } + + for i, filename := range configFiles { if i == 0 { v.SetConfigFile(filename) err := v.ReadInConfig() @@ -1539,6 +1544,22 @@ func (v *Viper) getConfigFile() (string, error) { return v.getConfigFile() } +func (v *Viper) getConfigFiles() ([]string, error) { + // if explicitly set, then use it + // else search config file and return as slice + if len(v.configFiles) > 0 { + return v.configFiles, nil + } + + cf, err := v.findConfigFile() + if err != nil { + return nil, err + } + + v.configFiles = []string{cf} + return v.getConfigFiles() +} + func (v *Viper) searchInPath(in string) (filename string) { jww.DEBUG.Println("Searching for config in ", in) for _, ext := range SupportedExts { diff --git a/viper_test.go b/viper_test.go index cd7b65c..3ded47d 100644 --- a/viper_test.go +++ b/viper_test.go @@ -1152,3 +1152,104 @@ func BenchmarkGetBoolFromMap(b *testing.B) { } } } + +func initConfigsForMerge(t *testing.T) (string, string, func()) { + + var ( + testDirs = []string{`a`, `b`, `c`} + configName = `config.toml` + ) + + root, err := ioutil.TempDir("", "") + + cleanup := true + defer func() { + if cleanup { + os.Chdir("..") + os.RemoveAll(root) + } + }() + + assert.Nil(t, err) + + err = os.Chdir(root) + assert.Nil(t, err) + + for _, dir := range testDirs { + err = os.Mkdir(dir, 0750) + assert.Nil(t, err) + + err = ioutil.WriteFile( + path.Join(dir, configName), + []byte("key = \"value is "+dir+"\"\n"+dir+" = \""+dir+"\"\n"), + 0640) + assert.Nil(t, err) + } + + cleanup = false + return root, configName, func() { + os.Chdir("..") + os.RemoveAll(root) + } +} + +func TestChainMergeConfig(t *testing.T) { + + root, config, cleanup := initConfigsForMerge(t) + defer cleanup() + + v := New() + v.SetDefault(`key`, `default`) + + entries, err := ioutil.ReadDir(root) + + var configfiles []string + for i, e := range entries { + if e.IsDir() { + configfile := path.Join(root, e.Name(), config) + configfiles = append(configfiles, configfile) + + v.SetConfigFiles(configfiles[:i+1]) + err = v.ChainMergeConfigFiles() + assert.Nil(t, err) + assert.Equal(t, `value is `+e.Name(), v.GetString(`key`)) + assert.Equal(t, `a`, v.GetString(`a`)) + + if i == 0 { + assert.Equal(t, ``, v.GetString(`b`)) + } else { + assert.Equal(t, `b`, v.GetString(`b`)) + } + + if i < 2 { + assert.Equal(t, ``, v.GetString(`c`)) + } else { + assert.Equal(t, `c`, v.GetString(`c`)) + } + } + } +} + +func TestNotSetConfigFilesChainMerge(t *testing.T) { + // If not set, behavior is identical to ReadInConfig + + root, config, cleanup := initDirs(t) + defer cleanup() + + v := New() + v.SetConfigName(config) + v.SetDefault(`key`, `default`) + + entries, err := ioutil.ReadDir(root) + for _, e := range entries { + if e.IsDir() { + v.AddConfigPath(e.Name()) + } + } + assert.Nil(t, err) + + err = v.ChainMergeConfigFiles() + assert.Nil(t, err) + + assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) +} From fb72938b554bd263ddc8f04557e4e3d87a3d2be3 Mon Sep 17 00:00:00 2001 From: satotake Date: Tue, 4 Apr 2017 01:50:16 +0900 Subject: [PATCH 3/3] rename configFiles > configFileChain --- viper.go | 40 ++++++++++++++++++++-------------------- viper_test.go | 10 +++++----- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/viper.go b/viper.go index 7c83663..0f4df52 100644 --- a/viper.go +++ b/viper.go @@ -145,11 +145,11 @@ type Viper struct { remoteProviders []*defaultRemoteProvider // Name of file to look for inside the path - configName string - configFile string - configFiles []string - configType string - envPrefix string + configName string + configFile string + configFileChain []string + configType string + envPrefix string automaticEnvApplied bool envKeyReplacer *strings.Replacer @@ -292,12 +292,12 @@ func (v *Viper) SetConfigFile(in string) { } } -// SetConfigFiles explicitly defines slice of the path, name and extension of the config files +// SetConfigFileChain explicitly defines slice of the path, name and extension of the config files // Viper will use these and not check any of the config paths -func SetConfigFiles(in []string) { v.SetConfigFiles(in) } -func (v *Viper) SetConfigFiles(in []string) { +func SetConfigChain(in []string) { v.SetConfigFileChain(in) } +func (v *Viper) SetConfigFileChain(in []string) { if len(in) > 0 { - v.configFiles = in + v.configFileChain = in } } @@ -1141,17 +1141,17 @@ func (v *Viper) MergeInConfig() error { return v.MergeConfig(bytes.NewReader(file)) } -// ChainMergeConfigfiles repeatedly merge with configFiles -func ChainMergeConfigFiles() error { return v.ChainMergeConfigFiles() } -func (v *Viper) ChainMergeConfigFiles() error { - jww.INFO.Println("Attempting to chain merge with configFiles") +// MergeConfigFileChain repeatedly merge with configFileChain +func MergeConfigFileChain() error { return v.MergeConfigFileChain() } +func (v *Viper) MergeConfigFileChain() error { + jww.INFO.Println("Attempting to chain merge with configFileChain") - configFiles, err := v.getConfigFiles() + configFileChain, err := v.getConfigFileChain() if err != nil { return err } - for i, filename := range configFiles { + for i, filename := range configFileChain { if i == 0 { v.SetConfigFile(filename) err := v.ReadInConfig() @@ -1544,11 +1544,11 @@ func (v *Viper) getConfigFile() (string, error) { return v.getConfigFile() } -func (v *Viper) getConfigFiles() ([]string, error) { +func (v *Viper) getConfigFileChain() ([]string, error) { // if explicitly set, then use it // else search config file and return as slice - if len(v.configFiles) > 0 { - return v.configFiles, nil + if len(v.configFileChain) > 0 { + return v.configFileChain, nil } cf, err := v.findConfigFile() @@ -1556,8 +1556,8 @@ func (v *Viper) getConfigFiles() ([]string, error) { return nil, err } - v.configFiles = []string{cf} - return v.getConfigFiles() + v.configFileChain = []string{cf} + return v.getConfigFileChain() } func (v *Viper) searchInPath(in string) (filename string) { diff --git a/viper_test.go b/viper_test.go index 3ded47d..5d3d5c4 100644 --- a/viper_test.go +++ b/viper_test.go @@ -1193,7 +1193,7 @@ func initConfigsForMerge(t *testing.T) (string, string, func()) { } } -func TestChainMergeConfig(t *testing.T) { +func TestMergeConfigChain(t *testing.T) { root, config, cleanup := initConfigsForMerge(t) defer cleanup() @@ -1209,8 +1209,8 @@ func TestChainMergeConfig(t *testing.T) { configfile := path.Join(root, e.Name(), config) configfiles = append(configfiles, configfile) - v.SetConfigFiles(configfiles[:i+1]) - err = v.ChainMergeConfigFiles() + v.SetConfigFileChain(configfiles[:i+1]) + err = v.MergeConfigFileChain() assert.Nil(t, err) assert.Equal(t, `value is `+e.Name(), v.GetString(`key`)) assert.Equal(t, `a`, v.GetString(`a`)) @@ -1230,7 +1230,7 @@ func TestChainMergeConfig(t *testing.T) { } } -func TestNotSetConfigFilesChainMerge(t *testing.T) { +func TestNotSetConfigFileChainMerge(t *testing.T) { // If not set, behavior is identical to ReadInConfig root, config, cleanup := initDirs(t) @@ -1248,7 +1248,7 @@ func TestNotSetConfigFilesChainMerge(t *testing.T) { } assert.Nil(t, err) - err = v.ChainMergeConfigFiles() + err = v.MergeConfigFileChain() assert.Nil(t, err) assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))