Putting Values into Viper

Establishing Defaults

A good configuration system will support default values. A default value is notrequired for a key, but it’s useful in the event that a key hasn’t been set viaconfig file, environment variable, remote configuration or flag.

Examples:

  1. viper.SetDefault("ContentDir", "content")
  2. viper.SetDefault("LayoutDir", "layouts")
  3. viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})

Reading Config Files

Viper requires minimal configuration so it knows where to look for config files.Viper supports JSON, TOML, YAML, HCL, and Java Properties files. Viper can search multiple paths, butcurrently a single Viper instance only supports a single configuration file.Viper does not default to any configuration search paths leaving defaults decisionto an application.

Here is an example of how to use Viper to search for and read a configuration file.None of the specific paths are required, but at least one path should be providedwhere a configuration file is expected.

  1. viper.SetConfigName("config") // name of config file (without extension)
  2. viper.AddConfigPath("/etc/appname/") // path to look for the config file in
  3. viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
  4. viper.AddConfigPath(".") // optionally look for config in the working directory
  5. err := viper.ReadInConfig() // Find and read the config file
  6. if err != nil { // Handle errors reading the config file
  7. panic(fmt.Errorf("Fatal error config file: %s \n", err))
  8. }

Watching and re-reading config files

Viper supports the ability to have your application live read a config file while running.

Gone are the days of needing to restart a server to have a config take effect,viper powered applications can read an update to a config file while running andnot miss a beat.

Simply tell the viper instance to watchConfig.Optionally you can provide a function for Viper to run each time a change occurs.

Make sure you add all of the configPaths prior to calling WatchConfig()

  1. viper.WatchConfig()
  2. viper.OnConfigChange(func(e fsnotify.Event) {
  3. fmt.Println("Config file changed:", e.Name)
  4. })

Reading Config from io.Reader

Viper predefines many configuration sources such as files, environmentvariables, flags, and remote K/V store, but you are not bound to them. You canalso implement your own required configuration source and feed it to viper.

  1. viper.SetConfigType("yaml") // or viper.SetConfigType("YAML")
  2.  
  3. // any approach to require this configuration into your program.
  4. var yamlExample = []byte(`
  5. Hacker: true
  6. name: steve
  7. hobbies:
  8. - skateboarding
  9. - snowboarding
  10. - go
  11. clothing:
  12. jacket: leather
  13. trousers: denim
  14. age: 35
  15. eyes : brown
  16. beard: true
  17. `)
  18.  
  19. viper.ReadConfig(bytes.NewBuffer(yamlExample))
  20.  
  21. viper.Get("name") // this would be "steve"

Setting Overrides

These could be from a command line flag, or from your own application logic.

  1. viper.Set("Verbose", true)
  2. viper.Set("LogFile", LogFile)

Registering and Using Aliases

Aliases permit a single value to be referenced by multiple keys

  1. viper.RegisterAlias("loud", "Verbose")
  2.  
  3. viper.Set("verbose", true) // same result as next line
  4. viper.Set("loud", true) // same result as prior line
  5.  
  6. viper.GetBool("loud") // true
  7. viper.GetBool("verbose") // true

Working with Environment Variables

Viper has full support for environment variables. This enables 12 factorapplications out of the box. There are five methods that exist to aid workingwith ENV:

  • AutomaticEnv()
  • BindEnv(string…) : error
  • SetEnvPrefix(string)
  • SetEnvKeyReplacer(string…) *strings.Replacer
  • AllowEmptyEnvVar(bool)
    When working with ENV variables, it’s important to recognize that Vipertreats ENV variables as case sensitive.

Viper provides a mechanism to try to ensure that ENV variables are unique. Byusing SetEnvPrefix, you can tell Viper to use a prefix while reading fromthe environment variables. Both BindEnv and AutomaticEnv will use thisprefix.

BindEnv takes one or two parameters. The first parameter is the key name, thesecond is the name of the environment variable. The name of the environmentvariable is case sensitive. If the ENV variable name is not provided, thenViper will automatically assume that the key name matches the ENV variable name,but the ENV variable is IN ALL CAPS. When you explicitly provide the ENVvariable name, it does not automatically add the prefix.

One important thing to recognize when working with ENV variables is that thevalue will be read each time it is accessed. Viper does not fix the value whenthe BindEnv is called.

AutomaticEnv is a powerful helper especially when combined withSetEnvPrefix. When called, Viper will check for an environment variable anytime a viper.Get request is made. It will apply the following rules. It willcheck for a environment variable with a name matching the key uppercased andprefixed with the EnvPrefix if set.

SetEnvKeyReplacer allows you to use a strings.Replacer object to rewrite Envkeys to an extent. This is useful if you want to use - or something in yourGet() calls, but want your environmental variables to use _ delimiters. Anexample of using it can be found in viper_test.go.

By default empty environment variables are considered unset and will fall back tothe next configuration source. To treat empty environment variables as set, usethe AllowEmptyEnv method.

Env example

  1. SetEnvPrefix("spf") // will be uppercased automatically
  2. BindEnv("id")
  3.  
  4. os.Setenv("SPF_ID", "13") // typically done outside of the app
  5.  
  6. id := Get("id") // 13

Working with Flags

Viper has the ability to bind to flags. Specifically, Viper supports Pflagsas used in the Cobra library.

Like BindEnv, the value is not set when the binding method is called, but whenit is accessed. This means you can bind as early as you want, even in aninit() function.

For individual flags, the BindPFlag() method provides this functionality.

Example:

  1. serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
  2. viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))

You can also bind an existing set of pflags (pflag.FlagSet):

Example:

  1. pflag.Int("flagname", 1234, "help message for flagname")
  2.  
  3. pflag.Parse()
  4. viper.BindPFlags(pflag.CommandLine)
  5.  
  6. i := viper.GetInt("flagname") // retrieve values from viper instead of pflag

The use of pflag in Viper does not precludethe use of other packages that use the flagpackage from the standard library. The pflag package can handle the flagsdefined for the flag package by importing these flags. This is accomplishedby a calling a convenience function provided by the pflag package calledAddGoFlagSet().

Example:

  1. package main
  2.  
  3. import (
  4. "flag"
  5. "github.com/spf13/pflag"
  6. )
  7.  
  8. func main() {
  9.  
  10. // using standard library "flag" package
  11. flag.Int("flagname", 1234, "help message for flagname")
  12.  
  13. pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
  14. pflag.Parse()
  15. viper.BindPFlags(pflag.CommandLine)
  16.  
  17. i := viper.GetInt("flagname") // retrieve value from viper
  18.  
  19. ...
  20. }

Flag interfaces

Viper provides two Go interfaces to bind other flag systems if you don’t use Pflags.

FlagValue represents a single flag. This is a very simple example on how to implement this interface:

  1. type myFlag struct {}
  2. func (f myFlag) HasChanged() bool { return false }
  3. func (f myFlag) Name() string { return "my-flag-name" }
  4. func (f myFlag) ValueString() string { return "my-flag-value" }
  5. func (f myFlag) ValueType() string { return "string" }

Once your flag implements this interface, you can simply tell Viper to bind it:

  1. viper.BindFlagValue("my-flag-name", myFlag{})

FlagValueSet represents a group of flags. This is a very simple example on how to implement this interface:

  1. type myFlagSet struct {
  2. flags []myFlag
  3. }
  4.  
  5. func (f myFlagSet) VisitAll(fn func(FlagValue)) {
  6. for _, flag := range flags {
  7. fn(flag)
  8. }
  9. }

Once your flag set implements this interface, you can simply tell Viper to bind it:

  1. fSet := myFlagSet{
  2. flags: []myFlag{myFlag{}, myFlag{}},
  3. }
  4. viper.BindFlagValues("my-flags", fSet)

Remote Key/Value Store Support

To enable remote support in Viper, do a blank import of the viper/remotepackage:

import _ "github.com/spf13/viper/remote"

Viper will read a config string (as JSON, TOML, YAML or HCL) retrieved from a pathin a Key/Value store such as etcd or Consul. These values take precedence overdefault values, but are overridden by configuration values retrieved from disk,flags, or environment variables.

Viper uses crypt to retrieveconfiguration from the K/V store, which means that you can store yourconfiguration values encrypted and have them automatically decrypted if you havethe correct gpg keyring. Encryption is optional.

You can use remote configuration in conjunction with local configuration, orindependently of it.

crypt has a command-line helper that you can use to put configurations in yourK/V store. crypt defaults to etcd on http://127.0.0.1:4001.

  1. $ go get github.com/xordataexchange/crypt/bin/crypt
  2. $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json

Confirm that your value was set:

  1. $ crypt get -plaintext /config/hugo.json

See the crypt documentation for examples of how to set encrypted values, orhow to use Consul.

Remote Key/Value Store Example - Unencrypted

etcd

  1. viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
  2. viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
  3. err := viper.ReadRemoteConfig()

Consul

You need to set a key to Consul key/value storage with JSON value containing your desired config.For example, create a Consul key/value store key MY_CONSUL_KEY with value:

  1. {
  2. "port": 8080,
  3. "hostname": "myhostname.com"
  4. }
  1. viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
  2. viper.SetConfigType("json") // Need to explicitly set this to json
  3. err := viper.ReadRemoteConfig()
  4.  
  5. fmt.Println(viper.Get("port")) // 8080
  6. fmt.Println(viper.Get("hostname")) // myhostname.com

Remote Key/Value Store Example - Encrypted

  1. viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
  2. viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
  3. err := viper.ReadRemoteConfig()

Watching Changes in etcd - Unencrypted

  1. // alternatively, you can create a new viper instance.
  2. var runtime_viper = viper.New()
  3.  
  4. runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
  5. 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"
  6.  
  7. // read from remote config the first time.
  8. err := runtime_viper.ReadRemoteConfig()
  9.  
  10. // unmarshal config
  11. runtime_viper.Unmarshal(&runtime_conf)
  12.  
  13. // open a goroutine to watch remote changes forever
  14. go func(){
  15. for {
  16. time.Sleep(time.Second * 5) // delay after each request
  17.  
  18. // currently, only tested with etcd support
  19. err := runtime_viper.WatchRemoteConfig()
  20. if err != nil {
  21. log.Errorf("unable to read remote config: %v", err)
  22. continue
  23. }
  24.  
  25. // unmarshal new config into our runtime config struct. you can also use channel
  26. // to implement a signal to notify the system of the changes
  27. runtime_viper.Unmarshal(&runtime_conf)
  28. }
  29. }()