1 // Copyright © 2014 Steve Francia <spf@spf13.com>. |
1 // Copyright © 2014 Steve Francia <spf@spf13.com>. |
2 // |
2 // |
3 // Use of this source code is governed by an MIT-style |
3 // Use of this source code is governed by an MIT-style |
4 // license that can be found in the LICENSE file. |
4 // license that can be found in the LICENSE file. |
5 |
5 |
6 // Viper is a application configuration system. |
6 // Viper is an application configuration system. |
7 // It believes that applications can be configured a variety of ways |
7 // It believes that applications can be configured a variety of ways |
8 // via flags, ENVIRONMENT variables, configuration files retrieved |
8 // via flags, ENVIRONMENT variables, configuration files retrieved |
9 // from the file system, or a remote key/value store. |
9 // from the file system, or a remote key/value store. |
10 |
10 |
11 // Each item takes precedence over the item below it: |
11 // Each item takes precedence over the item below it: |
21 |
21 |
22 import ( |
22 import ( |
23 "bytes" |
23 "bytes" |
24 "encoding/csv" |
24 "encoding/csv" |
25 "encoding/json" |
25 "encoding/json" |
|
26 "errors" |
26 "fmt" |
27 "fmt" |
27 "io" |
28 "io" |
28 "log" |
29 "log" |
29 "os" |
30 "os" |
30 "path/filepath" |
31 "path/filepath" |
31 "reflect" |
32 "reflect" |
32 "strings" |
33 "strings" |
|
34 "sync" |
33 "time" |
35 "time" |
34 |
|
35 yaml "gopkg.in/yaml.v2" |
|
36 |
36 |
37 "github.com/fsnotify/fsnotify" |
37 "github.com/fsnotify/fsnotify" |
38 "github.com/hashicorp/hcl" |
38 "github.com/hashicorp/hcl" |
39 "github.com/hashicorp/hcl/hcl/printer" |
39 "github.com/hashicorp/hcl/hcl/printer" |
40 "github.com/magiconair/properties" |
40 "github.com/magiconair/properties" |
41 "github.com/mitchellh/mapstructure" |
41 "github.com/mitchellh/mapstructure" |
42 toml "github.com/pelletier/go-toml" |
42 "github.com/pelletier/go-toml" |
43 "github.com/spf13/afero" |
43 "github.com/spf13/afero" |
44 "github.com/spf13/cast" |
44 "github.com/spf13/cast" |
45 jww "github.com/spf13/jwalterweatherman" |
45 jww "github.com/spf13/jwalterweatherman" |
46 "github.com/spf13/pflag" |
46 "github.com/spf13/pflag" |
|
47 "github.com/subosito/gotenv" |
|
48 "gopkg.in/ini.v1" |
|
49 "gopkg.in/yaml.v2" |
47 ) |
50 ) |
48 |
51 |
49 // ConfigMarshalError happens when failing to marshal the configuration. |
52 // ConfigMarshalError happens when failing to marshal the configuration. |
50 type ConfigMarshalError struct { |
53 type ConfigMarshalError struct { |
51 err error |
54 err error |
109 } |
112 } |
110 |
113 |
111 // Error returns the formatted configuration error. |
114 // Error returns the formatted configuration error. |
112 func (fnfe ConfigFileNotFoundError) Error() string { |
115 func (fnfe ConfigFileNotFoundError) Error() string { |
113 return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) |
116 return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) |
|
117 } |
|
118 |
|
119 // ConfigFileAlreadyExistsError denotes failure to write new configuration file. |
|
120 type ConfigFileAlreadyExistsError string |
|
121 |
|
122 // Error returns the formatted error when configuration already exists. |
|
123 func (faee ConfigFileAlreadyExistsError) Error() string { |
|
124 return fmt.Sprintf("Config File %q Already Exists", string(faee)) |
114 } |
125 } |
115 |
126 |
116 // A DecoderConfigOption can be passed to viper.Unmarshal to configure |
127 // A DecoderConfigOption can be passed to viper.Unmarshal to configure |
117 // mapstructure.DecoderConfig options |
128 // mapstructure.DecoderConfig options |
118 type DecoderConfigOption func(*mapstructure.DecoderConfig) |
129 type DecoderConfigOption func(*mapstructure.DecoderConfig) |
219 v.typeByDefValue = false |
233 v.typeByDefValue = false |
220 |
234 |
221 return v |
235 return v |
222 } |
236 } |
223 |
237 |
224 // Intended for testing, will reset all to default settings. |
238 // Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney. |
|
239 // If you're unfamiliar with this style, |
|
240 // see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and |
|
241 // https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis. |
|
242 type Option interface { |
|
243 apply(v *Viper) |
|
244 } |
|
245 |
|
246 type optionFunc func(v *Viper) |
|
247 |
|
248 func (fn optionFunc) apply(v *Viper) { |
|
249 fn(v) |
|
250 } |
|
251 |
|
252 // KeyDelimiter sets the delimiter used for determining key parts. |
|
253 // By default it's value is ".". |
|
254 func KeyDelimiter(d string) Option { |
|
255 return optionFunc(func(v *Viper) { |
|
256 v.keyDelim = d |
|
257 }) |
|
258 } |
|
259 |
|
260 // StringReplacer applies a set of replacements to a string. |
|
261 type StringReplacer interface { |
|
262 // Replace returns a copy of s with all replacements performed. |
|
263 Replace(s string) string |
|
264 } |
|
265 |
|
266 // EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys. |
|
267 func EnvKeyReplacer(r StringReplacer) Option { |
|
268 return optionFunc(func(v *Viper) { |
|
269 v.envKeyReplacer = r |
|
270 }) |
|
271 } |
|
272 |
|
273 // NewWithOptions creates a new Viper instance. |
|
274 func NewWithOptions(opts ...Option) *Viper { |
|
275 v := New() |
|
276 |
|
277 for _, opt := range opts { |
|
278 opt.apply(v) |
|
279 } |
|
280 |
|
281 return v |
|
282 } |
|
283 |
|
284 // Reset is intended for testing, will reset all to default settings. |
225 // In the public interface for the viper package so applications |
285 // In the public interface for the viper package so applications |
226 // can use it in their testing as well. |
286 // can use it in their testing as well. |
227 func Reset() { |
287 func Reset() { |
228 v = New() |
288 v = New() |
229 SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} |
289 SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} |
230 SupportedRemoteProviders = []string{"etcd", "consul"} |
290 SupportedRemoteProviders = []string{"etcd", "consul"} |
231 } |
291 } |
232 |
292 |
233 type defaultRemoteProvider struct { |
293 type defaultRemoteProvider struct { |
234 provider string |
294 provider string |
263 Path() string |
323 Path() string |
264 SecretKeyring() string |
324 SecretKeyring() string |
265 } |
325 } |
266 |
326 |
267 // SupportedExts are universally supported extensions. |
327 // SupportedExts are universally supported extensions. |
268 var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} |
328 var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} |
269 |
329 |
270 // SupportedRemoteProviders are universally supported remote providers. |
330 // SupportedRemoteProviders are universally supported remote providers. |
271 var SupportedRemoteProviders = []string{"etcd", "consul"} |
331 var SupportedRemoteProviders = []string{"etcd", "consul"} |
272 |
332 |
273 func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } |
333 func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } |
274 func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { |
334 func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { |
275 v.onConfigChange = run |
335 v.onConfigChange = run |
276 } |
336 } |
277 |
337 |
278 func WatchConfig() { v.WatchConfig() } |
338 func WatchConfig() { v.WatchConfig() } |
|
339 |
279 func (v *Viper) WatchConfig() { |
340 func (v *Viper) WatchConfig() { |
|
341 initWG := sync.WaitGroup{} |
|
342 initWG.Add(1) |
280 go func() { |
343 go func() { |
281 watcher, err := fsnotify.NewWatcher() |
344 watcher, err := fsnotify.NewWatcher() |
282 if err != nil { |
345 if err != nil { |
283 log.Fatal(err) |
346 log.Fatal(err) |
284 } |
347 } |
285 defer watcher.Close() |
348 defer watcher.Close() |
286 |
|
287 // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way |
349 // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way |
288 filename, err := v.getConfigFile() |
350 filename, err := v.getConfigFile() |
289 if err != nil { |
351 if err != nil { |
290 log.Println("error:", err) |
352 log.Printf("error: %v\n", err) |
|
353 initWG.Done() |
291 return |
354 return |
292 } |
355 } |
293 |
356 |
294 configFile := filepath.Clean(filename) |
357 configFile := filepath.Clean(filename) |
295 configDir, _ := filepath.Split(configFile) |
358 configDir, _ := filepath.Split(configFile) |
296 |
359 realConfigFile, _ := filepath.EvalSymlinks(filename) |
297 done := make(chan bool) |
360 |
|
361 eventsWG := sync.WaitGroup{} |
|
362 eventsWG.Add(1) |
298 go func() { |
363 go func() { |
299 for { |
364 for { |
300 select { |
365 select { |
301 case event := <-watcher.Events: |
366 case event, ok := <-watcher.Events: |
302 // we only care about the config file |
367 if !ok { // 'Events' channel is closed |
303 if filepath.Clean(event.Name) == configFile { |
368 eventsWG.Done() |
304 if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { |
369 return |
305 err := v.ReadInConfig() |
370 } |
306 if err != nil { |
371 currentConfigFile, _ := filepath.EvalSymlinks(filename) |
307 log.Println("error:", err) |
372 // we only care about the config file with the following cases: |
308 } |
373 // 1 - if the config file was modified or created |
309 if v.onConfigChange != nil { |
374 // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) |
310 v.onConfigChange(event) |
375 const writeOrCreateMask = fsnotify.Write | fsnotify.Create |
311 } |
376 if (filepath.Clean(event.Name) == configFile && |
|
377 event.Op&writeOrCreateMask != 0) || |
|
378 (currentConfigFile != "" && currentConfigFile != realConfigFile) { |
|
379 realConfigFile = currentConfigFile |
|
380 err := v.ReadInConfig() |
|
381 if err != nil { |
|
382 log.Printf("error reading config file: %v\n", err) |
312 } |
383 } |
|
384 if v.onConfigChange != nil { |
|
385 v.onConfigChange(event) |
|
386 } |
|
387 } else if filepath.Clean(event.Name) == configFile && |
|
388 event.Op&fsnotify.Remove&fsnotify.Remove != 0 { |
|
389 eventsWG.Done() |
|
390 return |
313 } |
391 } |
314 case err := <-watcher.Errors: |
392 |
315 log.Println("error:", err) |
393 case err, ok := <-watcher.Errors: |
|
394 if ok { // 'Errors' channel is not closed |
|
395 log.Printf("watcher error: %v\n", err) |
|
396 } |
|
397 eventsWG.Done() |
|
398 return |
316 } |
399 } |
317 } |
400 } |
318 }() |
401 }() |
319 |
|
320 watcher.Add(configDir) |
402 watcher.Add(configDir) |
321 <-done |
403 initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on... |
|
404 eventsWG.Wait() // now, wait for event loop to end in this go-routine... |
322 }() |
405 }() |
|
406 initWG.Wait() // make sure that the go routine above fully ended before returning |
323 } |
407 } |
324 |
408 |
325 // SetConfigFile explicitly defines the path, name and extension of the config file. |
409 // SetConfigFile explicitly defines the path, name and extension of the config file. |
326 // Viper will use this and not check any of the config paths. |
410 // Viper will use this and not check any of the config paths. |
327 func SetConfigFile(in string) { v.SetConfigFile(in) } |
411 func SetConfigFile(in string) { v.SetConfigFile(in) } |
347 } |
431 } |
348 |
432 |
349 return strings.ToUpper(in) |
433 return strings.ToUpper(in) |
350 } |
434 } |
351 |
435 |
|
436 // AllowEmptyEnv tells Viper to consider set, |
|
437 // but empty environment variables as valid values instead of falling back. |
|
438 // For backward compatibility reasons this is false by default. |
|
439 func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) } |
|
440 func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) { |
|
441 v.allowEmptyEnv = allowEmptyEnv |
|
442 } |
|
443 |
352 // TODO: should getEnv logic be moved into find(). Can generalize the use of |
444 // TODO: should getEnv logic be moved into find(). Can generalize the use of |
353 // rewriting keys many things, Ex: Get('someKey') -> some_key |
445 // rewriting keys many things, Ex: Get('someKey') -> some_key |
354 // (camel case to snake case for JSON keys perhaps) |
446 // (camel case to snake case for JSON keys perhaps) |
355 |
447 |
356 // getEnv is a wrapper around os.Getenv which replaces characters in the original |
448 // getEnv is a wrapper around os.Getenv which replaces characters in the original |
357 // key. This allows env vars which have different keys than the config object |
449 // key. This allows env vars which have different keys than the config object |
358 // keys. |
450 // keys. |
359 func (v *Viper) getEnv(key string) string { |
451 func (v *Viper) getEnv(key string) (string, bool) { |
360 if v.envKeyReplacer != nil { |
452 if v.envKeyReplacer != nil { |
361 key = v.envKeyReplacer.Replace(key) |
453 key = v.envKeyReplacer.Replace(key) |
362 } |
454 } |
363 return os.Getenv(key) |
455 |
|
456 val, ok := os.LookupEnv(key) |
|
457 |
|
458 return val, ok && (v.allowEmptyEnv || val != "") |
364 } |
459 } |
365 |
460 |
366 // ConfigFileUsed returns the file used to populate the config registry. |
461 // ConfigFileUsed returns the file used to populate the config registry. |
367 func ConfigFileUsed() string { return v.ConfigFileUsed() } |
462 func ConfigFileUsed() string { return v.ConfigFileUsed() } |
368 func (v *Viper) ConfigFileUsed() string { return v.configFile } |
463 func (v *Viper) ConfigFileUsed() string { return v.configFile } |
650 return cast.ToBool(val) |
744 return cast.ToBool(val) |
651 case string: |
745 case string: |
652 return cast.ToString(val) |
746 return cast.ToString(val) |
653 case int32, int16, int8, int: |
747 case int32, int16, int8, int: |
654 return cast.ToInt(val) |
748 return cast.ToInt(val) |
|
749 case uint: |
|
750 return cast.ToUint(val) |
|
751 case uint32: |
|
752 return cast.ToUint32(val) |
|
753 case uint64: |
|
754 return cast.ToUint64(val) |
655 case int64: |
755 case int64: |
656 return cast.ToInt64(val) |
756 return cast.ToInt64(val) |
657 case float64, float32: |
757 case float64, float32: |
658 return cast.ToFloat64(val) |
758 return cast.ToFloat64(val) |
659 case time.Time: |
759 case time.Time: |
660 return cast.ToTime(val) |
760 return cast.ToTime(val) |
661 case time.Duration: |
761 case time.Duration: |
662 return cast.ToDuration(val) |
762 return cast.ToDuration(val) |
663 case []string: |
763 case []string: |
664 return cast.ToStringSlice(val) |
764 return cast.ToStringSlice(val) |
|
765 case []int: |
|
766 return cast.ToIntSlice(val) |
665 } |
767 } |
666 } |
768 } |
667 |
769 |
668 return val |
770 return val |
669 } |
771 } |
713 func GetInt64(key string) int64 { return v.GetInt64(key) } |
815 func GetInt64(key string) int64 { return v.GetInt64(key) } |
714 func (v *Viper) GetInt64(key string) int64 { |
816 func (v *Viper) GetInt64(key string) int64 { |
715 return cast.ToInt64(v.Get(key)) |
817 return cast.ToInt64(v.Get(key)) |
716 } |
818 } |
717 |
819 |
|
820 // GetUint returns the value associated with the key as an unsigned integer. |
|
821 func GetUint(key string) uint { return v.GetUint(key) } |
|
822 func (v *Viper) GetUint(key string) uint { |
|
823 return cast.ToUint(v.Get(key)) |
|
824 } |
|
825 |
|
826 // GetUint32 returns the value associated with the key as an unsigned integer. |
|
827 func GetUint32(key string) uint32 { return v.GetUint32(key) } |
|
828 func (v *Viper) GetUint32(key string) uint32 { |
|
829 return cast.ToUint32(v.Get(key)) |
|
830 } |
|
831 |
|
832 // GetUint64 returns the value associated with the key as an unsigned integer. |
|
833 func GetUint64(key string) uint64 { return v.GetUint64(key) } |
|
834 func (v *Viper) GetUint64(key string) uint64 { |
|
835 return cast.ToUint64(v.Get(key)) |
|
836 } |
|
837 |
718 // GetFloat64 returns the value associated with the key as a float64. |
838 // GetFloat64 returns the value associated with the key as a float64. |
719 func GetFloat64(key string) float64 { return v.GetFloat64(key) } |
839 func GetFloat64(key string) float64 { return v.GetFloat64(key) } |
720 func (v *Viper) GetFloat64(key string) float64 { |
840 func (v *Viper) GetFloat64(key string) float64 { |
721 return cast.ToFloat64(v.Get(key)) |
841 return cast.ToFloat64(v.Get(key)) |
722 } |
842 } |
729 |
849 |
730 // GetDuration returns the value associated with the key as a duration. |
850 // GetDuration returns the value associated with the key as a duration. |
731 func GetDuration(key string) time.Duration { return v.GetDuration(key) } |
851 func GetDuration(key string) time.Duration { return v.GetDuration(key) } |
732 func (v *Viper) GetDuration(key string) time.Duration { |
852 func (v *Viper) GetDuration(key string) time.Duration { |
733 return cast.ToDuration(v.Get(key)) |
853 return cast.ToDuration(v.Get(key)) |
|
854 } |
|
855 |
|
856 // GetIntSlice returns the value associated with the key as a slice of int values. |
|
857 func GetIntSlice(key string) []int { return v.GetIntSlice(key) } |
|
858 func (v *Viper) GetIntSlice(key string) []int { |
|
859 return cast.ToIntSlice(v.Get(key)) |
734 } |
860 } |
735 |
861 |
736 // GetStringSlice returns the value associated with the key as a slice of strings. |
862 // GetStringSlice returns the value associated with the key as a slice of strings. |
737 func GetStringSlice(key string) []string { return v.GetStringSlice(key) } |
863 func GetStringSlice(key string) []string { return v.GetStringSlice(key) } |
738 func (v *Viper) GetStringSlice(key string) []string { |
864 func (v *Viper) GetStringSlice(key string) []string { |
825 return decoder.Decode(input) |
947 return decoder.Decode(input) |
826 } |
948 } |
827 |
949 |
828 // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent |
950 // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent |
829 // in the destination struct. |
951 // in the destination struct. |
830 func (v *Viper) UnmarshalExact(rawVal interface{}) error { |
952 func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error { |
831 config := defaultDecoderConfig(rawVal) |
953 return v.UnmarshalExact(rawVal, opts...) |
|
954 } |
|
955 func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error { |
|
956 config := defaultDecoderConfig(rawVal, opts...) |
832 config.ErrorUnused = true |
957 config.ErrorUnused = true |
833 |
958 |
834 err := decode(v.AllSettings(), config) |
959 err := decode(v.AllSettings(), config) |
835 |
960 |
836 if err != nil { |
961 if err != nil { |
837 return err |
962 return err |
838 } |
963 } |
839 |
|
840 v.insensitiviseMaps() |
|
841 |
964 |
842 return nil |
965 return nil |
843 } |
966 } |
844 |
967 |
845 // BindPFlags binds a full flag set to the configuration, using each flag's long |
968 // BindPFlags binds a full flag set to the configuration, using each flag's long |
910 |
1033 |
911 return nil |
1034 return nil |
912 } |
1035 } |
913 |
1036 |
914 // Given a key, find the value. |
1037 // Given a key, find the value. |
915 // Viper will check in the following order: |
1038 // |
916 // flag, env, config file, key/value store, default. |
|
917 // Viper will check to see if an alias exists first. |
1039 // Viper will check to see if an alias exists first. |
|
1040 // Viper will then check in the following order: |
|
1041 // flag, env, config file, key/value store. |
|
1042 // Lastly, if no value was found and flagDefault is true, and if the key |
|
1043 // corresponds to a flag, the flag's default value is returned. |
|
1044 // |
918 // Note: this assumes a lower-cased key given. |
1045 // Note: this assumes a lower-cased key given. |
919 func (v *Viper) find(lcaseKey string) interface{} { |
1046 func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { |
920 |
|
921 var ( |
1047 var ( |
922 val interface{} |
1048 val interface{} |
923 exists bool |
1049 exists bool |
924 path = strings.Split(lcaseKey, v.keyDelim) |
1050 path = strings.Split(lcaseKey, v.keyDelim) |
925 nested = len(path) > 1 |
1051 nested = len(path) > 1 |
967 |
1098 |
968 // Env override next |
1099 // Env override next |
969 if v.automaticEnvApplied { |
1100 if v.automaticEnvApplied { |
970 // even if it hasn't been registered, if automaticEnv is used, |
1101 // even if it hasn't been registered, if automaticEnv is used, |
971 // check any Get request |
1102 // check any Get request |
972 if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" { |
1103 if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok { |
973 return val |
1104 return val |
974 } |
1105 } |
975 if nested && v.isPathShadowedInAutoEnv(path) != "" { |
1106 if nested && v.isPathShadowedInAutoEnv(path) != "" { |
976 return nil |
1107 return nil |
977 } |
1108 } |
978 } |
1109 } |
979 envkey, exists := v.env[lcaseKey] |
1110 envkey, exists := v.env[lcaseKey] |
980 if exists { |
1111 if exists { |
981 if val = v.getEnv(envkey); val != "" { |
1112 if val, ok := v.getEnv(envkey); ok { |
982 return val |
1113 return val |
983 } |
1114 } |
984 } |
1115 } |
985 if nested && v.isPathShadowedInFlatMap(path, v.env) != "" { |
1116 if nested && v.isPathShadowedInFlatMap(path, v.env) != "" { |
986 return nil |
1117 return nil |
1011 } |
1142 } |
1012 if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" { |
1143 if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" { |
1013 return nil |
1144 return nil |
1014 } |
1145 } |
1015 |
1146 |
1016 // last chance: if no other value is returned and a flag does exist for the value, |
1147 if flagDefault { |
1017 // get the flag's value even if the flag's value has not changed |
1148 // last chance: if no value is found and a flag does exist for the key, |
1018 if flag, exists := v.pflags[lcaseKey]; exists { |
1149 // get the flag's default value even if the flag's value has not been set. |
1019 switch flag.ValueType() { |
1150 if flag, exists := v.pflags[lcaseKey]; exists { |
1020 case "int", "int8", "int16", "int32", "int64": |
1151 switch flag.ValueType() { |
1021 return cast.ToInt(flag.ValueString()) |
1152 case "int", "int8", "int16", "int32", "int64": |
1022 case "bool": |
1153 return cast.ToInt(flag.ValueString()) |
1023 return cast.ToBool(flag.ValueString()) |
1154 case "bool": |
1024 case "stringSlice": |
1155 return cast.ToBool(flag.ValueString()) |
1025 s := strings.TrimPrefix(flag.ValueString(), "[") |
1156 case "stringSlice": |
1026 s = strings.TrimSuffix(s, "]") |
1157 s := strings.TrimPrefix(flag.ValueString(), "[") |
1027 res, _ := readAsCSV(s) |
1158 s = strings.TrimSuffix(s, "]") |
1028 return res |
1159 res, _ := readAsCSV(s) |
1029 default: |
1160 return res |
1030 return flag.ValueString() |
1161 case "intSlice": |
1031 } |
1162 s := strings.TrimPrefix(flag.ValueString(), "[") |
1032 } |
1163 s = strings.TrimSuffix(s, "]") |
1033 // last item, no need to check shadowing |
1164 res, _ := readAsCSV(s) |
|
1165 return cast.ToIntSlice(res) |
|
1166 default: |
|
1167 return flag.ValueString() |
|
1168 } |
|
1169 } |
|
1170 // last item, no need to check shadowing |
|
1171 } |
1034 |
1172 |
1035 return nil |
1173 return nil |
1036 } |
1174 } |
1037 |
1175 |
1038 func readAsCSV(val string) ([]string, error) { |
1176 func readAsCSV(val string) ([]string, error) { |
1066 func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) } |
1204 func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) } |
1067 func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) { |
1205 func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) { |
1068 v.envKeyReplacer = r |
1206 v.envKeyReplacer = r |
1069 } |
1207 } |
1070 |
1208 |
1071 // Aliases provide another accessor for the same key. |
1209 // RegisterAlias creates an alias that provides another accessor for the same key. |
1072 // This enables one to change a name without breaking the application |
1210 // This enables one to change a name without breaking the application. |
1073 func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) } |
1211 func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) } |
1074 func (v *Viper) RegisterAlias(alias string, key string) { |
1212 func (v *Viper) RegisterAlias(alias string, key string) { |
1075 v.registerAlias(alias, strings.ToLower(key)) |
1213 v.registerAlias(alias, strings.ToLower(key)) |
1076 } |
1214 } |
1077 |
1215 |
1222 } |
1360 } |
1223 |
1361 |
1224 // MergeConfig merges a new configuration with an existing config. |
1362 // MergeConfig merges a new configuration with an existing config. |
1225 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } |
1363 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } |
1226 func (v *Viper) MergeConfig(in io.Reader) error { |
1364 func (v *Viper) MergeConfig(in io.Reader) error { |
1227 if v.config == nil { |
|
1228 v.config = make(map[string]interface{}) |
|
1229 } |
|
1230 cfg := make(map[string]interface{}) |
1365 cfg := make(map[string]interface{}) |
1231 if err := v.unmarshalReader(in, cfg); err != nil { |
1366 if err := v.unmarshalReader(in, cfg); err != nil { |
1232 return err |
1367 return err |
1233 } |
1368 } |
|
1369 return v.MergeConfigMap(cfg) |
|
1370 } |
|
1371 |
|
1372 // MergeConfigMap merges the configuration from the map given with an existing config. |
|
1373 // Note that the map given may be modified. |
|
1374 func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) } |
|
1375 func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error { |
|
1376 if v.config == nil { |
|
1377 v.config = make(map[string]interface{}) |
|
1378 } |
|
1379 insensitiviseMap(cfg) |
1234 mergeMaps(cfg, v.config, nil) |
1380 mergeMaps(cfg, v.config, nil) |
1235 return nil |
1381 return nil |
1236 } |
1382 } |
1237 |
1383 |
1238 // WriteConfig writes the current configuration to a file. |
1384 // WriteConfig writes the current configuration to a file. |
1246 } |
1392 } |
1247 |
1393 |
1248 // SafeWriteConfig writes current configuration to file only if the file does not exist. |
1394 // SafeWriteConfig writes current configuration to file only if the file does not exist. |
1249 func SafeWriteConfig() error { return v.SafeWriteConfig() } |
1395 func SafeWriteConfig() error { return v.SafeWriteConfig() } |
1250 func (v *Viper) SafeWriteConfig() error { |
1396 func (v *Viper) SafeWriteConfig() error { |
1251 filename, err := v.getConfigFile() |
1397 if len(v.configPaths) < 1 { |
1252 if err != nil { |
1398 return errors.New("missing configuration for 'configPath'") |
1253 return err |
1399 } |
1254 } |
1400 return v.SafeWriteConfigAs(filepath.Join(v.configPaths[0], v.configName+"."+v.configType)) |
1255 return v.writeConfig(filename, false) |
|
1256 } |
1401 } |
1257 |
1402 |
1258 // WriteConfigAs writes current configuration to a given filename. |
1403 // WriteConfigAs writes current configuration to a given filename. |
1259 func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } |
1404 func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } |
1260 func (v *Viper) WriteConfigAs(filename string) error { |
1405 func (v *Viper) WriteConfigAs(filename string) error { |
1262 } |
1407 } |
1263 |
1408 |
1264 // SafeWriteConfigAs writes current configuration to a given filename if it does not exist. |
1409 // SafeWriteConfigAs writes current configuration to a given filename if it does not exist. |
1265 func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } |
1410 func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } |
1266 func (v *Viper) SafeWriteConfigAs(filename string) error { |
1411 func (v *Viper) SafeWriteConfigAs(filename string) error { |
|
1412 alreadyExists, err := afero.Exists(v.fs, filename) |
|
1413 if alreadyExists && err == nil { |
|
1414 return ConfigFileAlreadyExistsError(filename) |
|
1415 } |
1267 return v.writeConfig(filename, false) |
1416 return v.writeConfig(filename, false) |
1268 } |
1417 } |
1269 |
1418 |
1270 func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) } |
|
1271 func (v *Viper) writeConfig(filename string, force bool) error { |
1419 func (v *Viper) writeConfig(filename string, force bool) error { |
1272 jww.INFO.Println("Attempting to write configuration to file.") |
1420 jww.INFO.Println("Attempting to write configuration to file.") |
1273 ext := filepath.Ext(filename) |
1421 ext := filepath.Ext(filename) |
1274 if len(ext) <= 1 { |
1422 if len(ext) <= 1 { |
1275 return fmt.Errorf("Filename: %s requires valid extension.", filename) |
1423 return fmt.Errorf("filename: %s requires valid extension", filename) |
1276 } |
1424 } |
1277 configType := ext[1:] |
1425 configType := ext[1:] |
1278 if !stringInSlice(configType, SupportedExts) { |
1426 if !stringInSlice(configType, SupportedExts) { |
1279 return UnsupportedConfigError(configType) |
1427 return UnsupportedConfigError(configType) |
1280 } |
1428 } |
1281 if v.config == nil { |
1429 if v.config == nil { |
1282 v.config = make(map[string]interface{}) |
1430 v.config = make(map[string]interface{}) |
1283 } |
1431 } |
1284 var flags int |
1432 flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY |
1285 if force == true { |
1433 if !force { |
1286 flags = os.O_CREATE | os.O_TRUNC | os.O_WRONLY |
1434 flags |= os.O_EXCL |
1287 } else { |
1435 } |
1288 if _, err := os.Stat(filename); os.IsNotExist(err) { |
1436 f, err := v.fs.OpenFile(filename, flags, v.configPermissions) |
1289 flags = os.O_WRONLY |
|
1290 } else { |
|
1291 return fmt.Errorf("File: %s exists. Use WriteConfig to overwrite.", filename) |
|
1292 } |
|
1293 } |
|
1294 f, err := v.fs.OpenFile(filename, flags, os.FileMode(0644)) |
|
1295 if err != nil { |
1437 if err != nil { |
1296 return err |
1438 return err |
1297 } |
1439 } |
1298 return v.marshalWriter(f, configType) |
1440 defer f.Close() |
|
1441 |
|
1442 if err := v.marshalWriter(f, configType); err != nil { |
|
1443 return err |
|
1444 } |
|
1445 |
|
1446 return f.Sync() |
1299 } |
1447 } |
1300 |
1448 |
1301 // Unmarshal a Reader into a map. |
1449 // Unmarshal a Reader into a map. |
1302 // Should probably be an unexported function. |
1450 // Should probably be an unexported function. |
1303 func unmarshalReader(in io.Reader, c map[string]interface{}) error { |
1451 func unmarshalReader(in io.Reader, c map[string]interface{}) error { |
1350 lastKey := strings.ToLower(path[len(path)-1]) |
1507 lastKey := strings.ToLower(path[len(path)-1]) |
1351 deepestMap := deepSearch(c, path[0:len(path)-1]) |
1508 deepestMap := deepSearch(c, path[0:len(path)-1]) |
1352 // set innermost value |
1509 // set innermost value |
1353 deepestMap[lastKey] = value |
1510 deepestMap[lastKey] = value |
1354 } |
1511 } |
|
1512 |
|
1513 case "ini": |
|
1514 cfg := ini.Empty() |
|
1515 err := cfg.Append(buf.Bytes()) |
|
1516 if err != nil { |
|
1517 return ConfigParseError{err} |
|
1518 } |
|
1519 sections := cfg.Sections() |
|
1520 for i := 0; i < len(sections); i++ { |
|
1521 section := sections[i] |
|
1522 keys := section.Keys() |
|
1523 for j := 0; j < len(keys); j++ { |
|
1524 key := keys[j] |
|
1525 value := cfg.Section(section.Name()).Key(key.Name()).String() |
|
1526 c[section.Name()+"."+key.Name()] = value |
|
1527 } |
|
1528 } |
1355 } |
1529 } |
1356 |
1530 |
1357 insensitiviseMap(c) |
1531 insensitiviseMap(c) |
1358 return nil |
1532 return nil |
1359 } |
1533 } |
1360 |
1534 |
1361 // Marshal a map into Writer. |
1535 // Marshal a map into Writer. |
1362 func marshalWriter(f afero.File, configType string) error { |
|
1363 return v.marshalWriter(f, configType) |
|
1364 } |
|
1365 func (v *Viper) marshalWriter(f afero.File, configType string) error { |
1536 func (v *Viper) marshalWriter(f afero.File, configType string) error { |
1366 c := v.AllSettings() |
1537 c := v.AllSettings() |
1367 switch configType { |
1538 switch configType { |
1368 case "json": |
1539 case "json": |
1369 b, err := json.MarshalIndent(c, "", " ") |
1540 b, err := json.MarshalIndent(c, "", " ") |
1400 _, err := p.WriteComment(f, "#", properties.UTF8) |
1574 _, err := p.WriteComment(f, "#", properties.UTF8) |
1401 if err != nil { |
1575 if err != nil { |
1402 return ConfigMarshalError{err} |
1576 return ConfigMarshalError{err} |
1403 } |
1577 } |
1404 |
1578 |
|
1579 case "dotenv", "env": |
|
1580 lines := []string{} |
|
1581 for _, key := range v.AllKeys() { |
|
1582 envName := strings.ToUpper(strings.Replace(key, ".", "_", -1)) |
|
1583 val := v.Get(key) |
|
1584 lines = append(lines, fmt.Sprintf("%v=%v", envName, val)) |
|
1585 } |
|
1586 s := strings.Join(lines, "\n") |
|
1587 if _, err := f.WriteString(s); err != nil { |
|
1588 return ConfigMarshalError{err} |
|
1589 } |
|
1590 |
1405 case "toml": |
1591 case "toml": |
1406 t, err := toml.TreeFromMap(c) |
1592 t, err := toml.TreeFromMap(c) |
1407 if err != nil { |
1593 if err != nil { |
1408 return ConfigMarshalError{err} |
1594 return ConfigMarshalError{err} |
1409 } |
1595 } |
1607 err = v.unmarshalReader(reader, v.kvstore) |
1802 err = v.unmarshalReader(reader, v.kvstore) |
1608 return v.kvstore, err |
1803 return v.kvstore, err |
1609 } |
1804 } |
1610 |
1805 |
1611 // AllKeys returns all keys holding a value, regardless of where they are set. |
1806 // AllKeys returns all keys holding a value, regardless of where they are set. |
1612 // Nested keys are returned with a v.keyDelim (= ".") separator |
1807 // Nested keys are returned with a v.keyDelim separator |
1613 func AllKeys() []string { return v.AllKeys() } |
1808 func AllKeys() []string { return v.AllKeys() } |
1614 func (v *Viper) AllKeys() []string { |
1809 func (v *Viper) AllKeys() []string { |
1615 m := map[string]bool{} |
1810 m := map[string]bool{} |
1616 // add all paths, by order of descending priority to ensure correct shadowing |
1811 // add all paths, by order of descending priority to ensure correct shadowing |
1617 m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "") |
1812 m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "") |
1621 m = v.flattenAndMergeMap(m, v.config, "") |
1816 m = v.flattenAndMergeMap(m, v.config, "") |
1622 m = v.flattenAndMergeMap(m, v.kvstore, "") |
1817 m = v.flattenAndMergeMap(m, v.kvstore, "") |
1623 m = v.flattenAndMergeMap(m, v.defaults, "") |
1818 m = v.flattenAndMergeMap(m, v.defaults, "") |
1624 |
1819 |
1625 // convert set of paths to list |
1820 // convert set of paths to list |
1626 a := []string{} |
1821 a := make([]string, 0, len(m)) |
1627 for x := range m { |
1822 for x := range m { |
1628 a = append(a, x) |
1823 a = append(a, x) |
1629 } |
1824 } |
1630 return a |
1825 return a |
1631 } |
1826 } |
1632 |
1827 |
1633 // flattenAndMergeMap recursively flattens the given map into a map[string]bool |
1828 // flattenAndMergeMap recursively flattens the given map into a map[string]bool |
1634 // of key paths (used as a set, easier to manipulate than a []string): |
1829 // of key paths (used as a set, easier to manipulate than a []string): |
1635 // - each path is merged into a single key string, delimited with v.keyDelim (= ".") |
1830 // - each path is merged into a single key string, delimited with v.keyDelim |
1636 // - if a path is shadowed by an earlier value in the initial shadow map, |
1831 // - if a path is shadowed by an earlier value in the initial shadow map, |
1637 // it is skipped. |
1832 // it is skipped. |
1638 // The resulting set of paths is merged to the given shadow set at the same time. |
1833 // The resulting set of paths is merged to the given shadow set at the same time. |
1639 func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool { |
1834 func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool { |
1640 if shadow != nil && prefix != "" && shadow[prefix] { |
1835 if shadow != nil && prefix != "" && shadow[prefix] { |
1670 // mergeFlatMap merges the given maps, excluding values of the second map |
1865 // mergeFlatMap merges the given maps, excluding values of the second map |
1671 // shadowed by values from the first map. |
1866 // shadowed by values from the first map. |
1672 func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool { |
1867 func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool { |
1673 // scan keys |
1868 // scan keys |
1674 outer: |
1869 outer: |
1675 for k, _ := range m { |
1870 for k := range m { |
1676 path := strings.Split(k, v.keyDelim) |
1871 path := strings.Split(k, v.keyDelim) |
1677 // scan intermediate paths |
1872 // scan intermediate paths |
1678 var parentKey string |
1873 var parentKey string |
1679 for i := 1; i < len(path); i++ { |
1874 for i := 1; i < len(path); i++ { |
1680 parentKey = strings.Join(path[0:i], v.keyDelim) |
1875 parentKey = strings.Join(path[0:i], v.keyDelim) |
1770 for _, ext := range SupportedExts { |
1971 for _, ext := range SupportedExts { |
1771 jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) |
1972 jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) |
1772 if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { |
1973 if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { |
1773 jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) |
1974 jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) |
1774 return filepath.Join(in, v.configName+"."+ext) |
1975 return filepath.Join(in, v.configName+"."+ext) |
|
1976 } |
|
1977 } |
|
1978 |
|
1979 if v.configType != "" { |
|
1980 if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b { |
|
1981 return filepath.Join(in, v.configName) |
1775 } |
1982 } |
1776 } |
1983 } |
1777 |
1984 |
1778 return "" |
1985 return "" |
1779 } |
1986 } |