- Putting Values into Viper
- Establishing Defaults
- Reading Config Files
- Watching and re-reading config files
- Reading Config from io.Reader
- Setting Overrides
- Registering and Using Aliases
- Working with Environment Variables
- Working with Flags
- Remote Key/Value Store Support
- Remote Key/Value Store Example - Unencrypted
- Remote Key/Value Store Example - Encrypted
- Watching Changes in etcd - Unencrypted
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:
- viper.SetDefault("ContentDir", "content")
- viper.SetDefault("LayoutDir", "layouts")
- 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.
- viper.SetConfigName("config") // name of config file (without extension)
- viper.AddConfigPath("/etc/appname/") // path to look for the config file in
- viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
- viper.AddConfigPath(".") // optionally look for config in the working directory
- err := viper.ReadInConfig() // Find and read the config file
- if err != nil { // Handle errors reading the config file
- panic(fmt.Errorf("Fatal error config file: %s \n", err))
- }
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()
- viper.WatchConfig()
- viper.OnConfigChange(func(e fsnotify.Event) {
- fmt.Println("Config file changed:", e.Name)
- })
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.
- viper.SetConfigType("yaml") // or viper.SetConfigType("YAML")
- // any approach to require this configuration into your program.
- var yamlExample = []byte(`
- Hacker: true
- name: steve
- hobbies:
- - skateboarding
- - snowboarding
- - go
- clothing:
- jacket: leather
- trousers: denim
- age: 35
- eyes : brown
- beard: true
- `)
- viper.ReadConfig(bytes.NewBuffer(yamlExample))
- viper.Get("name") // this would be "steve"
Setting Overrides
These could be from a command line flag, or from your own application logic.
- viper.Set("Verbose", true)
- viper.Set("LogFile", LogFile)
Registering and Using Aliases
Aliases permit a single value to be referenced by multiple keys
- viper.RegisterAlias("loud", "Verbose")
- viper.Set("verbose", true) // same result as next line
- viper.Set("loud", true) // same result as prior line
- viper.GetBool("loud") // true
- 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
- SetEnvPrefix("spf") // will be uppercased automatically
- BindEnv("id")
- os.Setenv("SPF_ID", "13") // typically done outside of the app
- id := Get("id") // 13
Working with Flags
Viper has the ability to bind to flags. Specifically, Viper supports Pflags
as 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:
- serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
- viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
You can also bind an existing set of pflags (pflag.FlagSet):
Example:
- pflag.Int("flagname", 1234, "help message for flagname")
- pflag.Parse()
- viper.BindPFlags(pflag.CommandLine)
- 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:
- package main
- import (
- "flag"
- "github.com/spf13/pflag"
- )
- func main() {
- // using standard library "flag" package
- flag.Int("flagname", 1234, "help message for flagname")
- pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
- pflag.Parse()
- viper.BindPFlags(pflag.CommandLine)
- i := viper.GetInt("flagname") // retrieve value from viper
- ...
- }
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:
- type myFlag struct {}
- func (f myFlag) HasChanged() bool { return false }
- func (f myFlag) Name() string { return "my-flag-name" }
- func (f myFlag) ValueString() string { return "my-flag-value" }
- func (f myFlag) ValueType() string { return "string" }
Once your flag implements this interface, you can simply tell Viper to bind it:
- viper.BindFlagValue("my-flag-name", myFlag{})
FlagValueSet
represents a group of flags. This is a very simple example on how to implement this interface:
- type myFlagSet struct {
- flags []myFlag
- }
- func (f myFlagSet) VisitAll(fn func(FlagValue)) {
- for _, flag := range flags {
- fn(flag)
- }
- }
Once your flag set implements this interface, you can simply tell Viper to bind it:
- fSet := myFlagSet{
- flags: []myFlag{myFlag{}, myFlag{}},
- }
- viper.BindFlagValues("my-flags", fSet)
Remote Key/Value Store Support
To enable remote support in Viper, do a blank import of the viper/remote
package:
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.
- $ go get github.com/xordataexchange/crypt/bin/crypt
- $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
Confirm that your value was set:
- $ 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
- viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
- viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
- 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:
- {
- "port": 8080,
- "hostname": "myhostname.com"
- }
- viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
- viper.SetConfigType("json") // Need to explicitly set this to json
- err := viper.ReadRemoteConfig()
- fmt.Println(viper.Get("port")) // 8080
- fmt.Println(viper.Get("hostname")) // myhostname.com
Remote Key/Value Store Example - Encrypted
- viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
- viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
- err := viper.ReadRemoteConfig()
Watching Changes in etcd - Unencrypted
- // alternatively, you can create a new viper instance.
- var runtime_viper = viper.New()
- runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
- 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"
- // read from remote config the first time.
- err := runtime_viper.ReadRemoteConfig()
- // unmarshal config
- runtime_viper.Unmarshal(&runtime_conf)
- // open a goroutine to watch remote changes forever
- go func(){
- for {
- time.Sleep(time.Second * 5) // delay after each request
- // currently, only tested with etcd support
- err := runtime_viper.WatchRemoteConfig()
- if err != nil {
- log.Errorf("unable to read remote config: %v", err)
- continue
- }
- // unmarshal new config into our runtime config struct. you can also use channel
- // to implement a signal to notify the system of the changes
- runtime_viper.Unmarshal(&runtime_conf)
- }
- }()