|
1 // Copyright © 2014 Steve Francia <spf@spf13.com>. |
|
2 // |
|
3 // Use of this source code is governed by an MIT-style |
|
4 // license that can be found in the LICENSE file. |
|
5 |
|
6 // Viper is a application configuration system. |
|
7 // It believes that applications can be configured a variety of ways |
|
8 // via flags, ENVIRONMENT variables, configuration files retrieved |
|
9 // from the file system, or a remote key/value store. |
|
10 |
|
11 // Each item takes precedence over the item below it: |
|
12 |
|
13 // overrides |
|
14 // flag |
|
15 // env |
|
16 // config |
|
17 // key/value store |
|
18 // default |
|
19 |
|
20 package viper |
|
21 |
|
22 import ( |
|
23 "bytes" |
|
24 "encoding/csv" |
|
25 "encoding/json" |
|
26 "fmt" |
|
27 "io" |
|
28 "log" |
|
29 "os" |
|
30 "path/filepath" |
|
31 "reflect" |
|
32 "strings" |
|
33 "time" |
|
34 |
|
35 yaml "gopkg.in/yaml.v2" |
|
36 |
|
37 "github.com/fsnotify/fsnotify" |
|
38 "github.com/hashicorp/hcl" |
|
39 "github.com/hashicorp/hcl/hcl/printer" |
|
40 "github.com/magiconair/properties" |
|
41 "github.com/mitchellh/mapstructure" |
|
42 toml "github.com/pelletier/go-toml" |
|
43 "github.com/spf13/afero" |
|
44 "github.com/spf13/cast" |
|
45 jww "github.com/spf13/jwalterweatherman" |
|
46 "github.com/spf13/pflag" |
|
47 ) |
|
48 |
|
49 // ConfigMarshalError happens when failing to marshal the configuration. |
|
50 type ConfigMarshalError struct { |
|
51 err error |
|
52 } |
|
53 |
|
54 // Error returns the formatted configuration error. |
|
55 func (e ConfigMarshalError) Error() string { |
|
56 return fmt.Sprintf("While marshaling config: %s", e.err.Error()) |
|
57 } |
|
58 |
|
59 var v *Viper |
|
60 |
|
61 type RemoteResponse struct { |
|
62 Value []byte |
|
63 Error error |
|
64 } |
|
65 |
|
66 func init() { |
|
67 v = New() |
|
68 } |
|
69 |
|
70 type remoteConfigFactory interface { |
|
71 Get(rp RemoteProvider) (io.Reader, error) |
|
72 Watch(rp RemoteProvider) (io.Reader, error) |
|
73 WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) |
|
74 } |
|
75 |
|
76 // RemoteConfig is optional, see the remote package |
|
77 var RemoteConfig remoteConfigFactory |
|
78 |
|
79 // UnsupportedConfigError denotes encountering an unsupported |
|
80 // configuration filetype. |
|
81 type UnsupportedConfigError string |
|
82 |
|
83 // Error returns the formatted configuration error. |
|
84 func (str UnsupportedConfigError) Error() string { |
|
85 return fmt.Sprintf("Unsupported Config Type %q", string(str)) |
|
86 } |
|
87 |
|
88 // UnsupportedRemoteProviderError denotes encountering an unsupported remote |
|
89 // provider. Currently only etcd and Consul are supported. |
|
90 type UnsupportedRemoteProviderError string |
|
91 |
|
92 // Error returns the formatted remote provider error. |
|
93 func (str UnsupportedRemoteProviderError) Error() string { |
|
94 return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str)) |
|
95 } |
|
96 |
|
97 // RemoteConfigError denotes encountering an error while trying to |
|
98 // pull the configuration from the remote provider. |
|
99 type RemoteConfigError string |
|
100 |
|
101 // Error returns the formatted remote provider error |
|
102 func (rce RemoteConfigError) Error() string { |
|
103 return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) |
|
104 } |
|
105 |
|
106 // ConfigFileNotFoundError denotes failing to find configuration file. |
|
107 type ConfigFileNotFoundError struct { |
|
108 name, locations string |
|
109 } |
|
110 |
|
111 // Error returns the formatted configuration error. |
|
112 func (fnfe ConfigFileNotFoundError) Error() string { |
|
113 return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) |
|
114 } |
|
115 |
|
116 // A DecoderConfigOption can be passed to viper.Unmarshal to configure |
|
117 // mapstructure.DecoderConfig options |
|
118 type DecoderConfigOption func(*mapstructure.DecoderConfig) |
|
119 |
|
120 // DecodeHook returns a DecoderConfigOption which overrides the default |
|
121 // DecoderConfig.DecodeHook value, the default is: |
|
122 // |
|
123 // mapstructure.ComposeDecodeHookFunc( |
|
124 // mapstructure.StringToTimeDurationHookFunc(), |
|
125 // mapstructure.StringToSliceHookFunc(","), |
|
126 // ) |
|
127 func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption { |
|
128 return func(c *mapstructure.DecoderConfig) { |
|
129 c.DecodeHook = hook |
|
130 } |
|
131 } |
|
132 |
|
133 // Viper is a prioritized configuration registry. It |
|
134 // maintains a set of configuration sources, fetches |
|
135 // values to populate those, and provides them according |
|
136 // to the source's priority. |
|
137 // The priority of the sources is the following: |
|
138 // 1. overrides |
|
139 // 2. flags |
|
140 // 3. env. variables |
|
141 // 4. config file |
|
142 // 5. key/value store |
|
143 // 6. defaults |
|
144 // |
|
145 // For example, if values from the following sources were loaded: |
|
146 // |
|
147 // Defaults : { |
|
148 // "secret": "", |
|
149 // "user": "default", |
|
150 // "endpoint": "https://localhost" |
|
151 // } |
|
152 // Config : { |
|
153 // "user": "root" |
|
154 // "secret": "defaultsecret" |
|
155 // } |
|
156 // Env : { |
|
157 // "secret": "somesecretkey" |
|
158 // } |
|
159 // |
|
160 // The resulting config will have the following values: |
|
161 // |
|
162 // { |
|
163 // "secret": "somesecretkey", |
|
164 // "user": "root", |
|
165 // "endpoint": "https://localhost" |
|
166 // } |
|
167 type Viper struct { |
|
168 // Delimiter that separates a list of keys |
|
169 // used to access a nested value in one go |
|
170 keyDelim string |
|
171 |
|
172 // A set of paths to look for the config file in |
|
173 configPaths []string |
|
174 |
|
175 // The filesystem to read config from. |
|
176 fs afero.Fs |
|
177 |
|
178 // A set of remote providers to search for the configuration |
|
179 remoteProviders []*defaultRemoteProvider |
|
180 |
|
181 // Name of file to look for inside the path |
|
182 configName string |
|
183 configFile string |
|
184 configType string |
|
185 envPrefix string |
|
186 |
|
187 automaticEnvApplied bool |
|
188 envKeyReplacer *strings.Replacer |
|
189 |
|
190 config map[string]interface{} |
|
191 override map[string]interface{} |
|
192 defaults map[string]interface{} |
|
193 kvstore map[string]interface{} |
|
194 pflags map[string]FlagValue |
|
195 env map[string]string |
|
196 aliases map[string]string |
|
197 typeByDefValue bool |
|
198 |
|
199 // Store read properties on the object so that we can write back in order with comments. |
|
200 // This will only be used if the configuration read is a properties file. |
|
201 properties *properties.Properties |
|
202 |
|
203 onConfigChange func(fsnotify.Event) |
|
204 } |
|
205 |
|
206 // New returns an initialized Viper instance. |
|
207 func New() *Viper { |
|
208 v := new(Viper) |
|
209 v.keyDelim = "." |
|
210 v.configName = "config" |
|
211 v.fs = afero.NewOsFs() |
|
212 v.config = make(map[string]interface{}) |
|
213 v.override = make(map[string]interface{}) |
|
214 v.defaults = make(map[string]interface{}) |
|
215 v.kvstore = make(map[string]interface{}) |
|
216 v.pflags = make(map[string]FlagValue) |
|
217 v.env = make(map[string]string) |
|
218 v.aliases = make(map[string]string) |
|
219 v.typeByDefValue = false |
|
220 |
|
221 return v |
|
222 } |
|
223 |
|
224 // Intended for testing, will reset all to default settings. |
|
225 // In the public interface for the viper package so applications |
|
226 // can use it in their testing as well. |
|
227 func Reset() { |
|
228 v = New() |
|
229 SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} |
|
230 SupportedRemoteProviders = []string{"etcd", "consul"} |
|
231 } |
|
232 |
|
233 type defaultRemoteProvider struct { |
|
234 provider string |
|
235 endpoint string |
|
236 path string |
|
237 secretKeyring string |
|
238 } |
|
239 |
|
240 func (rp defaultRemoteProvider) Provider() string { |
|
241 return rp.provider |
|
242 } |
|
243 |
|
244 func (rp defaultRemoteProvider) Endpoint() string { |
|
245 return rp.endpoint |
|
246 } |
|
247 |
|
248 func (rp defaultRemoteProvider) Path() string { |
|
249 return rp.path |
|
250 } |
|
251 |
|
252 func (rp defaultRemoteProvider) SecretKeyring() string { |
|
253 return rp.secretKeyring |
|
254 } |
|
255 |
|
256 // RemoteProvider stores the configuration necessary |
|
257 // to connect to a remote key/value store. |
|
258 // Optional secretKeyring to unencrypt encrypted values |
|
259 // can be provided. |
|
260 type RemoteProvider interface { |
|
261 Provider() string |
|
262 Endpoint() string |
|
263 Path() string |
|
264 SecretKeyring() string |
|
265 } |
|
266 |
|
267 // SupportedExts are universally supported extensions. |
|
268 var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} |
|
269 |
|
270 // SupportedRemoteProviders are universally supported remote providers. |
|
271 var SupportedRemoteProviders = []string{"etcd", "consul"} |
|
272 |
|
273 func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } |
|
274 func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { |
|
275 v.onConfigChange = run |
|
276 } |
|
277 |
|
278 func WatchConfig() { v.WatchConfig() } |
|
279 func (v *Viper) WatchConfig() { |
|
280 go func() { |
|
281 watcher, err := fsnotify.NewWatcher() |
|
282 if err != nil { |
|
283 log.Fatal(err) |
|
284 } |
|
285 defer watcher.Close() |
|
286 |
|
287 // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way |
|
288 filename, err := v.getConfigFile() |
|
289 if err != nil { |
|
290 log.Println("error:", err) |
|
291 return |
|
292 } |
|
293 |
|
294 configFile := filepath.Clean(filename) |
|
295 configDir, _ := filepath.Split(configFile) |
|
296 |
|
297 done := make(chan bool) |
|
298 go func() { |
|
299 for { |
|
300 select { |
|
301 case event := <-watcher.Events: |
|
302 // we only care about the config file |
|
303 if filepath.Clean(event.Name) == configFile { |
|
304 if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { |
|
305 err := v.ReadInConfig() |
|
306 if err != nil { |
|
307 log.Println("error:", err) |
|
308 } |
|
309 if v.onConfigChange != nil { |
|
310 v.onConfigChange(event) |
|
311 } |
|
312 } |
|
313 } |
|
314 case err := <-watcher.Errors: |
|
315 log.Println("error:", err) |
|
316 } |
|
317 } |
|
318 }() |
|
319 |
|
320 watcher.Add(configDir) |
|
321 <-done |
|
322 }() |
|
323 } |
|
324 |
|
325 // 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. |
|
327 func SetConfigFile(in string) { v.SetConfigFile(in) } |
|
328 func (v *Viper) SetConfigFile(in string) { |
|
329 if in != "" { |
|
330 v.configFile = in |
|
331 } |
|
332 } |
|
333 |
|
334 // SetEnvPrefix defines a prefix that ENVIRONMENT variables will use. |
|
335 // E.g. if your prefix is "spf", the env registry will look for env |
|
336 // variables that start with "SPF_". |
|
337 func SetEnvPrefix(in string) { v.SetEnvPrefix(in) } |
|
338 func (v *Viper) SetEnvPrefix(in string) { |
|
339 if in != "" { |
|
340 v.envPrefix = in |
|
341 } |
|
342 } |
|
343 |
|
344 func (v *Viper) mergeWithEnvPrefix(in string) string { |
|
345 if v.envPrefix != "" { |
|
346 return strings.ToUpper(v.envPrefix + "_" + in) |
|
347 } |
|
348 |
|
349 return strings.ToUpper(in) |
|
350 } |
|
351 |
|
352 // TODO: should getEnv logic be moved into find(). Can generalize the use of |
|
353 // rewriting keys many things, Ex: Get('someKey') -> some_key |
|
354 // (camel case to snake case for JSON keys perhaps) |
|
355 |
|
356 // 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 |
|
358 // keys. |
|
359 func (v *Viper) getEnv(key string) string { |
|
360 if v.envKeyReplacer != nil { |
|
361 key = v.envKeyReplacer.Replace(key) |
|
362 } |
|
363 return os.Getenv(key) |
|
364 } |
|
365 |
|
366 // ConfigFileUsed returns the file used to populate the config registry. |
|
367 func ConfigFileUsed() string { return v.ConfigFileUsed() } |
|
368 func (v *Viper) ConfigFileUsed() string { return v.configFile } |
|
369 |
|
370 // AddConfigPath adds a path for Viper to search for the config file in. |
|
371 // Can be called multiple times to define multiple search paths. |
|
372 func AddConfigPath(in string) { v.AddConfigPath(in) } |
|
373 func (v *Viper) AddConfigPath(in string) { |
|
374 if in != "" { |
|
375 absin := absPathify(in) |
|
376 jww.INFO.Println("adding", absin, "to paths to search") |
|
377 if !stringInSlice(absin, v.configPaths) { |
|
378 v.configPaths = append(v.configPaths, absin) |
|
379 } |
|
380 } |
|
381 } |
|
382 |
|
383 // AddRemoteProvider adds a remote configuration source. |
|
384 // Remote Providers are searched in the order they are added. |
|
385 // provider is a string value, "etcd" or "consul" are currently supported. |
|
386 // endpoint is the url. etcd requires http://ip:port consul requires ip:port |
|
387 // path is the path in the k/v store to retrieve configuration |
|
388 // To retrieve a config file called myapp.json from /configs/myapp.json |
|
389 // you should set path to /configs and set config name (SetConfigName()) to |
|
390 // "myapp" |
|
391 func AddRemoteProvider(provider, endpoint, path string) error { |
|
392 return v.AddRemoteProvider(provider, endpoint, path) |
|
393 } |
|
394 func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { |
|
395 if !stringInSlice(provider, SupportedRemoteProviders) { |
|
396 return UnsupportedRemoteProviderError(provider) |
|
397 } |
|
398 if provider != "" && endpoint != "" { |
|
399 jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) |
|
400 rp := &defaultRemoteProvider{ |
|
401 endpoint: endpoint, |
|
402 provider: provider, |
|
403 path: path, |
|
404 } |
|
405 if !v.providerPathExists(rp) { |
|
406 v.remoteProviders = append(v.remoteProviders, rp) |
|
407 } |
|
408 } |
|
409 return nil |
|
410 } |
|
411 |
|
412 // AddSecureRemoteProvider adds a remote configuration source. |
|
413 // Secure Remote Providers are searched in the order they are added. |
|
414 // provider is a string value, "etcd" or "consul" are currently supported. |
|
415 // endpoint is the url. etcd requires http://ip:port consul requires ip:port |
|
416 // secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg |
|
417 // path is the path in the k/v store to retrieve configuration |
|
418 // To retrieve a config file called myapp.json from /configs/myapp.json |
|
419 // you should set path to /configs and set config name (SetConfigName()) to |
|
420 // "myapp" |
|
421 // Secure Remote Providers are implemented with github.com/xordataexchange/crypt |
|
422 func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { |
|
423 return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) |
|
424 } |
|
425 |
|
426 func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { |
|
427 if !stringInSlice(provider, SupportedRemoteProviders) { |
|
428 return UnsupportedRemoteProviderError(provider) |
|
429 } |
|
430 if provider != "" && endpoint != "" { |
|
431 jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) |
|
432 rp := &defaultRemoteProvider{ |
|
433 endpoint: endpoint, |
|
434 provider: provider, |
|
435 path: path, |
|
436 secretKeyring: secretkeyring, |
|
437 } |
|
438 if !v.providerPathExists(rp) { |
|
439 v.remoteProviders = append(v.remoteProviders, rp) |
|
440 } |
|
441 } |
|
442 return nil |
|
443 } |
|
444 |
|
445 func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool { |
|
446 for _, y := range v.remoteProviders { |
|
447 if reflect.DeepEqual(y, p) { |
|
448 return true |
|
449 } |
|
450 } |
|
451 return false |
|
452 } |
|
453 |
|
454 // searchMap recursively searches for a value for path in source map. |
|
455 // Returns nil if not found. |
|
456 // Note: This assumes that the path entries and map keys are lower cased. |
|
457 func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} { |
|
458 if len(path) == 0 { |
|
459 return source |
|
460 } |
|
461 |
|
462 next, ok := source[path[0]] |
|
463 if ok { |
|
464 // Fast path |
|
465 if len(path) == 1 { |
|
466 return next |
|
467 } |
|
468 |
|
469 // Nested case |
|
470 switch next.(type) { |
|
471 case map[interface{}]interface{}: |
|
472 return v.searchMap(cast.ToStringMap(next), path[1:]) |
|
473 case map[string]interface{}: |
|
474 // Type assertion is safe here since it is only reached |
|
475 // if the type of `next` is the same as the type being asserted |
|
476 return v.searchMap(next.(map[string]interface{}), path[1:]) |
|
477 default: |
|
478 // got a value but nested key expected, return "nil" for not found |
|
479 return nil |
|
480 } |
|
481 } |
|
482 return nil |
|
483 } |
|
484 |
|
485 // searchMapWithPathPrefixes recursively searches for a value for path in source map. |
|
486 // |
|
487 // While searchMap() considers each path element as a single map key, this |
|
488 // function searches for, and prioritizes, merged path elements. |
|
489 // e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar" |
|
490 // is also defined, this latter value is returned for path ["foo", "bar"]. |
|
491 // |
|
492 // This should be useful only at config level (other maps may not contain dots |
|
493 // in their keys). |
|
494 // |
|
495 // Note: This assumes that the path entries and map keys are lower cased. |
|
496 func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} { |
|
497 if len(path) == 0 { |
|
498 return source |
|
499 } |
|
500 |
|
501 // search for path prefixes, starting from the longest one |
|
502 for i := len(path); i > 0; i-- { |
|
503 prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim)) |
|
504 |
|
505 next, ok := source[prefixKey] |
|
506 if ok { |
|
507 // Fast path |
|
508 if i == len(path) { |
|
509 return next |
|
510 } |
|
511 |
|
512 // Nested case |
|
513 var val interface{} |
|
514 switch next.(type) { |
|
515 case map[interface{}]interface{}: |
|
516 val = v.searchMapWithPathPrefixes(cast.ToStringMap(next), path[i:]) |
|
517 case map[string]interface{}: |
|
518 // Type assertion is safe here since it is only reached |
|
519 // if the type of `next` is the same as the type being asserted |
|
520 val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:]) |
|
521 default: |
|
522 // got a value but nested key expected, do nothing and look for next prefix |
|
523 } |
|
524 if val != nil { |
|
525 return val |
|
526 } |
|
527 } |
|
528 } |
|
529 |
|
530 // not found |
|
531 return nil |
|
532 } |
|
533 |
|
534 // isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere |
|
535 // on its path in the map. |
|
536 // e.g., if "foo.bar" has a value in the given map, it “shadows” |
|
537 // "foo.bar.baz" in a lower-priority map |
|
538 func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string { |
|
539 var parentVal interface{} |
|
540 for i := 1; i < len(path); i++ { |
|
541 parentVal = v.searchMap(m, path[0:i]) |
|
542 if parentVal == nil { |
|
543 // not found, no need to add more path elements |
|
544 return "" |
|
545 } |
|
546 switch parentVal.(type) { |
|
547 case map[interface{}]interface{}: |
|
548 continue |
|
549 case map[string]interface{}: |
|
550 continue |
|
551 default: |
|
552 // parentVal is a regular value which shadows "path" |
|
553 return strings.Join(path[0:i], v.keyDelim) |
|
554 } |
|
555 } |
|
556 return "" |
|
557 } |
|
558 |
|
559 // isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere |
|
560 // in a sub-path of the map. |
|
561 // e.g., if "foo.bar" has a value in the given map, it “shadows” |
|
562 // "foo.bar.baz" in a lower-priority map |
|
563 func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string { |
|
564 // unify input map |
|
565 var m map[string]interface{} |
|
566 switch mi.(type) { |
|
567 case map[string]string, map[string]FlagValue: |
|
568 m = cast.ToStringMap(mi) |
|
569 default: |
|
570 return "" |
|
571 } |
|
572 |
|
573 // scan paths |
|
574 var parentKey string |
|
575 for i := 1; i < len(path); i++ { |
|
576 parentKey = strings.Join(path[0:i], v.keyDelim) |
|
577 if _, ok := m[parentKey]; ok { |
|
578 return parentKey |
|
579 } |
|
580 } |
|
581 return "" |
|
582 } |
|
583 |
|
584 // isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere |
|
585 // in the environment, when automatic env is on. |
|
586 // e.g., if "foo.bar" has a value in the environment, it “shadows” |
|
587 // "foo.bar.baz" in a lower-priority map |
|
588 func (v *Viper) isPathShadowedInAutoEnv(path []string) string { |
|
589 var parentKey string |
|
590 var val string |
|
591 for i := 1; i < len(path); i++ { |
|
592 parentKey = strings.Join(path[0:i], v.keyDelim) |
|
593 if val = v.getEnv(v.mergeWithEnvPrefix(parentKey)); val != "" { |
|
594 return parentKey |
|
595 } |
|
596 } |
|
597 return "" |
|
598 } |
|
599 |
|
600 // SetTypeByDefaultValue enables or disables the inference of a key value's |
|
601 // type when the Get function is used based upon a key's default value as |
|
602 // opposed to the value returned based on the normal fetch logic. |
|
603 // |
|
604 // For example, if a key has a default value of []string{} and the same key |
|
605 // is set via an environment variable to "a b c", a call to the Get function |
|
606 // would return a string slice for the key if the key's type is inferred by |
|
607 // the default value and the Get function would return: |
|
608 // |
|
609 // []string {"a", "b", "c"} |
|
610 // |
|
611 // Otherwise the Get function would return: |
|
612 // |
|
613 // "a b c" |
|
614 func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) } |
|
615 func (v *Viper) SetTypeByDefaultValue(enable bool) { |
|
616 v.typeByDefValue = enable |
|
617 } |
|
618 |
|
619 // GetViper gets the global Viper instance. |
|
620 func GetViper() *Viper { |
|
621 return v |
|
622 } |
|
623 |
|
624 // Get can retrieve any value given the key to use. |
|
625 // Get is case-insensitive for a key. |
|
626 // Get has the behavior of returning the value associated with the first |
|
627 // place from where it is set. Viper will check in the following order: |
|
628 // override, flag, env, config file, key/value store, default |
|
629 // |
|
630 // Get returns an interface. For a specific value use one of the Get____ methods. |
|
631 func Get(key string) interface{} { return v.Get(key) } |
|
632 func (v *Viper) Get(key string) interface{} { |
|
633 lcaseKey := strings.ToLower(key) |
|
634 val := v.find(lcaseKey) |
|
635 if val == nil { |
|
636 return nil |
|
637 } |
|
638 |
|
639 if v.typeByDefValue { |
|
640 // TODO(bep) this branch isn't covered by a single test. |
|
641 valType := val |
|
642 path := strings.Split(lcaseKey, v.keyDelim) |
|
643 defVal := v.searchMap(v.defaults, path) |
|
644 if defVal != nil { |
|
645 valType = defVal |
|
646 } |
|
647 |
|
648 switch valType.(type) { |
|
649 case bool: |
|
650 return cast.ToBool(val) |
|
651 case string: |
|
652 return cast.ToString(val) |
|
653 case int32, int16, int8, int: |
|
654 return cast.ToInt(val) |
|
655 case int64: |
|
656 return cast.ToInt64(val) |
|
657 case float64, float32: |
|
658 return cast.ToFloat64(val) |
|
659 case time.Time: |
|
660 return cast.ToTime(val) |
|
661 case time.Duration: |
|
662 return cast.ToDuration(val) |
|
663 case []string: |
|
664 return cast.ToStringSlice(val) |
|
665 } |
|
666 } |
|
667 |
|
668 return val |
|
669 } |
|
670 |
|
671 // Sub returns new Viper instance representing a sub tree of this instance. |
|
672 // Sub is case-insensitive for a key. |
|
673 func Sub(key string) *Viper { return v.Sub(key) } |
|
674 func (v *Viper) Sub(key string) *Viper { |
|
675 subv := New() |
|
676 data := v.Get(key) |
|
677 if data == nil { |
|
678 return nil |
|
679 } |
|
680 |
|
681 if reflect.TypeOf(data).Kind() == reflect.Map { |
|
682 subv.config = cast.ToStringMap(data) |
|
683 return subv |
|
684 } |
|
685 return nil |
|
686 } |
|
687 |
|
688 // GetString returns the value associated with the key as a string. |
|
689 func GetString(key string) string { return v.GetString(key) } |
|
690 func (v *Viper) GetString(key string) string { |
|
691 return cast.ToString(v.Get(key)) |
|
692 } |
|
693 |
|
694 // GetBool returns the value associated with the key as a boolean. |
|
695 func GetBool(key string) bool { return v.GetBool(key) } |
|
696 func (v *Viper) GetBool(key string) bool { |
|
697 return cast.ToBool(v.Get(key)) |
|
698 } |
|
699 |
|
700 // GetInt returns the value associated with the key as an integer. |
|
701 func GetInt(key string) int { return v.GetInt(key) } |
|
702 func (v *Viper) GetInt(key string) int { |
|
703 return cast.ToInt(v.Get(key)) |
|
704 } |
|
705 |
|
706 // GetInt32 returns the value associated with the key as an integer. |
|
707 func GetInt32(key string) int32 { return v.GetInt32(key) } |
|
708 func (v *Viper) GetInt32(key string) int32 { |
|
709 return cast.ToInt32(v.Get(key)) |
|
710 } |
|
711 |
|
712 // GetInt64 returns the value associated with the key as an integer. |
|
713 func GetInt64(key string) int64 { return v.GetInt64(key) } |
|
714 func (v *Viper) GetInt64(key string) int64 { |
|
715 return cast.ToInt64(v.Get(key)) |
|
716 } |
|
717 |
|
718 // GetFloat64 returns the value associated with the key as a float64. |
|
719 func GetFloat64(key string) float64 { return v.GetFloat64(key) } |
|
720 func (v *Viper) GetFloat64(key string) float64 { |
|
721 return cast.ToFloat64(v.Get(key)) |
|
722 } |
|
723 |
|
724 // GetTime returns the value associated with the key as time. |
|
725 func GetTime(key string) time.Time { return v.GetTime(key) } |
|
726 func (v *Viper) GetTime(key string) time.Time { |
|
727 return cast.ToTime(v.Get(key)) |
|
728 } |
|
729 |
|
730 // GetDuration returns the value associated with the key as a duration. |
|
731 func GetDuration(key string) time.Duration { return v.GetDuration(key) } |
|
732 func (v *Viper) GetDuration(key string) time.Duration { |
|
733 return cast.ToDuration(v.Get(key)) |
|
734 } |
|
735 |
|
736 // GetStringSlice returns the value associated with the key as a slice of strings. |
|
737 func GetStringSlice(key string) []string { return v.GetStringSlice(key) } |
|
738 func (v *Viper) GetStringSlice(key string) []string { |
|
739 return cast.ToStringSlice(v.Get(key)) |
|
740 } |
|
741 |
|
742 // GetStringMap returns the value associated with the key as a map of interfaces. |
|
743 func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) } |
|
744 func (v *Viper) GetStringMap(key string) map[string]interface{} { |
|
745 return cast.ToStringMap(v.Get(key)) |
|
746 } |
|
747 |
|
748 // GetStringMapString returns the value associated with the key as a map of strings. |
|
749 func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) } |
|
750 func (v *Viper) GetStringMapString(key string) map[string]string { |
|
751 return cast.ToStringMapString(v.Get(key)) |
|
752 } |
|
753 |
|
754 // GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. |
|
755 func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) } |
|
756 func (v *Viper) GetStringMapStringSlice(key string) map[string][]string { |
|
757 return cast.ToStringMapStringSlice(v.Get(key)) |
|
758 } |
|
759 |
|
760 // GetSizeInBytes returns the size of the value associated with the given key |
|
761 // in bytes. |
|
762 func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) } |
|
763 func (v *Viper) GetSizeInBytes(key string) uint { |
|
764 sizeStr := cast.ToString(v.Get(key)) |
|
765 return parseSizeInBytes(sizeStr) |
|
766 } |
|
767 |
|
768 // UnmarshalKey takes a single key and unmarshals it into a Struct. |
|
769 func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { |
|
770 return v.UnmarshalKey(key, rawVal, opts...) |
|
771 } |
|
772 func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { |
|
773 err := decode(v.Get(key), defaultDecoderConfig(rawVal, opts...)) |
|
774 |
|
775 if err != nil { |
|
776 return err |
|
777 } |
|
778 |
|
779 v.insensitiviseMaps() |
|
780 |
|
781 return nil |
|
782 } |
|
783 |
|
784 // Unmarshal unmarshals the config into a Struct. Make sure that the tags |
|
785 // on the fields of the structure are properly set. |
|
786 func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { |
|
787 return v.Unmarshal(rawVal, opts...) |
|
788 } |
|
789 func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { |
|
790 err := decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...)) |
|
791 |
|
792 if err != nil { |
|
793 return err |
|
794 } |
|
795 |
|
796 v.insensitiviseMaps() |
|
797 |
|
798 return nil |
|
799 } |
|
800 |
|
801 // defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot |
|
802 // of time.Duration values & string slices |
|
803 func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { |
|
804 c := &mapstructure.DecoderConfig{ |
|
805 Metadata: nil, |
|
806 Result: output, |
|
807 WeaklyTypedInput: true, |
|
808 DecodeHook: mapstructure.ComposeDecodeHookFunc( |
|
809 mapstructure.StringToTimeDurationHookFunc(), |
|
810 mapstructure.StringToSliceHookFunc(","), |
|
811 ), |
|
812 } |
|
813 for _, opt := range opts { |
|
814 opt(c) |
|
815 } |
|
816 return c |
|
817 } |
|
818 |
|
819 // A wrapper around mapstructure.Decode that mimics the WeakDecode functionality |
|
820 func decode(input interface{}, config *mapstructure.DecoderConfig) error { |
|
821 decoder, err := mapstructure.NewDecoder(config) |
|
822 if err != nil { |
|
823 return err |
|
824 } |
|
825 return decoder.Decode(input) |
|
826 } |
|
827 |
|
828 // UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent |
|
829 // in the destination struct. |
|
830 func (v *Viper) UnmarshalExact(rawVal interface{}) error { |
|
831 config := defaultDecoderConfig(rawVal) |
|
832 config.ErrorUnused = true |
|
833 |
|
834 err := decode(v.AllSettings(), config) |
|
835 |
|
836 if err != nil { |
|
837 return err |
|
838 } |
|
839 |
|
840 v.insensitiviseMaps() |
|
841 |
|
842 return nil |
|
843 } |
|
844 |
|
845 // BindPFlags binds a full flag set to the configuration, using each flag's long |
|
846 // name as the config key. |
|
847 func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) } |
|
848 func (v *Viper) BindPFlags(flags *pflag.FlagSet) error { |
|
849 return v.BindFlagValues(pflagValueSet{flags}) |
|
850 } |
|
851 |
|
852 // BindPFlag binds a specific key to a pflag (as used by cobra). |
|
853 // Example (where serverCmd is a Cobra instance): |
|
854 // |
|
855 // serverCmd.Flags().Int("port", 1138, "Port to run Application server on") |
|
856 // Viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) |
|
857 // |
|
858 func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) } |
|
859 func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error { |
|
860 return v.BindFlagValue(key, pflagValue{flag}) |
|
861 } |
|
862 |
|
863 // BindFlagValues binds a full FlagValue set to the configuration, using each flag's long |
|
864 // name as the config key. |
|
865 func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) } |
|
866 func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { |
|
867 flags.VisitAll(func(flag FlagValue) { |
|
868 if err = v.BindFlagValue(flag.Name(), flag); err != nil { |
|
869 return |
|
870 } |
|
871 }) |
|
872 return nil |
|
873 } |
|
874 |
|
875 // BindFlagValue binds a specific key to a FlagValue. |
|
876 // Example (where serverCmd is a Cobra instance): |
|
877 // |
|
878 // serverCmd.Flags().Int("port", 1138, "Port to run Application server on") |
|
879 // Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port")) |
|
880 // |
|
881 func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) } |
|
882 func (v *Viper) BindFlagValue(key string, flag FlagValue) error { |
|
883 if flag == nil { |
|
884 return fmt.Errorf("flag for %q is nil", key) |
|
885 } |
|
886 v.pflags[strings.ToLower(key)] = flag |
|
887 return nil |
|
888 } |
|
889 |
|
890 // BindEnv binds a Viper key to a ENV variable. |
|
891 // ENV variables are case sensitive. |
|
892 // If only a key is provided, it will use the env key matching the key, uppercased. |
|
893 // EnvPrefix will be used when set when env name is not provided. |
|
894 func BindEnv(input ...string) error { return v.BindEnv(input...) } |
|
895 func (v *Viper) BindEnv(input ...string) error { |
|
896 var key, envkey string |
|
897 if len(input) == 0 { |
|
898 return fmt.Errorf("BindEnv missing key to bind to") |
|
899 } |
|
900 |
|
901 key = strings.ToLower(input[0]) |
|
902 |
|
903 if len(input) == 1 { |
|
904 envkey = v.mergeWithEnvPrefix(key) |
|
905 } else { |
|
906 envkey = input[1] |
|
907 } |
|
908 |
|
909 v.env[key] = envkey |
|
910 |
|
911 return nil |
|
912 } |
|
913 |
|
914 // Given a key, find the value. |
|
915 // Viper will check in the following order: |
|
916 // flag, env, config file, key/value store, default. |
|
917 // Viper will check to see if an alias exists first. |
|
918 // Note: this assumes a lower-cased key given. |
|
919 func (v *Viper) find(lcaseKey string) interface{} { |
|
920 |
|
921 var ( |
|
922 val interface{} |
|
923 exists bool |
|
924 path = strings.Split(lcaseKey, v.keyDelim) |
|
925 nested = len(path) > 1 |
|
926 ) |
|
927 |
|
928 // compute the path through the nested maps to the nested value |
|
929 if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" { |
|
930 return nil |
|
931 } |
|
932 |
|
933 // if the requested key is an alias, then return the proper key |
|
934 lcaseKey = v.realKey(lcaseKey) |
|
935 path = strings.Split(lcaseKey, v.keyDelim) |
|
936 nested = len(path) > 1 |
|
937 |
|
938 // Set() override first |
|
939 val = v.searchMap(v.override, path) |
|
940 if val != nil { |
|
941 return val |
|
942 } |
|
943 if nested && v.isPathShadowedInDeepMap(path, v.override) != "" { |
|
944 return nil |
|
945 } |
|
946 |
|
947 // PFlag override next |
|
948 flag, exists := v.pflags[lcaseKey] |
|
949 if exists && flag.HasChanged() { |
|
950 switch flag.ValueType() { |
|
951 case "int", "int8", "int16", "int32", "int64": |
|
952 return cast.ToInt(flag.ValueString()) |
|
953 case "bool": |
|
954 return cast.ToBool(flag.ValueString()) |
|
955 case "stringSlice": |
|
956 s := strings.TrimPrefix(flag.ValueString(), "[") |
|
957 s = strings.TrimSuffix(s, "]") |
|
958 res, _ := readAsCSV(s) |
|
959 return res |
|
960 default: |
|
961 return flag.ValueString() |
|
962 } |
|
963 } |
|
964 if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" { |
|
965 return nil |
|
966 } |
|
967 |
|
968 // Env override next |
|
969 if v.automaticEnvApplied { |
|
970 // even if it hasn't been registered, if automaticEnv is used, |
|
971 // check any Get request |
|
972 if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" { |
|
973 return val |
|
974 } |
|
975 if nested && v.isPathShadowedInAutoEnv(path) != "" { |
|
976 return nil |
|
977 } |
|
978 } |
|
979 envkey, exists := v.env[lcaseKey] |
|
980 if exists { |
|
981 if val = v.getEnv(envkey); val != "" { |
|
982 return val |
|
983 } |
|
984 } |
|
985 if nested && v.isPathShadowedInFlatMap(path, v.env) != "" { |
|
986 return nil |
|
987 } |
|
988 |
|
989 // Config file next |
|
990 val = v.searchMapWithPathPrefixes(v.config, path) |
|
991 if val != nil { |
|
992 return val |
|
993 } |
|
994 if nested && v.isPathShadowedInDeepMap(path, v.config) != "" { |
|
995 return nil |
|
996 } |
|
997 |
|
998 // K/V store next |
|
999 val = v.searchMap(v.kvstore, path) |
|
1000 if val != nil { |
|
1001 return val |
|
1002 } |
|
1003 if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" { |
|
1004 return nil |
|
1005 } |
|
1006 |
|
1007 // Default next |
|
1008 val = v.searchMap(v.defaults, path) |
|
1009 if val != nil { |
|
1010 return val |
|
1011 } |
|
1012 if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" { |
|
1013 return nil |
|
1014 } |
|
1015 |
|
1016 // last chance: if no other value is returned and a flag does exist for the value, |
|
1017 // get the flag's value even if the flag's value has not changed |
|
1018 if flag, exists := v.pflags[lcaseKey]; exists { |
|
1019 switch flag.ValueType() { |
|
1020 case "int", "int8", "int16", "int32", "int64": |
|
1021 return cast.ToInt(flag.ValueString()) |
|
1022 case "bool": |
|
1023 return cast.ToBool(flag.ValueString()) |
|
1024 case "stringSlice": |
|
1025 s := strings.TrimPrefix(flag.ValueString(), "[") |
|
1026 s = strings.TrimSuffix(s, "]") |
|
1027 res, _ := readAsCSV(s) |
|
1028 return res |
|
1029 default: |
|
1030 return flag.ValueString() |
|
1031 } |
|
1032 } |
|
1033 // last item, no need to check shadowing |
|
1034 |
|
1035 return nil |
|
1036 } |
|
1037 |
|
1038 func readAsCSV(val string) ([]string, error) { |
|
1039 if val == "" { |
|
1040 return []string{}, nil |
|
1041 } |
|
1042 stringReader := strings.NewReader(val) |
|
1043 csvReader := csv.NewReader(stringReader) |
|
1044 return csvReader.Read() |
|
1045 } |
|
1046 |
|
1047 // IsSet checks to see if the key has been set in any of the data locations. |
|
1048 // IsSet is case-insensitive for a key. |
|
1049 func IsSet(key string) bool { return v.IsSet(key) } |
|
1050 func (v *Viper) IsSet(key string) bool { |
|
1051 lcaseKey := strings.ToLower(key) |
|
1052 val := v.find(lcaseKey) |
|
1053 return val != nil |
|
1054 } |
|
1055 |
|
1056 // AutomaticEnv has Viper check ENV variables for all. |
|
1057 // keys set in config, default & flags |
|
1058 func AutomaticEnv() { v.AutomaticEnv() } |
|
1059 func (v *Viper) AutomaticEnv() { |
|
1060 v.automaticEnvApplied = true |
|
1061 } |
|
1062 |
|
1063 // SetEnvKeyReplacer sets the strings.Replacer on the viper object |
|
1064 // Useful for mapping an environmental variable to a key that does |
|
1065 // not match it. |
|
1066 func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) } |
|
1067 func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) { |
|
1068 v.envKeyReplacer = r |
|
1069 } |
|
1070 |
|
1071 // Aliases provide another accessor for the same key. |
|
1072 // This enables one to change a name without breaking the application |
|
1073 func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) } |
|
1074 func (v *Viper) RegisterAlias(alias string, key string) { |
|
1075 v.registerAlias(alias, strings.ToLower(key)) |
|
1076 } |
|
1077 |
|
1078 func (v *Viper) registerAlias(alias string, key string) { |
|
1079 alias = strings.ToLower(alias) |
|
1080 if alias != key && alias != v.realKey(key) { |
|
1081 _, exists := v.aliases[alias] |
|
1082 |
|
1083 if !exists { |
|
1084 // if we alias something that exists in one of the maps to another |
|
1085 // name, we'll never be able to get that value using the original |
|
1086 // name, so move the config value to the new realkey. |
|
1087 if val, ok := v.config[alias]; ok { |
|
1088 delete(v.config, alias) |
|
1089 v.config[key] = val |
|
1090 } |
|
1091 if val, ok := v.kvstore[alias]; ok { |
|
1092 delete(v.kvstore, alias) |
|
1093 v.kvstore[key] = val |
|
1094 } |
|
1095 if val, ok := v.defaults[alias]; ok { |
|
1096 delete(v.defaults, alias) |
|
1097 v.defaults[key] = val |
|
1098 } |
|
1099 if val, ok := v.override[alias]; ok { |
|
1100 delete(v.override, alias) |
|
1101 v.override[key] = val |
|
1102 } |
|
1103 v.aliases[alias] = key |
|
1104 } |
|
1105 } else { |
|
1106 jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key)) |
|
1107 } |
|
1108 } |
|
1109 |
|
1110 func (v *Viper) realKey(key string) string { |
|
1111 newkey, exists := v.aliases[key] |
|
1112 if exists { |
|
1113 jww.DEBUG.Println("Alias", key, "to", newkey) |
|
1114 return v.realKey(newkey) |
|
1115 } |
|
1116 return key |
|
1117 } |
|
1118 |
|
1119 // InConfig checks to see if the given key (or an alias) is in the config file. |
|
1120 func InConfig(key string) bool { return v.InConfig(key) } |
|
1121 func (v *Viper) InConfig(key string) bool { |
|
1122 // if the requested key is an alias, then return the proper key |
|
1123 key = v.realKey(key) |
|
1124 |
|
1125 _, exists := v.config[key] |
|
1126 return exists |
|
1127 } |
|
1128 |
|
1129 // SetDefault sets the default value for this key. |
|
1130 // SetDefault is case-insensitive for a key. |
|
1131 // Default only used when no value is provided by the user via flag, config or ENV. |
|
1132 func SetDefault(key string, value interface{}) { v.SetDefault(key, value) } |
|
1133 func (v *Viper) SetDefault(key string, value interface{}) { |
|
1134 // If alias passed in, then set the proper default |
|
1135 key = v.realKey(strings.ToLower(key)) |
|
1136 value = toCaseInsensitiveValue(value) |
|
1137 |
|
1138 path := strings.Split(key, v.keyDelim) |
|
1139 lastKey := strings.ToLower(path[len(path)-1]) |
|
1140 deepestMap := deepSearch(v.defaults, path[0:len(path)-1]) |
|
1141 |
|
1142 // set innermost value |
|
1143 deepestMap[lastKey] = value |
|
1144 } |
|
1145 |
|
1146 // Set sets the value for the key in the override register. |
|
1147 // Set is case-insensitive for a key. |
|
1148 // Will be used instead of values obtained via |
|
1149 // flags, config file, ENV, default, or key/value store. |
|
1150 func Set(key string, value interface{}) { v.Set(key, value) } |
|
1151 func (v *Viper) Set(key string, value interface{}) { |
|
1152 // If alias passed in, then set the proper override |
|
1153 key = v.realKey(strings.ToLower(key)) |
|
1154 value = toCaseInsensitiveValue(value) |
|
1155 |
|
1156 path := strings.Split(key, v.keyDelim) |
|
1157 lastKey := strings.ToLower(path[len(path)-1]) |
|
1158 deepestMap := deepSearch(v.override, path[0:len(path)-1]) |
|
1159 |
|
1160 // set innermost value |
|
1161 deepestMap[lastKey] = value |
|
1162 } |
|
1163 |
|
1164 // ReadInConfig will discover and load the configuration file from disk |
|
1165 // and key/value stores, searching in one of the defined paths. |
|
1166 func ReadInConfig() error { return v.ReadInConfig() } |
|
1167 func (v *Viper) ReadInConfig() error { |
|
1168 jww.INFO.Println("Attempting to read in config file") |
|
1169 filename, err := v.getConfigFile() |
|
1170 if err != nil { |
|
1171 return err |
|
1172 } |
|
1173 |
|
1174 if !stringInSlice(v.getConfigType(), SupportedExts) { |
|
1175 return UnsupportedConfigError(v.getConfigType()) |
|
1176 } |
|
1177 |
|
1178 jww.DEBUG.Println("Reading file: ", filename) |
|
1179 file, err := afero.ReadFile(v.fs, filename) |
|
1180 if err != nil { |
|
1181 return err |
|
1182 } |
|
1183 |
|
1184 config := make(map[string]interface{}) |
|
1185 |
|
1186 err = v.unmarshalReader(bytes.NewReader(file), config) |
|
1187 if err != nil { |
|
1188 return err |
|
1189 } |
|
1190 |
|
1191 v.config = config |
|
1192 return nil |
|
1193 } |
|
1194 |
|
1195 // MergeInConfig merges a new configuration with an existing config. |
|
1196 func MergeInConfig() error { return v.MergeInConfig() } |
|
1197 func (v *Viper) MergeInConfig() error { |
|
1198 jww.INFO.Println("Attempting to merge in config file") |
|
1199 filename, err := v.getConfigFile() |
|
1200 if err != nil { |
|
1201 return err |
|
1202 } |
|
1203 |
|
1204 if !stringInSlice(v.getConfigType(), SupportedExts) { |
|
1205 return UnsupportedConfigError(v.getConfigType()) |
|
1206 } |
|
1207 |
|
1208 file, err := afero.ReadFile(v.fs, filename) |
|
1209 if err != nil { |
|
1210 return err |
|
1211 } |
|
1212 |
|
1213 return v.MergeConfig(bytes.NewReader(file)) |
|
1214 } |
|
1215 |
|
1216 // ReadConfig will read a configuration file, setting existing keys to nil if the |
|
1217 // key does not exist in the file. |
|
1218 func ReadConfig(in io.Reader) error { return v.ReadConfig(in) } |
|
1219 func (v *Viper) ReadConfig(in io.Reader) error { |
|
1220 v.config = make(map[string]interface{}) |
|
1221 return v.unmarshalReader(in, v.config) |
|
1222 } |
|
1223 |
|
1224 // MergeConfig merges a new configuration with an existing config. |
|
1225 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } |
|
1226 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{}) |
|
1231 if err := v.unmarshalReader(in, cfg); err != nil { |
|
1232 return err |
|
1233 } |
|
1234 mergeMaps(cfg, v.config, nil) |
|
1235 return nil |
|
1236 } |
|
1237 |
|
1238 // WriteConfig writes the current configuration to a file. |
|
1239 func WriteConfig() error { return v.WriteConfig() } |
|
1240 func (v *Viper) WriteConfig() error { |
|
1241 filename, err := v.getConfigFile() |
|
1242 if err != nil { |
|
1243 return err |
|
1244 } |
|
1245 return v.writeConfig(filename, true) |
|
1246 } |
|
1247 |
|
1248 // SafeWriteConfig writes current configuration to file only if the file does not exist. |
|
1249 func SafeWriteConfig() error { return v.SafeWriteConfig() } |
|
1250 func (v *Viper) SafeWriteConfig() error { |
|
1251 filename, err := v.getConfigFile() |
|
1252 if err != nil { |
|
1253 return err |
|
1254 } |
|
1255 return v.writeConfig(filename, false) |
|
1256 } |
|
1257 |
|
1258 // WriteConfigAs writes current configuration to a given filename. |
|
1259 func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } |
|
1260 func (v *Viper) WriteConfigAs(filename string) error { |
|
1261 return v.writeConfig(filename, true) |
|
1262 } |
|
1263 |
|
1264 // SafeWriteConfigAs writes current configuration to a given filename if it does not exist. |
|
1265 func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } |
|
1266 func (v *Viper) SafeWriteConfigAs(filename string) error { |
|
1267 return v.writeConfig(filename, false) |
|
1268 } |
|
1269 |
|
1270 func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) } |
|
1271 func (v *Viper) writeConfig(filename string, force bool) error { |
|
1272 jww.INFO.Println("Attempting to write configuration to file.") |
|
1273 ext := filepath.Ext(filename) |
|
1274 if len(ext) <= 1 { |
|
1275 return fmt.Errorf("Filename: %s requires valid extension.", filename) |
|
1276 } |
|
1277 configType := ext[1:] |
|
1278 if !stringInSlice(configType, SupportedExts) { |
|
1279 return UnsupportedConfigError(configType) |
|
1280 } |
|
1281 if v.config == nil { |
|
1282 v.config = make(map[string]interface{}) |
|
1283 } |
|
1284 var flags int |
|
1285 if force == true { |
|
1286 flags = os.O_CREATE | os.O_TRUNC | os.O_WRONLY |
|
1287 } else { |
|
1288 if _, err := os.Stat(filename); os.IsNotExist(err) { |
|
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 { |
|
1296 return err |
|
1297 } |
|
1298 return v.marshalWriter(f, configType) |
|
1299 } |
|
1300 |
|
1301 // Unmarshal a Reader into a map. |
|
1302 // Should probably be an unexported function. |
|
1303 func unmarshalReader(in io.Reader, c map[string]interface{}) error { |
|
1304 return v.unmarshalReader(in, c) |
|
1305 } |
|
1306 func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { |
|
1307 buf := new(bytes.Buffer) |
|
1308 buf.ReadFrom(in) |
|
1309 |
|
1310 switch strings.ToLower(v.getConfigType()) { |
|
1311 case "yaml", "yml": |
|
1312 if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil { |
|
1313 return ConfigParseError{err} |
|
1314 } |
|
1315 |
|
1316 case "json": |
|
1317 if err := json.Unmarshal(buf.Bytes(), &c); err != nil { |
|
1318 return ConfigParseError{err} |
|
1319 } |
|
1320 |
|
1321 case "hcl": |
|
1322 obj, err := hcl.Parse(string(buf.Bytes())) |
|
1323 if err != nil { |
|
1324 return ConfigParseError{err} |
|
1325 } |
|
1326 if err = hcl.DecodeObject(&c, obj); err != nil { |
|
1327 return ConfigParseError{err} |
|
1328 } |
|
1329 |
|
1330 case "toml": |
|
1331 tree, err := toml.LoadReader(buf) |
|
1332 if err != nil { |
|
1333 return ConfigParseError{err} |
|
1334 } |
|
1335 tmap := tree.ToMap() |
|
1336 for k, v := range tmap { |
|
1337 c[k] = v |
|
1338 } |
|
1339 |
|
1340 case "properties", "props", "prop": |
|
1341 v.properties = properties.NewProperties() |
|
1342 var err error |
|
1343 if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil { |
|
1344 return ConfigParseError{err} |
|
1345 } |
|
1346 for _, key := range v.properties.Keys() { |
|
1347 value, _ := v.properties.Get(key) |
|
1348 // recursively build nested maps |
|
1349 path := strings.Split(key, ".") |
|
1350 lastKey := strings.ToLower(path[len(path)-1]) |
|
1351 deepestMap := deepSearch(c, path[0:len(path)-1]) |
|
1352 // set innermost value |
|
1353 deepestMap[lastKey] = value |
|
1354 } |
|
1355 } |
|
1356 |
|
1357 insensitiviseMap(c) |
|
1358 return nil |
|
1359 } |
|
1360 |
|
1361 // 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 { |
|
1366 c := v.AllSettings() |
|
1367 switch configType { |
|
1368 case "json": |
|
1369 b, err := json.MarshalIndent(c, "", " ") |
|
1370 if err != nil { |
|
1371 return ConfigMarshalError{err} |
|
1372 } |
|
1373 _, err = f.WriteString(string(b)) |
|
1374 if err != nil { |
|
1375 return ConfigMarshalError{err} |
|
1376 } |
|
1377 |
|
1378 case "hcl": |
|
1379 b, err := json.Marshal(c) |
|
1380 ast, err := hcl.Parse(string(b)) |
|
1381 if err != nil { |
|
1382 return ConfigMarshalError{err} |
|
1383 } |
|
1384 err = printer.Fprint(f, ast.Node) |
|
1385 if err != nil { |
|
1386 return ConfigMarshalError{err} |
|
1387 } |
|
1388 |
|
1389 case "prop", "props", "properties": |
|
1390 if v.properties == nil { |
|
1391 v.properties = properties.NewProperties() |
|
1392 } |
|
1393 p := v.properties |
|
1394 for _, key := range v.AllKeys() { |
|
1395 _, _, err := p.Set(key, v.GetString(key)) |
|
1396 if err != nil { |
|
1397 return ConfigMarshalError{err} |
|
1398 } |
|
1399 } |
|
1400 _, err := p.WriteComment(f, "#", properties.UTF8) |
|
1401 if err != nil { |
|
1402 return ConfigMarshalError{err} |
|
1403 } |
|
1404 |
|
1405 case "toml": |
|
1406 t, err := toml.TreeFromMap(c) |
|
1407 if err != nil { |
|
1408 return ConfigMarshalError{err} |
|
1409 } |
|
1410 s := t.String() |
|
1411 if _, err := f.WriteString(s); err != nil { |
|
1412 return ConfigMarshalError{err} |
|
1413 } |
|
1414 |
|
1415 case "yaml", "yml": |
|
1416 b, err := yaml.Marshal(c) |
|
1417 if err != nil { |
|
1418 return ConfigMarshalError{err} |
|
1419 } |
|
1420 if _, err = f.WriteString(string(b)); err != nil { |
|
1421 return ConfigMarshalError{err} |
|
1422 } |
|
1423 } |
|
1424 return nil |
|
1425 } |
|
1426 |
|
1427 func keyExists(k string, m map[string]interface{}) string { |
|
1428 lk := strings.ToLower(k) |
|
1429 for mk := range m { |
|
1430 lmk := strings.ToLower(mk) |
|
1431 if lmk == lk { |
|
1432 return mk |
|
1433 } |
|
1434 } |
|
1435 return "" |
|
1436 } |
|
1437 |
|
1438 func castToMapStringInterface( |
|
1439 src map[interface{}]interface{}) map[string]interface{} { |
|
1440 tgt := map[string]interface{}{} |
|
1441 for k, v := range src { |
|
1442 tgt[fmt.Sprintf("%v", k)] = v |
|
1443 } |
|
1444 return tgt |
|
1445 } |
|
1446 |
|
1447 func castMapStringToMapInterface(src map[string]string) map[string]interface{} { |
|
1448 tgt := map[string]interface{}{} |
|
1449 for k, v := range src { |
|
1450 tgt[k] = v |
|
1451 } |
|
1452 return tgt |
|
1453 } |
|
1454 |
|
1455 func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} { |
|
1456 tgt := map[string]interface{}{} |
|
1457 for k, v := range src { |
|
1458 tgt[k] = v |
|
1459 } |
|
1460 return tgt |
|
1461 } |
|
1462 |
|
1463 // mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's |
|
1464 // insistence on parsing nested structures as `map[interface{}]interface{}` |
|
1465 // instead of using a `string` as the key for nest structures beyond one level |
|
1466 // deep. Both map types are supported as there is a go-yaml fork that uses |
|
1467 // `map[string]interface{}` instead. |
|
1468 func mergeMaps( |
|
1469 src, tgt map[string]interface{}, itgt map[interface{}]interface{}) { |
|
1470 for sk, sv := range src { |
|
1471 tk := keyExists(sk, tgt) |
|
1472 if tk == "" { |
|
1473 jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv) |
|
1474 tgt[sk] = sv |
|
1475 if itgt != nil { |
|
1476 itgt[sk] = sv |
|
1477 } |
|
1478 continue |
|
1479 } |
|
1480 |
|
1481 tv, ok := tgt[tk] |
|
1482 if !ok { |
|
1483 jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv) |
|
1484 tgt[sk] = sv |
|
1485 if itgt != nil { |
|
1486 itgt[sk] = sv |
|
1487 } |
|
1488 continue |
|
1489 } |
|
1490 |
|
1491 svType := reflect.TypeOf(sv) |
|
1492 tvType := reflect.TypeOf(tv) |
|
1493 if svType != tvType { |
|
1494 jww.ERROR.Printf( |
|
1495 "svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v", |
|
1496 sk, svType, tvType, sv, tv) |
|
1497 continue |
|
1498 } |
|
1499 |
|
1500 jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v", |
|
1501 sk, svType, tvType, sv, tv) |
|
1502 |
|
1503 switch ttv := tv.(type) { |
|
1504 case map[interface{}]interface{}: |
|
1505 jww.TRACE.Printf("merging maps (must convert)") |
|
1506 tsv := sv.(map[interface{}]interface{}) |
|
1507 ssv := castToMapStringInterface(tsv) |
|
1508 stv := castToMapStringInterface(ttv) |
|
1509 mergeMaps(ssv, stv, ttv) |
|
1510 case map[string]interface{}: |
|
1511 jww.TRACE.Printf("merging maps") |
|
1512 mergeMaps(sv.(map[string]interface{}), ttv, nil) |
|
1513 default: |
|
1514 jww.TRACE.Printf("setting value") |
|
1515 tgt[tk] = sv |
|
1516 if itgt != nil { |
|
1517 itgt[tk] = sv |
|
1518 } |
|
1519 } |
|
1520 } |
|
1521 } |
|
1522 |
|
1523 // ReadRemoteConfig attempts to get configuration from a remote source |
|
1524 // and read it in the remote configuration registry. |
|
1525 func ReadRemoteConfig() error { return v.ReadRemoteConfig() } |
|
1526 func (v *Viper) ReadRemoteConfig() error { |
|
1527 return v.getKeyValueConfig() |
|
1528 } |
|
1529 |
|
1530 func WatchRemoteConfig() error { return v.WatchRemoteConfig() } |
|
1531 func (v *Viper) WatchRemoteConfig() error { |
|
1532 return v.watchKeyValueConfig() |
|
1533 } |
|
1534 |
|
1535 func (v *Viper) WatchRemoteConfigOnChannel() error { |
|
1536 return v.watchKeyValueConfigOnChannel() |
|
1537 } |
|
1538 |
|
1539 func (v *Viper) insensitiviseMaps() { |
|
1540 insensitiviseMap(v.config) |
|
1541 insensitiviseMap(v.defaults) |
|
1542 insensitiviseMap(v.override) |
|
1543 insensitiviseMap(v.kvstore) |
|
1544 } |
|
1545 |
|
1546 // Retrieve the first found remote configuration. |
|
1547 func (v *Viper) getKeyValueConfig() error { |
|
1548 if RemoteConfig == nil { |
|
1549 return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'") |
|
1550 } |
|
1551 |
|
1552 for _, rp := range v.remoteProviders { |
|
1553 val, err := v.getRemoteConfig(rp) |
|
1554 if err != nil { |
|
1555 continue |
|
1556 } |
|
1557 v.kvstore = val |
|
1558 return nil |
|
1559 } |
|
1560 return RemoteConfigError("No Files Found") |
|
1561 } |
|
1562 |
|
1563 func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { |
|
1564 reader, err := RemoteConfig.Get(provider) |
|
1565 if err != nil { |
|
1566 return nil, err |
|
1567 } |
|
1568 err = v.unmarshalReader(reader, v.kvstore) |
|
1569 return v.kvstore, err |
|
1570 } |
|
1571 |
|
1572 // Retrieve the first found remote configuration. |
|
1573 func (v *Viper) watchKeyValueConfigOnChannel() error { |
|
1574 for _, rp := range v.remoteProviders { |
|
1575 respc, _ := RemoteConfig.WatchChannel(rp) |
|
1576 //Todo: Add quit channel |
|
1577 go func(rc <-chan *RemoteResponse) { |
|
1578 for { |
|
1579 b := <-rc |
|
1580 reader := bytes.NewReader(b.Value) |
|
1581 v.unmarshalReader(reader, v.kvstore) |
|
1582 } |
|
1583 }(respc) |
|
1584 return nil |
|
1585 } |
|
1586 return RemoteConfigError("No Files Found") |
|
1587 } |
|
1588 |
|
1589 // Retrieve the first found remote configuration. |
|
1590 func (v *Viper) watchKeyValueConfig() error { |
|
1591 for _, rp := range v.remoteProviders { |
|
1592 val, err := v.watchRemoteConfig(rp) |
|
1593 if err != nil { |
|
1594 continue |
|
1595 } |
|
1596 v.kvstore = val |
|
1597 return nil |
|
1598 } |
|
1599 return RemoteConfigError("No Files Found") |
|
1600 } |
|
1601 |
|
1602 func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { |
|
1603 reader, err := RemoteConfig.Watch(provider) |
|
1604 if err != nil { |
|
1605 return nil, err |
|
1606 } |
|
1607 err = v.unmarshalReader(reader, v.kvstore) |
|
1608 return v.kvstore, err |
|
1609 } |
|
1610 |
|
1611 // AllKeys returns all keys holding a value, regardless of where they are set. |
|
1612 // Nested keys are returned with a v.keyDelim (= ".") separator |
|
1613 func AllKeys() []string { return v.AllKeys() } |
|
1614 func (v *Viper) AllKeys() []string { |
|
1615 m := map[string]bool{} |
|
1616 // add all paths, by order of descending priority to ensure correct shadowing |
|
1617 m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "") |
|
1618 m = v.flattenAndMergeMap(m, v.override, "") |
|
1619 m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags)) |
|
1620 m = v.mergeFlatMap(m, castMapStringToMapInterface(v.env)) |
|
1621 m = v.flattenAndMergeMap(m, v.config, "") |
|
1622 m = v.flattenAndMergeMap(m, v.kvstore, "") |
|
1623 m = v.flattenAndMergeMap(m, v.defaults, "") |
|
1624 |
|
1625 // convert set of paths to list |
|
1626 a := []string{} |
|
1627 for x := range m { |
|
1628 a = append(a, x) |
|
1629 } |
|
1630 return a |
|
1631 } |
|
1632 |
|
1633 // 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): |
|
1635 // - 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, |
|
1637 // it is skipped. |
|
1638 // 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 { |
|
1640 if shadow != nil && prefix != "" && shadow[prefix] { |
|
1641 // prefix is shadowed => nothing more to flatten |
|
1642 return shadow |
|
1643 } |
|
1644 if shadow == nil { |
|
1645 shadow = make(map[string]bool) |
|
1646 } |
|
1647 |
|
1648 var m2 map[string]interface{} |
|
1649 if prefix != "" { |
|
1650 prefix += v.keyDelim |
|
1651 } |
|
1652 for k, val := range m { |
|
1653 fullKey := prefix + k |
|
1654 switch val.(type) { |
|
1655 case map[string]interface{}: |
|
1656 m2 = val.(map[string]interface{}) |
|
1657 case map[interface{}]interface{}: |
|
1658 m2 = cast.ToStringMap(val) |
|
1659 default: |
|
1660 // immediate value |
|
1661 shadow[strings.ToLower(fullKey)] = true |
|
1662 continue |
|
1663 } |
|
1664 // recursively merge to shadow map |
|
1665 shadow = v.flattenAndMergeMap(shadow, m2, fullKey) |
|
1666 } |
|
1667 return shadow |
|
1668 } |
|
1669 |
|
1670 // mergeFlatMap merges the given maps, excluding values of the second map |
|
1671 // shadowed by values from the first map. |
|
1672 func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool { |
|
1673 // scan keys |
|
1674 outer: |
|
1675 for k, _ := range m { |
|
1676 path := strings.Split(k, v.keyDelim) |
|
1677 // scan intermediate paths |
|
1678 var parentKey string |
|
1679 for i := 1; i < len(path); i++ { |
|
1680 parentKey = strings.Join(path[0:i], v.keyDelim) |
|
1681 if shadow[parentKey] { |
|
1682 // path is shadowed, continue |
|
1683 continue outer |
|
1684 } |
|
1685 } |
|
1686 // add key |
|
1687 shadow[strings.ToLower(k)] = true |
|
1688 } |
|
1689 return shadow |
|
1690 } |
|
1691 |
|
1692 // AllSettings merges all settings and returns them as a map[string]interface{}. |
|
1693 func AllSettings() map[string]interface{} { return v.AllSettings() } |
|
1694 func (v *Viper) AllSettings() map[string]interface{} { |
|
1695 m := map[string]interface{}{} |
|
1696 // start from the list of keys, and construct the map one value at a time |
|
1697 for _, k := range v.AllKeys() { |
|
1698 value := v.Get(k) |
|
1699 if value == nil { |
|
1700 // should not happen, since AllKeys() returns only keys holding a value, |
|
1701 // check just in case anything changes |
|
1702 continue |
|
1703 } |
|
1704 path := strings.Split(k, v.keyDelim) |
|
1705 lastKey := strings.ToLower(path[len(path)-1]) |
|
1706 deepestMap := deepSearch(m, path[0:len(path)-1]) |
|
1707 // set innermost value |
|
1708 deepestMap[lastKey] = value |
|
1709 } |
|
1710 return m |
|
1711 } |
|
1712 |
|
1713 // SetFs sets the filesystem to use to read configuration. |
|
1714 func SetFs(fs afero.Fs) { v.SetFs(fs) } |
|
1715 func (v *Viper) SetFs(fs afero.Fs) { |
|
1716 v.fs = fs |
|
1717 } |
|
1718 |
|
1719 // SetConfigName sets name for the config file. |
|
1720 // Does not include extension. |
|
1721 func SetConfigName(in string) { v.SetConfigName(in) } |
|
1722 func (v *Viper) SetConfigName(in string) { |
|
1723 if in != "" { |
|
1724 v.configName = in |
|
1725 v.configFile = "" |
|
1726 } |
|
1727 } |
|
1728 |
|
1729 // SetConfigType sets the type of the configuration returned by the |
|
1730 // remote source, e.g. "json". |
|
1731 func SetConfigType(in string) { v.SetConfigType(in) } |
|
1732 func (v *Viper) SetConfigType(in string) { |
|
1733 if in != "" { |
|
1734 v.configType = in |
|
1735 } |
|
1736 } |
|
1737 |
|
1738 func (v *Viper) getConfigType() string { |
|
1739 if v.configType != "" { |
|
1740 return v.configType |
|
1741 } |
|
1742 |
|
1743 cf, err := v.getConfigFile() |
|
1744 if err != nil { |
|
1745 return "" |
|
1746 } |
|
1747 |
|
1748 ext := filepath.Ext(cf) |
|
1749 |
|
1750 if len(ext) > 1 { |
|
1751 return ext[1:] |
|
1752 } |
|
1753 |
|
1754 return "" |
|
1755 } |
|
1756 |
|
1757 func (v *Viper) getConfigFile() (string, error) { |
|
1758 if v.configFile == "" { |
|
1759 cf, err := v.findConfigFile() |
|
1760 if err != nil { |
|
1761 return "", err |
|
1762 } |
|
1763 v.configFile = cf |
|
1764 } |
|
1765 return v.configFile, nil |
|
1766 } |
|
1767 |
|
1768 func (v *Viper) searchInPath(in string) (filename string) { |
|
1769 jww.DEBUG.Println("Searching for config in ", in) |
|
1770 for _, ext := range SupportedExts { |
|
1771 jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) |
|
1772 if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { |
|
1773 jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) |
|
1774 return filepath.Join(in, v.configName+"."+ext) |
|
1775 } |
|
1776 } |
|
1777 |
|
1778 return "" |
|
1779 } |
|
1780 |
|
1781 // Search all configPaths for any config file. |
|
1782 // Returns the first path that exists (and is a config file). |
|
1783 func (v *Viper) findConfigFile() (string, error) { |
|
1784 jww.INFO.Println("Searching for config in ", v.configPaths) |
|
1785 |
|
1786 for _, cp := range v.configPaths { |
|
1787 file := v.searchInPath(cp) |
|
1788 if file != "" { |
|
1789 return file, nil |
|
1790 } |
|
1791 } |
|
1792 return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} |
|
1793 } |
|
1794 |
|
1795 // Debug prints all configuration registries for debugging |
|
1796 // purposes. |
|
1797 func Debug() { v.Debug() } |
|
1798 func (v *Viper) Debug() { |
|
1799 fmt.Printf("Aliases:\n%#v\n", v.aliases) |
|
1800 fmt.Printf("Override:\n%#v\n", v.override) |
|
1801 fmt.Printf("PFlags:\n%#v\n", v.pflags) |
|
1802 fmt.Printf("Env:\n%#v\n", v.env) |
|
1803 fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore) |
|
1804 fmt.Printf("Config:\n%#v\n", v.config) |
|
1805 fmt.Printf("Defaults:\n%#v\n", v.defaults) |
|
1806 } |