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