github.com/gozelle/viper@v1.14.0/README.md (about) 1 > ## Viper v2 feedback 2 > Viper is heading towards v2 and we would love to hear what _**you**_ would like to see in it. Share your thoughts here: https://forms.gle/R6faU74qPRPAzchZ9 3 > 4 > **Thank you!** 5 6 ![Viper](.github/logo.png?raw=true) 7 8 [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration) 9 [![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go) 10 11 [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/gozelle/viper/actions?query=workflow%3ACI) 12 [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 13 [![Go Report Card](https://goreportcard.com/badge/github.com/gozelle/viper?style=flat-square)](https://goreportcard.com/report/github.com/gozelle/viper) 14 ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square) 15 [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/gozelle/viper)](https://pkg.go.dev/mod/github.com/gozelle/viper) 16 17 **Go configuration with fangs!** 18 19 Many Go projects are built using Viper including: 20 21 * [Hugo](http://gohugo.io) 22 * [EMC RexRay](http://rexray.readthedocs.org/en/stable/) 23 * [Imgur’s Incus](https://github.com/Imgur/incus) 24 * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) 25 * [Docker Notary](https://github.com/docker/Notary) 26 * [BloomApi](https://www.bloomapi.com/) 27 * [doctl](https://github.com/digitalocean/doctl) 28 * [Clairctl](https://github.com/jgsqware/clairctl) 29 * [Mercure](https://mercure.rocks) 30 31 32 ## Install 33 34 ```shell 35 go get github.com/gozelle/viper 36 ``` 37 38 **Note:** Viper uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies. 39 40 41 ## What is Viper? 42 43 Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed 44 to work within an application, and can handle all types of configuration needs 45 and formats. It supports: 46 47 * setting defaults 48 * reading from JSON, TOML, YAML, HCL, envfile and Java properties config files 49 * live watching and re-reading of config files (optional) 50 * reading from environment variables 51 * reading from remote config systems (etcd or Consul), and watching changes 52 * reading from command line flags 53 * reading from buffer 54 * setting explicit values 55 56 Viper can be thought of as a registry for all of your applications configuration needs. 57 58 59 ## Why Viper? 60 61 When building a modern application, you don’t want to worry about 62 configuration file formats; you want to focus on building awesome software. 63 Viper is here to help with that. 64 65 Viper does the following for you: 66 67 1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, INI, envfile or Java properties formats. 68 2. Provide a mechanism to set default values for your different configuration options. 69 3. Provide a mechanism to set override values for options specified through command line flags. 70 4. Provide an alias system to easily rename parameters without breaking existing code. 71 5. Make it easy to tell the difference between when a user has provided a command line or config file which is the same as the default. 72 73 Viper uses the following precedence order. Each item takes precedence over the item below it: 74 75 * explicit call to `Set` 76 * flag 77 * env 78 * config 79 * key/value store 80 * default 81 82 **Important:** Viper configuration keys are case insensitive. 83 There are ongoing discussions about making that optional. 84 85 86 ## Putting Values into Viper 87 88 ### Establishing Defaults 89 90 A good configuration system will support default values. A default value is not 91 required for a key, but it’s useful in the event that a key hasn't been set via 92 config file, environment variable, remote configuration or flag. 93 94 Examples: 95 96 ```go 97 viper.SetDefault("ContentDir", "content") 98 viper.SetDefault("LayoutDir", "layouts") 99 viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"}) 100 ``` 101 102 ### Reading Config Files 103 104 Viper requires minimal configuration so it knows where to look for config files. 105 Viper supports JSON, TOML, YAML, HCL, INI, envfile and Java Properties files. Viper can search multiple paths, but 106 currently a single Viper instance only supports a single configuration file. 107 Viper does not default to any configuration search paths leaving defaults decision 108 to an application. 109 110 Here is an example of how to use Viper to search for and read a configuration file. 111 None of the specific paths are required, but at least one path should be provided 112 where a configuration file is expected. 113 114 ```go 115 viper.SetConfigName("config") // name of config file (without extension) 116 viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name 117 viper.AddConfigPath("/etc/appname/") // path to look for the config file in 118 viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths 119 viper.AddConfigPath(".") // optionally look for config in the working directory 120 err := viper.ReadInConfig() // Find and read the config file 121 if err != nil { // Handle errors reading the config file 122 panic(fmt.Errorf("fatal error config file: %w", err)) 123 } 124 ``` 125 126 You can handle the specific case where no config file is found like this: 127 128 ```go 129 if err := viper.ReadInConfig(); err != nil { 130 if _, ok := err.(viper.ConfigFileNotFoundError); ok { 131 // Config file not found; ignore error if desired 132 } else { 133 // Config file was found but another error was produced 134 } 135 } 136 137 // Config file found and successfully parsed 138 ``` 139 140 *NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmaticaly. For those configuration files that lie in the home of the user without any extension like `.bashrc` 141 142 ### Writing Config Files 143 144 Reading from config files is useful, but at times you want to store all modifications made at run time. 145 For that, a bunch of commands are available, each with its own purpose: 146 147 * WriteConfig - writes the current viper configuration to the predefined path, if exists. Errors if no predefined path. Will overwrite the current config file, if it exists. 148 * SafeWriteConfig - writes the current viper configuration to the predefined path. Errors if no predefined path. Will not overwrite the current config file, if it exists. 149 * WriteConfigAs - writes the current viper configuration to the given filepath. Will overwrite the given file, if it exists. 150 * SafeWriteConfigAs - writes the current viper configuration to the given filepath. Will not overwrite the given file, if it exists. 151 152 As a rule of the thumb, everything marked with safe won't overwrite any file, but just create if not existent, whilst the default behavior is to create or truncate. 153 154 A small examples section: 155 156 ```go 157 viper.WriteConfig() // writes current config to predefined path set by 'viper.AddConfigPath()' and 'viper.SetConfigName' 158 viper.SafeWriteConfig() 159 viper.WriteConfigAs("/path/to/my/.config") 160 viper.SafeWriteConfigAs("/path/to/my/.config") // will error since it has already been written 161 viper.SafeWriteConfigAs("/path/to/my/.other_config") 162 ``` 163 164 ### Watching and re-reading config files 165 166 Viper supports the ability to have your application live read a config file while running. 167 168 Gone are the days of needing to restart a server to have a config take effect, 169 viper powered applications can read an update to a config file while running and 170 not miss a beat. 171 172 Simply tell the viper instance to watchConfig. 173 Optionally you can provide a function for Viper to run each time a change occurs. 174 175 **Make sure you add all of the configPaths prior to calling `WatchConfig()`** 176 177 ```go 178 viper.OnConfigChange(func(e fsnotify.Event) { 179 fmt.Println("Config file changed:", e.Name) 180 }) 181 viper.WatchConfig() 182 ``` 183 184 ### Reading Config from io.Reader 185 186 Viper predefines many configuration sources such as files, environment 187 variables, flags, and remote K/V store, but you are not bound to them. You can 188 also implement your own required configuration source and feed it to viper. 189 190 ```go 191 viper.SetConfigType("yaml") // or viper.SetConfigType("YAML") 192 193 // any approach to require this configuration into your program. 194 var yamlExample = []byte(` 195 Hacker: true 196 name: steve 197 hobbies: 198 - skateboarding 199 - snowboarding 200 - go 201 clothing: 202 jacket: leather 203 trousers: denim 204 age: 35 205 eyes : brown 206 beard: true 207 `) 208 209 viper.ReadConfig(bytes.NewBuffer(yamlExample)) 210 211 viper.Get("name") // this would be "steve" 212 ``` 213 214 ### Setting Overrides 215 216 These could be from a command line flag, or from your own application logic. 217 218 ```go 219 viper.Set("Verbose", true) 220 viper.Set("LogFile", LogFile) 221 ``` 222 223 ### Registering and Using Aliases 224 225 Aliases permit a single value to be referenced by multiple keys 226 227 ```go 228 viper.RegisterAlias("loud", "Verbose") 229 230 viper.Set("verbose", true) // same result as next line 231 viper.Set("loud", true) // same result as prior line 232 233 viper.GetBool("loud") // true 234 viper.GetBool("verbose") // true 235 ``` 236 237 ### Working with Environment Variables 238 239 Viper has full support for environment variables. This enables 12 factor 240 applications out of the box. There are five methods that exist to aid working 241 with ENV: 242 243 * `AutomaticEnv()` 244 * `BindEnv(string...) : error` 245 * `SetEnvPrefix(string)` 246 * `SetEnvKeyReplacer(string...) *strings.Replacer` 247 * `AllowEmptyEnv(bool)` 248 249 _When working with ENV variables, it’s important to recognize that Viper 250 treats ENV variables as case sensitive._ 251 252 Viper provides a mechanism to try to ensure that ENV variables are unique. By 253 using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from 254 the environment variables. Both `BindEnv` and `AutomaticEnv` will use this 255 prefix. 256 257 `BindEnv` takes one or more parameters. The first parameter is the key name, the 258 rest are the name of the environment variables to bind to this key. If more than 259 one are provided, they will take precedence in the specified order. The name of 260 the environment variable is case sensitive. If the ENV variable name is not provided, then 261 Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter), 262 it **does not** automatically add the prefix. For example if the second parameter is "id", 263 Viper will look for the ENV variable "ID". 264 265 One important thing to recognize when working with ENV variables is that the 266 value will be read each time it is accessed. Viper does not fix the value when 267 the `BindEnv` is called. 268 269 `AutomaticEnv` is a powerful helper especially when combined with 270 `SetEnvPrefix`. When called, Viper will check for an environment variable any 271 time a `viper.Get` request is made. It will apply the following rules. It will 272 check for an environment variable with a name matching the key uppercased and 273 prefixed with the `EnvPrefix` if set. 274 275 `SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env 276 keys to an extent. This is useful if you want to use `-` or something in your 277 `Get()` calls, but want your environmental variables to use `_` delimiters. An 278 example of using it can be found in `viper_test.go`. 279 280 Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function. 281 Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic. 282 283 By default empty environment variables are considered unset and will fall back to 284 the next configuration source. To treat empty environment variables as set, use 285 the `AllowEmptyEnv` method. 286 287 #### Env example 288 289 ```go 290 SetEnvPrefix("spf") // will be uppercased automatically 291 BindEnv("id") 292 293 os.Setenv("SPF_ID", "13") // typically done outside of the app 294 295 id := Get("id") // 13 296 ``` 297 298 ### Working with Flags 299 300 Viper has the ability to bind to flags. Specifically, Viper supports `Pflags` 301 as used in the [Cobra](https://github.com/spf13/cobra) library. 302 303 Like `BindEnv`, the value is not set when the binding method is called, but when 304 it is accessed. This means you can bind as early as you want, even in an 305 `init()` function. 306 307 For individual flags, the `BindPFlag()` method provides this functionality. 308 309 Example: 310 311 ```go 312 serverCmd.Flags().Int("port", 1138, "Port to run Application server on") 313 viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) 314 ``` 315 316 You can also bind an existing set of pflags (pflag.FlagSet): 317 318 Example: 319 320 ```go 321 pflag.Int("flagname", 1234, "help message for flagname") 322 323 pflag.Parse() 324 viper.BindPFlags(pflag.CommandLine) 325 326 i := viper.GetInt("flagname") // retrieve values from viper instead of pflag 327 ``` 328 329 The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude 330 the use of other packages that use the [flag](https://golang.org/pkg/flag/) 331 package from the standard library. The pflag package can handle the flags 332 defined for the flag package by importing these flags. This is accomplished 333 by a calling a convenience function provided by the pflag package called 334 AddGoFlagSet(). 335 336 Example: 337 338 ```go 339 package main 340 341 import ( 342 "flag" 343 "github.com/spf13/pflag" 344 ) 345 346 func main() { 347 348 // using standard library "flag" package 349 flag.Int("flagname", 1234, "help message for flagname") 350 351 pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 352 pflag.Parse() 353 viper.BindPFlags(pflag.CommandLine) 354 355 i := viper.GetInt("flagname") // retrieve value from viper 356 357 // ... 358 } 359 ``` 360 361 #### Flag interfaces 362 363 Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`. 364 365 `FlagValue` represents a single flag. This is a very simple example on how to implement this interface: 366 367 ```go 368 type myFlag struct {} 369 func (f myFlag) HasChanged() bool { return false } 370 func (f myFlag) Name() string { return "my-flag-name" } 371 func (f myFlag) ValueString() string { return "my-flag-value" } 372 func (f myFlag) ValueType() string { return "string" } 373 ``` 374 375 Once your flag implements this interface, you can simply tell Viper to bind it: 376 377 ```go 378 viper.BindFlagValue("my-flag-name", myFlag{}) 379 ``` 380 381 `FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface: 382 383 ```go 384 type myFlagSet struct { 385 flags []myFlag 386 } 387 388 func (f myFlagSet) VisitAll(fn func(FlagValue)) { 389 for _, flag := range flags { 390 fn(flag) 391 } 392 } 393 ``` 394 395 Once your flag set implements this interface, you can simply tell Viper to bind it: 396 397 ```go 398 fSet := myFlagSet{ 399 flags: []myFlag{myFlag{}, myFlag{}}, 400 } 401 viper.BindFlagValues("my-flags", fSet) 402 ``` 403 404 ### Remote Key/Value Store Support 405 406 To enable remote support in Viper, do a blank import of the `viper/remote` 407 package: 408 409 `import _ "github.com/gozelle/viper/remote"` 410 411 Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path 412 in a Key/Value store such as etcd or Consul. These values take precedence over 413 default values, but are overridden by configuration values retrieved from disk, 414 flags, or environment variables. 415 416 Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve 417 configuration from the K/V store, which means that you can store your 418 configuration values encrypted and have them automatically decrypted if you have 419 the correct gpg keyring. Encryption is optional. 420 421 You can use remote configuration in conjunction with local configuration, or 422 independently of it. 423 424 `crypt` has a command-line helper that you can use to put configurations in your 425 K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001. 426 427 ```bash 428 $ go get github.com/bketelsen/crypt/bin/crypt 429 $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json 430 ``` 431 432 Confirm that your value was set: 433 434 ```bash 435 $ crypt get -plaintext /config/hugo.json 436 ``` 437 438 See the `crypt` documentation for examples of how to set encrypted values, or 439 how to use Consul. 440 441 ### Remote Key/Value Store Example - Unencrypted 442 443 #### etcd 444 ```go 445 viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") 446 viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 447 err := viper.ReadRemoteConfig() 448 ``` 449 450 #### etcd3 451 ```go 452 viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json") 453 viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 454 err := viper.ReadRemoteConfig() 455 ``` 456 457 #### Consul 458 You need to set a key to Consul key/value storage with JSON value containing your desired config. 459 For example, create a Consul key/value store key `MY_CONSUL_KEY` with value: 460 461 ```json 462 { 463 "port": 8080, 464 "hostname": "myhostname.com" 465 } 466 ``` 467 468 ```go 469 viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY") 470 viper.SetConfigType("json") // Need to explicitly set this to json 471 err := viper.ReadRemoteConfig() 472 473 fmt.Println(viper.Get("port")) // 8080 474 fmt.Println(viper.Get("hostname")) // myhostname.com 475 ``` 476 477 #### Firestore 478 479 ```go 480 viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document") 481 viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml" 482 err := viper.ReadRemoteConfig() 483 ``` 484 485 Of course, you're allowed to use `SecureRemoteProvider` also 486 487 ### Remote Key/Value Store Example - Encrypted 488 489 ```go 490 viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") 491 viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 492 err := viper.ReadRemoteConfig() 493 ``` 494 495 ### Watching Changes in etcd - Unencrypted 496 497 ```go 498 // alternatively, you can create a new viper instance. 499 var runtime_viper = viper.New() 500 501 runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") 502 runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" 503 504 // read from remote config the first time. 505 err := runtime_viper.ReadRemoteConfig() 506 507 // unmarshal config 508 runtime_viper.Unmarshal(&runtime_conf) 509 510 // open a goroutine to watch remote changes forever 511 go func(){ 512 for { 513 time.Sleep(time.Second * 5) // delay after each request 514 515 // currently, only tested with etcd support 516 err := runtime_viper.WatchRemoteConfig() 517 if err != nil { 518 log.Errorf("unable to read remote config: %v", err) 519 continue 520 } 521 522 // unmarshal new config into our runtime config struct. you can also use channel 523 // to implement a signal to notify the system of the changes 524 runtime_viper.Unmarshal(&runtime_conf) 525 } 526 }() 527 ``` 528 529 ## Getting Values From Viper 530 531 In Viper, there are a few ways to get a value depending on the value’s type. 532 The following functions and methods exist: 533 534 * `Get(key string) : interface{}` 535 * `GetBool(key string) : bool` 536 * `GetFloat64(key string) : float64` 537 * `GetInt(key string) : int` 538 * `GetIntSlice(key string) : []int` 539 * `GetString(key string) : string` 540 * `GetStringMap(key string) : map[string]interface{}` 541 * `GetStringMapString(key string) : map[string]string` 542 * `GetStringSlice(key string) : []string` 543 * `GetTime(key string) : time.Time` 544 * `GetDuration(key string) : time.Duration` 545 * `IsSet(key string) : bool` 546 * `AllSettings() : map[string]interface{}` 547 548 One important thing to recognize is that each Get function will return a zero 549 value if it’s not found. To check if a given key exists, the `IsSet()` method 550 has been provided. 551 552 Example: 553 ```go 554 viper.GetString("logfile") // case-insensitive Setting & Getting 555 if viper.GetBool("verbose") { 556 fmt.Println("verbose enabled") 557 } 558 ``` 559 ### Accessing nested keys 560 561 The accessor methods also accept formatted paths to deeply nested keys. For 562 example, if the following JSON file is loaded: 563 564 ```json 565 { 566 "host": { 567 "address": "localhost", 568 "port": 5799 569 }, 570 "datastore": { 571 "metric": { 572 "host": "127.0.0.1", 573 "port": 3099 574 }, 575 "warehouse": { 576 "host": "198.0.0.1", 577 "port": 2112 578 } 579 } 580 } 581 582 ``` 583 584 Viper can access a nested field by passing a `.` delimited path of keys: 585 586 ```go 587 GetString("datastore.metric.host") // (returns "127.0.0.1") 588 ``` 589 590 This obeys the precedence rules established above; the search for the path 591 will cascade through the remaining configuration registries until found. 592 593 For example, given this configuration file, both `datastore.metric.host` and 594 `datastore.metric.port` are already defined (and may be overridden). If in addition 595 `datastore.metric.protocol` was defined in the defaults, Viper would also find it. 596 597 However, if `datastore.metric` was overridden (by a flag, an environment variable, 598 the `Set()` method, …) with an immediate value, then all sub-keys of 599 `datastore.metric` become undefined, they are “shadowed” by the higher-priority 600 configuration level. 601 602 Viper can access array indices by using numbers in the path. For example: 603 604 ```jsonc 605 { 606 "host": { 607 "address": "localhost", 608 "ports": [ 609 5799, 610 6029 611 ] 612 }, 613 "datastore": { 614 "metric": { 615 "host": "127.0.0.1", 616 "port": 3099 617 }, 618 "warehouse": { 619 "host": "198.0.0.1", 620 "port": 2112 621 } 622 } 623 } 624 625 GetInt("host.ports.1") // returns 6029 626 627 ``` 628 629 Lastly, if there exists a key that matches the delimited key path, its value 630 will be returned instead. E.g. 631 632 ```jsonc 633 { 634 "datastore.metric.host": "0.0.0.0", 635 "host": { 636 "address": "localhost", 637 "port": 5799 638 }, 639 "datastore": { 640 "metric": { 641 "host": "127.0.0.1", 642 "port": 3099 643 }, 644 "warehouse": { 645 "host": "198.0.0.1", 646 "port": 2112 647 } 648 } 649 } 650 651 GetString("datastore.metric.host") // returns "0.0.0.0" 652 ``` 653 654 ### Extracting a sub-tree 655 656 When developing reusable modules, it's often useful to extract a subset of the configuration 657 and pass it to a module. This way the module can be instantiated more than once, with different configurations. 658 659 For example, an application might use multiple different cache stores for different purposes: 660 661 ```yaml 662 cache: 663 cache1: 664 max-items: 100 665 item-size: 64 666 cache2: 667 max-items: 200 668 item-size: 80 669 ``` 670 671 We could pass the cache name to a module (eg. `NewCache("cache1")`), 672 but it would require weird concatenation for accessing config keys and would be less separated from the global config. 673 674 So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration: 675 676 ```go 677 cache1Config := viper.Sub("cache.cache1") 678 if cache1Config == nil { // Sub returns nil if the key cannot be found 679 panic("cache configuration not found") 680 } 681 682 cache1 := NewCache(cache1Config) 683 ``` 684 685 **Note:** Always check the return value of `Sub`. It returns `nil` if a key cannot be found. 686 687 Internally, the `NewCache` function can address `max-items` and `item-size` keys directly: 688 689 ```go 690 func NewCache(v *Viper) *Cache { 691 return &Cache{ 692 MaxItems: v.GetInt("max-items"), 693 ItemSize: v.GetInt("item-size"), 694 } 695 } 696 ``` 697 698 The resulting code is easy to test, since it's decoupled from the main config structure, 699 and easier to reuse (for the same reason). 700 701 702 ### Unmarshaling 703 704 You also have the option of Unmarshaling all or a specific value to a struct, map, 705 etc. 706 707 There are two methods to do this: 708 709 * `Unmarshal(rawVal interface{}) : error` 710 * `UnmarshalKey(key string, rawVal interface{}) : error` 711 712 Example: 713 714 ```go 715 type config struct { 716 Port int 717 Name string 718 PathMap string `mapstructure:"path_map"` 719 } 720 721 var C config 722 723 err := viper.Unmarshal(&C) 724 if err != nil { 725 t.Fatalf("unable to decode into struct, %v", err) 726 } 727 ``` 728 729 If you want to unmarshal configuration where the keys themselves contain dot (the default key delimiter), 730 you have to change the delimiter: 731 732 ```go 733 v := viper.NewWithOptions(viper.KeyDelimiter("::")) 734 735 v.SetDefault("chart::values", map[string]interface{}{ 736 "ingress": map[string]interface{}{ 737 "annotations": map[string]interface{}{ 738 "traefik.frontend.rule.type": "PathPrefix", 739 "traefik.ingress.kubernetes.io/ssl-redirect": "true", 740 }, 741 }, 742 }) 743 744 type config struct { 745 Chart struct{ 746 Values map[string]interface{} 747 } 748 } 749 750 var C config 751 752 v.Unmarshal(&C) 753 ``` 754 755 Viper also supports unmarshaling into embedded structs: 756 757 ```go 758 /* 759 Example config: 760 761 module: 762 enabled: true 763 token: 89h3f98hbwf987h3f98wenf89ehf 764 */ 765 type config struct { 766 Module struct { 767 Enabled bool 768 769 moduleConfig `mapstructure:",squash"` 770 } 771 } 772 773 // moduleConfig could be in a module specific package 774 type moduleConfig struct { 775 Token string 776 } 777 778 var C config 779 780 err := viper.Unmarshal(&C) 781 if err != nil { 782 t.Fatalf("unable to decode into struct, %v", err) 783 } 784 ``` 785 786 Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. 787 788 ### Decoding custom formats 789 790 A frequently requested feature for Viper is adding more value formats and decoders. 791 For example, parsing character (dot, comma, semicolon, etc) separated strings into slices. 792 793 This is already available in Viper using mapstructure decode hooks. 794 795 Read more about the details in [this blog post](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/). 796 797 ### Marshalling to string 798 799 You may need to marshal all the settings held in viper into a string rather than write them to a file. 800 You can use your favorite format's marshaller with the config returned by `AllSettings()`. 801 802 ```go 803 import ( 804 yaml "gopkg.in/yaml.v2" 805 // ... 806 ) 807 808 func yamlStringSettings() string { 809 c := viper.AllSettings() 810 bs, err := yaml.Marshal(c) 811 if err != nil { 812 log.Fatalf("unable to marshal config to YAML: %v", err) 813 } 814 return string(bs) 815 } 816 ``` 817 818 ## Viper or Vipers? 819 820 Viper comes ready to use out of the box. There is no configuration or 821 initialization needed to begin using Viper. Since most applications will want 822 to use a single central repository for their configuration, the viper package 823 provides this. It is similar to a singleton. 824 825 In all of the examples above, they demonstrate using viper in its singleton 826 style approach. 827 828 ### Working with multiple vipers 829 830 You can also create many different vipers for use in your application. Each will 831 have its own unique set of configurations and values. Each can read from a 832 different config file, key value store, etc. All of the functions that viper 833 package supports are mirrored as methods on a viper. 834 835 Example: 836 837 ```go 838 x := viper.New() 839 y := viper.New() 840 841 x.SetDefault("ContentDir", "content") 842 y.SetDefault("ContentDir", "foobar") 843 844 //... 845 ``` 846 847 When working with multiple vipers, it is up to the user to keep track of the 848 different vipers. 849 850 851 ## Q & A 852 853 ### Why is it called “Viper”? 854 855 A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) 856 to [Cobra](https://github.com/spf13/cobra). While both can operate completely 857 independently, together they make a powerful pair to handle much of your 858 application foundation needs. 859 860 ### Why is it called “Cobra”? 861 862 Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)? 863 864 ### Does Viper support case sensitive keys? 865 866 **tl;dr:** No. 867 868 Viper merges configuration from various sources, many of which are either case insensitive or uses different casing than the rest of the sources (eg. env vars). 869 In order to provide the best experience when using multiple sources, the decision has been made to make all keys case insensitive. 870 871 There has been several attempts to implement case sensitivity, but unfortunately it's not that trivial. We might take a stab at implementing it in [Viper v2](https://github.com/gozelle/viper/issues/772), but despite the initial noise, it does not seem to be requested that much. 872 873 You can vote for case sensitivity by filling out this feedback form: https://forms.gle/R6faU74qPRPAzchZ9 874 875 ### Is it safe to concurrently read and write to a viper? 876 877 No, you will need to synchronize access to the viper yourself (for example by using the `sync` package). Concurrent reads and writes can cause a panic. 878 879 ## Troubleshooting 880 881 See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).