summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/spf13/viper
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/spf13/viper')
-rw-r--r--vendor/github.com/spf13/viper/.gitignore24
-rw-r--r--vendor/github.com/spf13/viper/.travis.yml26
-rw-r--r--vendor/github.com/spf13/viper/LICENSE21
-rw-r--r--vendor/github.com/spf13/viper/README.md621
-rw-r--r--vendor/github.com/spf13/viper/flags.go57
-rw-r--r--vendor/github.com/spf13/viper/flags_test.go66
-rw-r--r--vendor/github.com/spf13/viper/nohup.out1
-rw-r--r--vendor/github.com/spf13/viper/overrides_test.go173
-rw-r--r--vendor/github.com/spf13/viper/remote/remote.go107
-rw-r--r--vendor/github.com/spf13/viper/util.go282
-rw-r--r--vendor/github.com/spf13/viper/util_test.go55
-rw-r--r--vendor/github.com/spf13/viper/viper.go1550
-rw-r--r--vendor/github.com/spf13/viper/viper_test.go1154
13 files changed, 4137 insertions, 0 deletions
diff --git a/vendor/github.com/spf13/viper/.gitignore b/vendor/github.com/spf13/viper/.gitignore
new file mode 100644
index 000000000..352a34a56
--- /dev/null
+++ b/vendor/github.com/spf13/viper/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.bench \ No newline at end of file
diff --git a/vendor/github.com/spf13/viper/.travis.yml b/vendor/github.com/spf13/viper/.travis.yml
new file mode 100644
index 000000000..d4c2559c2
--- /dev/null
+++ b/vendor/github.com/spf13/viper/.travis.yml
@@ -0,0 +1,26 @@
+go_import_path: github.com/spf13/viper
+
+language: go
+go:
+ - 1.7.5
+ - 1.8
+ - tip
+
+os:
+ - linux
+ - osx
+
+matrix:
+ allow_failures:
+ - go: tip
+ fast_finish: true
+
+script:
+ - go install ./...
+ - go test -v ./...
+
+after_success:
+ - go get -u -d github.com/spf13/hugo
+ - cd $GOPATH/src/github.com/spf13/hugo && make && ./hugo -s docs && cd -
+
+sudo: false
diff --git a/vendor/github.com/spf13/viper/LICENSE b/vendor/github.com/spf13/viper/LICENSE
new file mode 100644
index 000000000..4527efb9c
--- /dev/null
+++ b/vendor/github.com/spf13/viper/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Steve Francia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md
new file mode 100644
index 000000000..25181dff1
--- /dev/null
+++ b/vendor/github.com/spf13/viper/README.md
@@ -0,0 +1,621 @@
+![viper logo](https://cloud.githubusercontent.com/assets/173412/10886745/998df88a-8151-11e5-9448-4736db51020d.png)
+
+Go configuration with fangs!
+
+Many Go projects are built using Viper including:
+
+* [Hugo](http://gohugo.io)
+* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)
+* [Imgur's Incus](https://github.com/Imgur/incus)
+* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
+* [Docker Notary](https://github.com/docker/Notary)
+* [BloomApi](https://www.bloomapi.com/)
+* [doctl](https://github.com/digitalocean/doctl)
+
+[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![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) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper)
+
+
+## What is Viper?
+
+Viper is a complete configuration solution for go applications including 12 factor apps. It is designed
+to work within an application, and can handle all types of configuration needs
+and formats. It supports:
+
+* setting defaults
+* reading from JSON, TOML, YAML, HCL, and Java properties config files
+* live watching and re-reading of config files (optional)
+* reading from environment variables
+* reading from remote config systems (etcd or Consul), and watching changes
+* reading from command line flags
+* reading from buffer
+* setting explicit values
+
+Viper can be thought of as a registry for all of your applications
+configuration needs.
+
+## Why Viper?
+
+When building a modern application, you don’t want to worry about
+configuration file formats; you want to focus on building awesome software.
+Viper is here to help with that.
+
+Viper does the following for you:
+
+1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, or Java properties formats.
+2. Provide a mechanism to set default values for your different
+ configuration options.
+3. Provide a mechanism to set override values for options specified through
+ command line flags.
+4. Provide an alias system to easily rename parameters without breaking existing
+ code.
+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.
+
+Viper uses the following precedence order. Each item takes precedence over the
+item below it:
+
+ * explicit call to Set
+ * flag
+ * env
+ * config
+ * key/value store
+ * default
+
+Viper configuration keys are case insensitive.
+
+## Putting Values into Viper
+
+### Establishing Defaults
+
+A good configuration system will support default values. A default value is not
+required for a key, but it's useful in the event that a key hasn’t been set via
+config file, environment variable, remote configuration or flag.
+
+Examples:
+
+```go
+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, but
+currently a single Viper instance only supports a single configuration file.
+Viper does not default to any configuration search paths leaving defaults decision
+to 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 provided
+where a configuration file is expected.
+
+```go
+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 and
+not 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()`**
+
+```go
+ 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, environment
+variables, flags, and remote K/V store, but you are not bound to them. You can
+also implement your own required configuration source and feed it to viper.
+
+```go
+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.
+
+```go
+viper.Set("Verbose", true)
+viper.Set("LogFile", LogFile)
+```
+
+### Registering and Using Aliases
+
+Aliases permit a single value to be referenced by multiple keys
+
+```go
+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 factor
+applications out of the box. There are four methods that exist to aid working
+with ENV:
+
+ * `AutomaticEnv()`
+ * `BindEnv(string...) : error`
+ * `SetEnvPrefix(string)`
+ * `SetEnvReplacer(string...) *strings.Replacer`
+
+_When working with ENV variables, it’s important to recognize that Viper
+treats ENV variables as case sensitive._
+
+Viper provides a mechanism to try to ensure that ENV variables are unique. By
+using `SetEnvPrefix`, you can tell Viper to use add a prefix while reading from
+the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
+prefix.
+
+`BindEnv` takes one or two parameters. The first parameter is the key name, the
+second is the name of the environment variable. The name of the environment
+variable is case sensitive. If the ENV variable name is not provided, then
+Viper 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 ENV
+variable name, it **does not** automatically add the prefix.
+
+One important thing to recognize when working with ENV variables is that the
+value will be read each time it is accessed. Viper does not fix the value when
+the `BindEnv` is called.
+
+`AutomaticEnv` is a powerful helper especially when combined with
+`SetEnvPrefix`. When called, Viper will check for an environment variable any
+time a `viper.Get` request is made. It will apply the following rules. It will
+check for a environment variable with a name matching the key uppercased and
+prefixed with the `EnvPrefix` if set.
+
+`SetEnvReplacer` allows you to use a `strings.Replacer` object to rewrite Env
+keys to an extent. This is useful if you want to use `-` or something in your
+`Get()` calls, but want your environmental variables to use `_` delimiters. An
+example of using it can be found in `viper_test.go`.
+
+#### Env example
+
+```go
+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](https://github.com/spf13/cobra) library.
+
+Like `BindEnv`, the value is not set when the binding method is called, but when
+it is accessed. This means you can bind as early as you want, even in an
+`init()` function.
+
+The `BindPFlag()` method provides this functionality.
+
+Example:
+
+```go
+serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
+viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
+```
+
+The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude
+the use of other packages that use the [flag](https://golang.org/pkg/flag/)
+package from the standard library. The pflag package can handle the flags
+defined for the flag package by importing these flags. This is accomplished
+by a calling a convenience function provided by the pflag package called
+AddGoFlagSet().
+
+Example:
+
+```go
+package main
+
+import (
+ "flag"
+ "github.com/spf13/pflag"
+)
+
+func main() {
+ pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
+ pflag.Parse()
+ ...
+}
+```
+
+#### 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:
+
+```go
+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:
+
+```go
+viper.BindFlagValue("my-flag-name", myFlag{})
+```
+
+`FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface:
+
+```go
+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:
+
+```go
+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 path
+in a Key/Value store such as etcd or Consul. These values take precedence over
+default values, but are overridden by configuration values retrieved from disk,
+flags, or environment variables.
+
+Viper uses [crypt](https://github.com/xordataexchange/crypt) to retrieve
+configuration from the K/V store, which means that you can store your
+configuration values encrypted and have them automatically decrypted if you have
+the correct gpg keyring. Encryption is optional.
+
+You can use remote configuration in conjunction with local configuration, or
+independently of it.
+
+`crypt` has a command-line helper that you can use to put configurations in your
+K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001.
+
+```bash
+$ 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:
+
+```bash
+$ crypt get -plaintext /config/hugo.json
+```
+
+See the `crypt` documentation for examples of how to set encrypted values, or
+how to use Consul.
+
+### Remote Key/Value Store Example - Unencrypted
+
+```go
+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()
+```
+
+### Remote Key/Value Store Example - Encrypted
+
+```go
+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
+
+```go
+// 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)
+ }
+}()
+```
+
+## Getting Values From Viper
+
+In Viper, there are a few ways to get a value depending on the value's type.
+The following functions and methods exist:
+
+ * `Get(key string) : interface{}`
+ * `GetBool(key string) : bool`
+ * `GetFloat64(key string) : float64`
+ * `GetInt(key string) : int`
+ * `GetString(key string) : string`
+ * `GetStringMap(key string) : map[string]interface{}`
+ * `GetStringMapString(key string) : map[string]string`
+ * `GetStringSlice(key string) : []string`
+ * `GetTime(key string) : time.Time`
+ * `GetDuration(key string) : time.Duration`
+ * `IsSet(key string) : bool`
+
+One important thing to recognize is that each Get function will return a zero
+value if it’s not found. To check if a given key exists, the `IsSet()` method
+has been provided.
+
+Example:
+```go
+viper.GetString("logfile") // case-insensitive Setting & Getting
+if viper.GetBool("verbose") {
+ fmt.Println("verbose enabled")
+}
+```
+### Accessing nested keys
+
+The accessor methods also accept formatted paths to deeply nested keys. For
+example, if the following JSON file is loaded:
+
+```json
+{
+ "host": {
+ "address": "localhost",
+ "port": 5799
+ },
+ "datastore": {
+ "metric": {
+ "host": "127.0.0.1",
+ "port": 3099
+ },
+ "warehouse": {
+ "host": "198.0.0.1",
+ "port": 2112
+ }
+ }
+}
+
+```
+
+Viper can access a nested field by passing a `.` delimited path of keys:
+
+```go
+GetString("datastore.metric.host") // (returns "127.0.0.1")
+```
+
+This obeys the precedence rules established above; the search for the path
+will cascade through the remaining configuration registries until found.
+
+For example, given this configuration file, both `datastore.metric.host` and
+`datastore.metric.port` are already defined (and may be overridden). If in addition
+`datastore.metric.protocol` was defined in the defaults, Viper would also find it.
+
+However, if `datastore.metric` was overridden (by a flag, an environment variable,
+the `Set()` method, …) with an immediate value, then all sub-keys of
+`datastore.metric` become undefined, they are “shadowed” by the higher-priority
+configuration level.
+
+Lastly, if there exists a key that matches the delimited key path, its value
+will be returned instead. E.g.
+
+```json
+{
+ "datastore.metric.host": "0.0.0.0",
+ "host": {
+ "address": "localhost",
+ "port": 5799
+ },
+ "datastore": {
+ "metric": {
+ "host": "127.0.0.1",
+ "port": 3099
+ },
+ "warehouse": {
+ "host": "198.0.0.1",
+ "port": 2112
+ }
+ }
+}
+
+GetString("datastore.metric.host") // returns "0.0.0.0"
+```
+
+### Extract sub-tree
+
+Extract sub-tree from Viper.
+
+For example, `viper` represents:
+
+```json
+app:
+ cache1:
+ max-items: 100
+ item-size: 64
+ cache2:
+ max-items: 200
+ item-size: 80
+```
+
+After executing:
+
+```go
+subv := viper.Sub("app.cache1")
+```
+
+`subv` represents:
+
+```json
+max-items: 100
+item-size: 64
+```
+
+Suppose we have:
+
+```go
+func NewCache(cfg *Viper) *Cache {...}
+```
+
+which creates a cache based on config information formatted as `subv`.
+Now it's easy to create these 2 caches separately as:
+
+```go
+cfg1 := viper.Sub("app.cache1")
+cache1 := NewCache(cfg1)
+
+cfg2 := viper.Sub("app.cache2")
+cache2 := NewCache(cfg2)
+```
+
+### Unmarshaling
+
+You also have the option of Unmarshaling all or a specific value to a struct, map,
+etc.
+
+There are two methods to do this:
+
+ * `Unmarshal(rawVal interface{}) : error`
+ * `UnmarshalKey(key string, rawVal interface{}) : error`
+
+Example:
+
+```go
+type config struct {
+ Port int
+ Name string
+ PathMap string `mapstructure:"path_map"`
+}
+
+var C config
+
+err := Unmarshal(&C)
+if err != nil {
+ t.Fatalf("unable to decode into struct, %v", err)
+}
+```
+
+## Viper or Vipers?
+
+Viper comes ready to use out of the box. There is no configuration or
+initialization needed to begin using Viper. Since most applications will want
+to use a single central repository for their configuration, the viper package
+provides this. It is similar to a singleton.
+
+In all of the examples above, they demonstrate using viper in it's singleton
+style approach.
+
+### Working with multiple vipers
+
+You can also create many different vipers for use in your application. Each will
+have it’s own unique set of configurations and values. Each can read from a
+different config file, key value store, etc. All of the functions that viper
+package supports are mirrored as methods on a viper.
+
+Example:
+
+```go
+x := viper.New()
+y := viper.New()
+
+x.SetDefault("ContentDir", "content")
+y.SetDefault("ContentDir", "foobar")
+
+//...
+```
+
+When working with multiple vipers, it is up to the user to keep track of the
+different vipers.
+
+## Q & A
+
+Q: Why not INI files?
+
+A: Ini files are pretty awful. There’s no standard format, and they are hard to
+validate. Viper is designed to work with JSON, TOML or YAML files. If someone
+really wants to add this feature, I’d be happy to merge it. It’s easy to specify
+which formats your application will permit.
+
+Q: Why is it called “Viper”?
+
+A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe))
+to [Cobra](https://github.com/spf13/cobra). While both can operate completely
+independently, together they make a powerful pair to handle much of your
+application foundation needs.
+
+Q: Why is it called “Cobra”?
+
+A: Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)?
diff --git a/vendor/github.com/spf13/viper/flags.go b/vendor/github.com/spf13/viper/flags.go
new file mode 100644
index 000000000..dd32f4e1c
--- /dev/null
+++ b/vendor/github.com/spf13/viper/flags.go
@@ -0,0 +1,57 @@
+package viper
+
+import "github.com/spf13/pflag"
+
+// FlagValueSet is an interface that users can implement
+// to bind a set of flags to viper.
+type FlagValueSet interface {
+ VisitAll(fn func(FlagValue))
+}
+
+// FlagValue is an interface that users can implement
+// to bind different flags to viper.
+type FlagValue interface {
+ HasChanged() bool
+ Name() string
+ ValueString() string
+ ValueType() string
+}
+
+// pflagValueSet is a wrapper around *pflag.ValueSet
+// that implements FlagValueSet.
+type pflagValueSet struct {
+ flags *pflag.FlagSet
+}
+
+// VisitAll iterates over all *pflag.Flag inside the *pflag.FlagSet.
+func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
+ p.flags.VisitAll(func(flag *pflag.Flag) {
+ fn(pflagValue{flag})
+ })
+}
+
+// pflagValue is a wrapper aroung *pflag.flag
+// that implements FlagValue
+type pflagValue struct {
+ flag *pflag.Flag
+}
+
+// HasChanges returns whether the flag has changes or not.
+func (p pflagValue) HasChanged() bool {
+ return p.flag.Changed
+}
+
+// Name returns the name of the flag.
+func (p pflagValue) Name() string {
+ return p.flag.Name
+}
+
+// ValueString returns the value of the flag as a string.
+func (p pflagValue) ValueString() string {
+ return p.flag.Value.String()
+}
+
+// ValueType returns the type of the flag as a string.
+func (p pflagValue) ValueType() string {
+ return p.flag.Value.Type()
+}
diff --git a/vendor/github.com/spf13/viper/flags_test.go b/vendor/github.com/spf13/viper/flags_test.go
new file mode 100644
index 000000000..5bffca36c
--- /dev/null
+++ b/vendor/github.com/spf13/viper/flags_test.go
@@ -0,0 +1,66 @@
+package viper
+
+import (
+ "testing"
+
+ "github.com/spf13/pflag"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBindFlagValueSet(t *testing.T) {
+ flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
+
+ var testValues = map[string]*string{
+ "host": nil,
+ "port": nil,
+ "endpoint": nil,
+ }
+
+ var mutatedTestValues = map[string]string{
+ "host": "localhost",
+ "port": "6060",
+ "endpoint": "/public",
+ }
+
+ for name := range testValues {
+ testValues[name] = flagSet.String(name, "", "test")
+ }
+
+ flagValueSet := pflagValueSet{flagSet}
+
+ err := BindFlagValues(flagValueSet)
+ if err != nil {
+ t.Fatalf("error binding flag set, %v", err)
+ }
+
+ flagSet.VisitAll(func(flag *pflag.Flag) {
+ flag.Value.Set(mutatedTestValues[flag.Name])
+ flag.Changed = true
+ })
+
+ for name, expected := range mutatedTestValues {
+ assert.Equal(t, Get(name), expected)
+ }
+}
+
+func TestBindFlagValue(t *testing.T) {
+ var testString = "testing"
+ var testValue = newStringValue(testString, &testString)
+
+ flag := &pflag.Flag{
+ Name: "testflag",
+ Value: testValue,
+ Changed: false,
+ }
+
+ flagValue := pflagValue{flag}
+ BindFlagValue("testvalue", flagValue)
+
+ assert.Equal(t, testString, Get("testvalue"))
+
+ flag.Value.Set("testing_mutate")
+ flag.Changed = true //hack for pflag usage
+
+ assert.Equal(t, "testing_mutate", Get("testvalue"))
+
+}
diff --git a/vendor/github.com/spf13/viper/nohup.out b/vendor/github.com/spf13/viper/nohup.out
new file mode 100644
index 000000000..8973bf27b
--- /dev/null
+++ b/vendor/github.com/spf13/viper/nohup.out
@@ -0,0 +1 @@
+QProcess::start: Process is already running
diff --git a/vendor/github.com/spf13/viper/overrides_test.go b/vendor/github.com/spf13/viper/overrides_test.go
new file mode 100644
index 000000000..dd2aa9b0d
--- /dev/null
+++ b/vendor/github.com/spf13/viper/overrides_test.go
@@ -0,0 +1,173 @@
+package viper
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/spf13/cast"
+ "github.com/stretchr/testify/assert"
+)
+
+type layer int
+
+const (
+ defaultLayer layer = iota + 1
+ overrideLayer
+)
+
+func TestNestedOverrides(t *testing.T) {
+ assert := assert.New(t)
+ var v *Viper
+
+ // Case 0: value overridden by a value
+ overrideDefault(assert, "tom", 10, "tom", 20) // "tom" is first given 10 as default value, then overridden by 20
+ override(assert, "tom", 10, "tom", 20) // "tom" is first given value 10, then overridden by 20
+ overrideDefault(assert, "tom.age", 10, "tom.age", 20)
+ override(assert, "tom.age", 10, "tom.age", 20)
+ overrideDefault(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20)
+ override(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20)
+
+ // Case 1: key:value overridden by a value
+ v = overrideDefault(assert, "tom.age", 10, "tom", "boy") // "tom.age" is first given 10 as default value, then "tom" is overridden by "boy"
+ assert.Nil(v.Get("tom.age")) // "tom.age" should not exist anymore
+ v = override(assert, "tom.age", 10, "tom", "boy")
+ assert.Nil(v.Get("tom.age"))
+
+ // Case 2: value overridden by a key:value
+ overrideDefault(assert, "tom", "boy", "tom.age", 10) // "tom" is first given "boy" as default value, then "tom" is overridden by map{"age":10}
+ override(assert, "tom.age", 10, "tom", "boy")
+
+ // Case 3: key:value overridden by a key:value
+ v = overrideDefault(assert, "tom.size", 4, "tom.age", 10)
+ assert.Equal(4, v.Get("tom.size")) // value should still be reachable
+ v = override(assert, "tom.size", 4, "tom.age", 10)
+ assert.Equal(4, v.Get("tom.size"))
+ deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4)
+
+ // Case 4: key:value overridden by a map
+ v = overrideDefault(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10}
+ assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable
+ assert.Equal(10, v.Get("tom.age")) // new value should be there
+ deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there
+ v = override(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10})
+ assert.Nil(v.Get("tom.size"))
+ assert.Equal(10, v.Get("tom.age"))
+ deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10)
+
+ // Case 5: array overridden by a value
+ overrideDefault(assert, "tom", []int{10, 20}, "tom", 30)
+ override(assert, "tom", []int{10, 20}, "tom", 30)
+ overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", 30)
+ override(assert, "tom.age", []int{10, 20}, "tom.age", 30)
+
+ // Case 6: array overridden by an array
+ overrideDefault(assert, "tom", []int{10, 20}, "tom", []int{30, 40})
+ override(assert, "tom", []int{10, 20}, "tom", []int{30, 40})
+ overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40})
+ v = override(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40})
+ // explicit array merge:
+ s, ok := v.Get("tom.age").([]int)
+ if assert.True(ok, "tom[\"age\"] is not a slice") {
+ v.Set("tom.age", append(s, []int{50, 60}...))
+ assert.Equal([]int{30, 40, 50, 60}, v.Get("tom.age"))
+ deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, []int{30, 40, 50, 60})
+ }
+}
+
+func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
+ return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
+}
+func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
+ return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
+}
+
+// overrideFromLayer performs the sequential override and low-level checks.
+//
+// First assignment is made on layer l for path firstPath with value firstValue,
+// the second one on the override layer (i.e., with the Set() function)
+// for path secondPath with value secondValue.
+//
+// firstPath and secondPath can include an arbitrary number of dots to indicate
+// a nested element.
+//
+// After each assignment, the value is checked, retrieved both by its full path
+// and by its key sequence (successive maps).
+func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
+ v := New()
+ firstKeys := strings.Split(firstPath, v.keyDelim)
+ if assert == nil ||
+ len(firstKeys) == 0 || len(firstKeys[0]) == 0 {
+ return v
+ }
+
+ // Set and check first value
+ switch l {
+ case defaultLayer:
+ v.SetDefault(firstPath, firstValue)
+ case overrideLayer:
+ v.Set(firstPath, firstValue)
+ default:
+ return v
+ }
+ assert.Equal(firstValue, v.Get(firstPath))
+ deepCheckValue(assert, v, l, firstKeys, firstValue)
+
+ // Override and check new value
+ secondKeys := strings.Split(secondPath, v.keyDelim)
+ if len(secondKeys) == 0 || len(secondKeys[0]) == 0 {
+ return v
+ }
+ v.Set(secondPath, secondValue)
+ assert.Equal(secondValue, v.Get(secondPath))
+ deepCheckValue(assert, v, overrideLayer, secondKeys, secondValue)
+
+ return v
+}
+
+// deepCheckValue checks that all given keys correspond to a valid path in the
+// configuration map of the given layer, and that the final value equals the one given
+func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value interface{}) {
+ if assert == nil || v == nil ||
+ len(keys) == 0 || len(keys[0]) == 0 {
+ return
+ }
+
+ // init
+ var val interface{}
+ var ms string
+ switch l {
+ case defaultLayer:
+ val = v.defaults
+ ms = "v.defaults"
+ case overrideLayer:
+ val = v.override
+ ms = "v.override"
+ }
+
+ // loop through map
+ var m map[string]interface{}
+ err := false
+ for _, k := range keys {
+ if val == nil {
+ assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
+ return
+ }
+
+ // deep scan of the map to get the final value
+ switch val.(type) {
+ case map[interface{}]interface{}:
+ m = cast.ToStringMap(val)
+ case map[string]interface{}:
+ m = val.(map[string]interface{})
+ default:
+ assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
+ return
+ }
+ ms = ms + "[\"" + k + "\"]"
+ val = m[k]
+ }
+ if !err {
+ assert.Equal(value, val)
+ }
+}
diff --git a/vendor/github.com/spf13/viper/remote/remote.go b/vendor/github.com/spf13/viper/remote/remote.go
new file mode 100644
index 000000000..f100a9c7f
--- /dev/null
+++ b/vendor/github.com/spf13/viper/remote/remote.go
@@ -0,0 +1,107 @@
+// Copyright © 2015 Steve Francia <spf@spf13.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// Package remote integrates the remote features of Viper.
+package remote
+
+import (
+ "bytes"
+ "github.com/spf13/viper"
+ crypt "github.com/xordataexchange/crypt/config"
+ "io"
+ "os"
+)
+
+type remoteConfigProvider struct{}
+
+func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
+ cm, err := getConfigManager(rp)
+ if err != nil {
+ return nil, err
+ }
+ b, err := cm.Get(rp.Path())
+ if err != nil {
+ return nil, err
+ }
+ return bytes.NewReader(b), nil
+}
+
+func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
+ cm, err := getConfigManager(rp)
+ if err != nil {
+ return nil, err
+ }
+ resp,err := cm.Get(rp.Path())
+ if err != nil {
+ return nil, err
+ }
+
+ return bytes.NewReader(resp), nil
+}
+func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
+ cm, err := getConfigManager(rp)
+ if err != nil {
+ return nil, nil
+ }
+ quit := make(chan bool)
+ quitwc := make(chan bool)
+ viperResponsCh := make(chan *viper.RemoteResponse)
+ cryptoResponseCh := cm.Watch(rp.Path(), quit)
+ // need this function to convert the Channel response form crypt.Response to viper.Response
+ go func(cr <-chan *crypt.Response,vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
+ for {
+ select {
+ case <- quitwc:
+ quit <- true
+ return
+ case resp := <-cr:
+ vr <- &viper.RemoteResponse{
+ Error: resp.Error,
+ Value: resp.Value,
+ }
+
+ }
+
+ }
+ }(cryptoResponseCh,viperResponsCh,quitwc,quit)
+
+ return viperResponsCh,quitwc
+
+}
+
+
+func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
+
+ var cm crypt.ConfigManager
+ var err error
+
+ if rp.SecretKeyring() != "" {
+ kr, err := os.Open(rp.SecretKeyring())
+ defer kr.Close()
+ if err != nil {
+ return nil, err
+ }
+ if rp.Provider() == "etcd" {
+ cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
+ } else {
+ cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
+ }
+ } else {
+ if rp.Provider() == "etcd" {
+ cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
+ } else {
+ cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ return cm, nil
+
+}
+
+func init() {
+ viper.RemoteConfig = &remoteConfigProvider{}
+}
diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go
new file mode 100644
index 000000000..3ebada91a
--- /dev/null
+++ b/vendor/github.com/spf13/viper/util.go
@@ -0,0 +1,282 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// Viper is a application configuration system.
+// It believes that applications can be configured a variety of ways
+// via flags, ENVIRONMENT variables, configuration files retrieved
+// from the file system, or a remote key/value store.
+
+package viper
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "unicode"
+
+ "github.com/hashicorp/hcl"
+ "github.com/magiconair/properties"
+ toml "github.com/pelletier/go-toml"
+ "github.com/spf13/cast"
+ jww "github.com/spf13/jwalterweatherman"
+ "gopkg.in/yaml.v2"
+)
+
+// ConfigParseError denotes failing to parse configuration file.
+type ConfigParseError struct {
+ err error
+}
+
+// Error returns the formatted configuration error.
+func (pe ConfigParseError) Error() string {
+ return fmt.Sprintf("While parsing config: %s", pe.err.Error())
+}
+
+// toCaseInsensitiveValue checks if the value is a map;
+// if so, create a copy and lower-case the keys recursively.
+func toCaseInsensitiveValue(value interface{}) interface{} {
+ switch v := value.(type) {
+ case map[interface{}]interface{}:
+ value = copyAndInsensitiviseMap(cast.ToStringMap(v))
+ case map[string]interface{}:
+ value = copyAndInsensitiviseMap(v)
+ }
+
+ return value
+}
+
+// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
+// any map it makes case insensitive.
+func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
+ nm := make(map[string]interface{})
+
+ for key, val := range m {
+ lkey := strings.ToLower(key)
+ switch v := val.(type) {
+ case map[interface{}]interface{}:
+ nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
+ case map[string]interface{}:
+ nm[lkey] = copyAndInsensitiviseMap(v)
+ default:
+ nm[lkey] = v
+ }
+ }
+
+ return nm
+}
+
+func insensitiviseMap(m map[string]interface{}) {
+ for key, val := range m {
+ switch val.(type) {
+ case map[interface{}]interface{}:
+ // nested map: cast and recursively insensitivise
+ val = cast.ToStringMap(val)
+ insensitiviseMap(val.(map[string]interface{}))
+ case map[string]interface{}:
+ // nested map: recursively insensitivise
+ insensitiviseMap(val.(map[string]interface{}))
+ }
+
+ lower := strings.ToLower(key)
+ if key != lower {
+ // remove old key (not lower-cased)
+ delete(m, key)
+ }
+ // update map
+ m[lower] = val
+ }
+}
+
+func absPathify(inPath string) string {
+ jww.INFO.Println("Trying to resolve absolute path to", inPath)
+
+ if strings.HasPrefix(inPath, "$HOME") {
+ inPath = userHomeDir() + inPath[5:]
+ }
+
+ if strings.HasPrefix(inPath, "$") {
+ end := strings.Index(inPath, string(os.PathSeparator))
+ inPath = os.Getenv(inPath[1:end]) + inPath[end:]
+ }
+
+ if filepath.IsAbs(inPath) {
+ return filepath.Clean(inPath)
+ }
+
+ p, err := filepath.Abs(inPath)
+ if err == nil {
+ return filepath.Clean(p)
+ }
+
+ jww.ERROR.Println("Couldn't discover absolute path")
+ jww.ERROR.Println(err)
+ return ""
+}
+
+// Check if File / Directory Exists
+func exists(path string) (bool, error) {
+ _, err := v.fs.Stat(path)
+ if err == nil {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+}
+
+func stringInSlice(a string, list []string) bool {
+ for _, b := range list {
+ if b == a {
+ return true
+ }
+ }
+ return false
+}
+
+func userHomeDir() string {
+ if runtime.GOOS == "windows" {
+ home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
+ if home == "" {
+ home = os.Getenv("USERPROFILE")
+ }
+ return home
+ }
+ return os.Getenv("HOME")
+}
+
+func unmarshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error {
+ buf := new(bytes.Buffer)
+ buf.ReadFrom(in)
+
+ switch strings.ToLower(configType) {
+ case "yaml", "yml":
+ if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil {
+ return ConfigParseError{err}
+ }
+
+ case "json":
+ if err := json.Unmarshal(buf.Bytes(), &c); err != nil {
+ return ConfigParseError{err}
+ }
+
+ case "hcl":
+ obj, err := hcl.Parse(string(buf.Bytes()))
+ if err != nil {
+ return ConfigParseError{err}
+ }
+ if err = hcl.DecodeObject(&c, obj); err != nil {
+ return ConfigParseError{err}
+ }
+
+ case "toml":
+ tree, err := toml.LoadReader(buf)
+ if err != nil {
+ return ConfigParseError{err}
+ }
+ tmap := tree.ToMap()
+ for k, v := range tmap {
+ c[k] = v
+ }
+
+ case "properties", "props", "prop":
+ var p *properties.Properties
+ var err error
+ if p, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
+ return ConfigParseError{err}
+ }
+ for _, key := range p.Keys() {
+ value, _ := p.Get(key)
+ // recursively build nested maps
+ path := strings.Split(key, ".")
+ lastKey := strings.ToLower(path[len(path)-1])
+ deepestMap := deepSearch(c, path[0:len(path)-1])
+ // set innermost value
+ deepestMap[lastKey] = value
+ }
+ }
+
+ insensitiviseMap(c)
+ return nil
+}
+
+func safeMul(a, b uint) uint {
+ c := a * b
+ if a > 1 && b > 1 && c/b != a {
+ return 0
+ }
+ return c
+}
+
+// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
+func parseSizeInBytes(sizeStr string) uint {
+ sizeStr = strings.TrimSpace(sizeStr)
+ lastChar := len(sizeStr) - 1
+ multiplier := uint(1)
+
+ if lastChar > 0 {
+ if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' {
+ if lastChar > 1 {
+ switch unicode.ToLower(rune(sizeStr[lastChar-1])) {
+ case 'k':
+ multiplier = 1 << 10
+ sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
+ case 'm':
+ multiplier = 1 << 20
+ sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
+ case 'g':
+ multiplier = 1 << 30
+ sizeStr = strings.TrimSpace(sizeStr[:lastChar-1])
+ default:
+ multiplier = 1
+ sizeStr = strings.TrimSpace(sizeStr[:lastChar])
+ }
+ }
+ }
+ }
+
+ size := cast.ToInt(sizeStr)
+ if size < 0 {
+ size = 0
+ }
+
+ return safeMul(uint(size), multiplier)
+}
+
+// deepSearch scans deep maps, following the key indexes listed in the
+// sequence "path".
+// The last value is expected to be another map, and is returned.
+//
+// In case intermediate keys do not exist, or map to a non-map value,
+// a new map is created and inserted, and the search continues from there:
+// the initial map "m" may be modified!
+func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
+ for _, k := range path {
+ m2, ok := m[k]
+ if !ok {
+ // intermediate key does not exist
+ // => create it and continue from there
+ m3 := make(map[string]interface{})
+ m[k] = m3
+ m = m3
+ continue
+ }
+ m3, ok := m2.(map[string]interface{})
+ if !ok {
+ // intermediate key is a value
+ // => replace with a new map
+ m3 = make(map[string]interface{})
+ m[k] = m3
+ }
+ // continue search from here
+ m = m3
+ }
+ return m
+}
diff --git a/vendor/github.com/spf13/viper/util_test.go b/vendor/github.com/spf13/viper/util_test.go
new file mode 100644
index 000000000..5949e09e4
--- /dev/null
+++ b/vendor/github.com/spf13/viper/util_test.go
@@ -0,0 +1,55 @@
+// Copyright © 2016 Steve Francia <spf@spf13.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// Viper is a application configuration system.
+// It believes that applications can be configured a variety of ways
+// via flags, ENVIRONMENT variables, configuration files retrieved
+// from the file system, or a remote key/value store.
+
+package viper
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestCopyAndInsensitiviseMap(t *testing.T) {
+
+ var (
+ given = map[string]interface{}{
+ "Foo": 32,
+ "Bar": map[interface{}]interface {
+ }{
+ "ABc": "A",
+ "cDE": "B"},
+ }
+ expected = map[string]interface{}{
+ "foo": 32,
+ "bar": map[string]interface {
+ }{
+ "abc": "A",
+ "cde": "B"},
+ }
+ )
+
+ got := copyAndInsensitiviseMap(given)
+
+ if !reflect.DeepEqual(got, expected) {
+ t.Fatalf("Got %q\nexpected\n%q", got, expected)
+ }
+
+ if _, ok := given["foo"]; ok {
+ t.Fatal("Input map changed")
+ }
+
+ if _, ok := given["bar"]; ok {
+ t.Fatal("Input map changed")
+ }
+
+ m := given["Bar"].(map[interface{}]interface{})
+ if _, ok := m["ABc"]; !ok {
+ t.Fatal("Input map changed")
+ }
+}
diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go
new file mode 100644
index 000000000..22a2ed8ad
--- /dev/null
+++ b/vendor/github.com/spf13/viper/viper.go
@@ -0,0 +1,1550 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// Viper is a application configuration system.
+// It believes that applications can be configured a variety of ways
+// via flags, ENVIRONMENT variables, configuration files retrieved
+// from the file system, or a remote key/value store.
+
+// Each item takes precedence over the item below it:
+
+// overrides
+// flag
+// env
+// config
+// key/value store
+// default
+
+package viper
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "time"
+
+ "github.com/fsnotify/fsnotify"
+ "github.com/mitchellh/mapstructure"
+ "github.com/spf13/afero"
+ "github.com/spf13/cast"
+ jww "github.com/spf13/jwalterweatherman"
+ "github.com/spf13/pflag"
+)
+
+var v *Viper
+
+type RemoteResponse struct {
+ Value []byte
+ Error error
+}
+
+func init() {
+ v = New()
+}
+
+type remoteConfigFactory interface {
+ Get(rp RemoteProvider) (io.Reader, error)
+ Watch(rp RemoteProvider) (io.Reader, error)
+ WatchChannel(rp RemoteProvider)(<-chan *RemoteResponse, chan bool)
+}
+
+// RemoteConfig is optional, see the remote package
+var RemoteConfig remoteConfigFactory
+
+// UnsupportedConfigError denotes encountering an unsupported
+// configuration filetype.
+type UnsupportedConfigError string
+
+// Error returns the formatted configuration error.
+func (str UnsupportedConfigError) Error() string {
+ return fmt.Sprintf("Unsupported Config Type %q", string(str))
+}
+
+// UnsupportedRemoteProviderError denotes encountering an unsupported remote
+// provider. Currently only etcd and Consul are
+// supported.
+type UnsupportedRemoteProviderError string
+
+// Error returns the formatted remote provider error.
+func (str UnsupportedRemoteProviderError) Error() string {
+ return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))
+}
+
+// RemoteConfigError denotes encountering an error while trying to
+// pull the configuration from the remote provider.
+type RemoteConfigError string
+
+// Error returns the formatted remote provider error
+func (rce RemoteConfigError) Error() string {
+ return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
+}
+
+// ConfigFileNotFoundError denotes failing to find configuration file.
+type ConfigFileNotFoundError struct {
+ name, locations string
+}
+
+// Error returns the formatted configuration error.
+func (fnfe ConfigFileNotFoundError) Error() string {
+ return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations)
+}
+
+// Viper is a prioritized configuration registry. It
+// maintains a set of configuration sources, fetches
+// values to populate those, and provides them according
+// to the source's priority.
+// The priority of the sources is the following:
+// 1. overrides
+// 2. flags
+// 3. env. variables
+// 4. config file
+// 5. key/value store
+// 6. defaults
+//
+// For example, if values from the following sources were loaded:
+//
+// Defaults : {
+// "secret": "",
+// "user": "default",
+// "endpoint": "https://localhost"
+// }
+// Config : {
+// "user": "root"
+// "secret": "defaultsecret"
+// }
+// Env : {
+// "secret": "somesecretkey"
+// }
+//
+// The resulting config will have the following values:
+//
+// {
+// "secret": "somesecretkey",
+// "user": "root",
+// "endpoint": "https://localhost"
+// }
+type Viper struct {
+ // Delimiter that separates a list of keys
+ // used to access a nested value in one go
+ keyDelim string
+
+ // A set of paths to look for the config file in
+ configPaths []string
+
+ // The filesystem to read config from.
+ fs afero.Fs
+
+ // A set of remote providers to search for the configuration
+ remoteProviders []*defaultRemoteProvider
+
+ // Name of file to look for inside the path
+ configName string
+ configFile string
+ configType string
+ envPrefix string
+
+ automaticEnvApplied bool
+ envKeyReplacer *strings.Replacer
+
+ config map[string]interface{}
+ override map[string]interface{}
+ defaults map[string]interface{}
+ kvstore map[string]interface{}
+ pflags map[string]FlagValue
+ env map[string]string
+ aliases map[string]string
+ typeByDefValue bool
+
+ onConfigChange func(fsnotify.Event)
+}
+
+// New returns an initialized Viper instance.
+func New() *Viper {
+ v := new(Viper)
+ v.keyDelim = "."
+ v.configName = "config"
+ v.fs = afero.NewOsFs()
+ v.config = make(map[string]interface{})
+ v.override = make(map[string]interface{})
+ v.defaults = make(map[string]interface{})
+ v.kvstore = make(map[string]interface{})
+ v.pflags = make(map[string]FlagValue)
+ v.env = make(map[string]string)
+ v.aliases = make(map[string]string)
+ v.typeByDefValue = false
+
+ return v
+}
+
+// Intended for testing, will reset all to default settings.
+// In the public interface for the viper package so applications
+// can use it in their testing as well.
+func Reset() {
+ v = New()
+ SupportedExts = []string{"json", "toml", "yaml", "yml", "hcl"}
+ SupportedRemoteProviders = []string{"etcd", "consul"}
+}
+
+type defaultRemoteProvider struct {
+ provider string
+ endpoint string
+ path string
+ secretKeyring string
+}
+
+func (rp defaultRemoteProvider) Provider() string {
+ return rp.provider
+}
+
+func (rp defaultRemoteProvider) Endpoint() string {
+ return rp.endpoint
+}
+
+func (rp defaultRemoteProvider) Path() string {
+ return rp.path
+}
+
+func (rp defaultRemoteProvider) SecretKeyring() string {
+ return rp.secretKeyring
+}
+
+// RemoteProvider stores the configuration necessary
+// to connect to a remote key/value store.
+// Optional secretKeyring to unencrypt encrypted values
+// can be provided.
+type RemoteProvider interface {
+ Provider() string
+ Endpoint() string
+ Path() string
+ SecretKeyring() string
+}
+
+// SupportedExts are universally supported extensions.
+var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"}
+
+// SupportedRemoteProviders are universally supported remote providers.
+var SupportedRemoteProviders = []string{"etcd", "consul"}
+
+func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
+func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
+ v.onConfigChange = run
+}
+
+func WatchConfig() { v.WatchConfig() }
+func (v *Viper) WatchConfig() {
+ go func() {
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer watcher.Close()
+
+ // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
+ filename, err := v.getConfigFile()
+ if err != nil {
+ log.Println("error:", err)
+ return
+ }
+
+ configFile := filepath.Clean(filename)
+ configDir, _ := filepath.Split(configFile)
+
+ done := make(chan bool)
+ go func() {
+ for {
+ select {
+ case event := <-watcher.Events:
+ // we only care about the config file
+ if filepath.Clean(event.Name) == configFile {
+ if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
+ err := v.ReadInConfig()
+ if err != nil {
+ log.Println("error:", err)
+ }
+ v.onConfigChange(event)
+ }
+ }
+ case err := <-watcher.Errors:
+ log.Println("error:", err)
+ }
+ }
+ }()
+
+ watcher.Add(configDir)
+ <-done
+ }()
+}
+
+// SetConfigFile explicitly defines the path, name and extension of the config file
+// Viper will use this and not check any of the config paths
+func SetConfigFile(in string) { v.SetConfigFile(in) }
+func (v *Viper) SetConfigFile(in string) {
+ if in != "" {
+ v.configFile = in
+ }
+}
+
+// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
+// E.g. if your prefix is "spf", the env registry
+// will look for env. variables that start with "SPF_"
+func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
+func (v *Viper) SetEnvPrefix(in string) {
+ if in != "" {
+ v.envPrefix = in
+ }
+}
+
+func (v *Viper) mergeWithEnvPrefix(in string) string {
+ if v.envPrefix != "" {
+ return strings.ToUpper(v.envPrefix + "_" + in)
+ }
+
+ return strings.ToUpper(in)
+}
+
+// TODO: should getEnv logic be moved into find(). Can generalize the use of
+// rewriting keys many things, Ex: Get('someKey') -> some_key
+// (cammel case to snake case for JSON keys perhaps)
+
+// getEnv is a wrapper around os.Getenv which replaces characters in the original
+// key. This allows env vars which have different keys then the config object
+// keys
+func (v *Viper) getEnv(key string) string {
+ if v.envKeyReplacer != nil {
+ key = v.envKeyReplacer.Replace(key)
+ }
+ return os.Getenv(key)
+}
+
+// ConfigFileUsed returns the file used to populate the config registry
+func ConfigFileUsed() string { return v.ConfigFileUsed() }
+func (v *Viper) ConfigFileUsed() string { return v.configFile }
+
+// AddConfigPath adds a path for Viper to search for the config file in.
+// Can be called multiple times to define multiple search paths.
+func AddConfigPath(in string) { v.AddConfigPath(in) }
+func (v *Viper) AddConfigPath(in string) {
+ if in != "" {
+ absin := absPathify(in)
+ jww.INFO.Println("adding", absin, "to paths to search")
+ if !stringInSlice(absin, v.configPaths) {
+ v.configPaths = append(v.configPaths, absin)
+ }
+ }
+}
+
+// AddRemoteProvider adds a remote configuration source.
+// Remote Providers are searched in the order they are added.
+// provider is a string value, "etcd" or "consul" are currently supported.
+// endpoint is the url. etcd requires http://ip:port consul requires ip:port
+// path is the path in the k/v store to retrieve configuration
+// To retrieve a config file called myapp.json from /configs/myapp.json
+// you should set path to /configs and set config name (SetConfigName()) to
+// "myapp"
+func AddRemoteProvider(provider, endpoint, path string) error {
+ return v.AddRemoteProvider(provider, endpoint, path)
+}
+func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
+ if !stringInSlice(provider, SupportedRemoteProviders) {
+ return UnsupportedRemoteProviderError(provider)
+ }
+ if provider != "" && endpoint != "" {
+ jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
+ rp := &defaultRemoteProvider{
+ endpoint: endpoint,
+ provider: provider,
+ path: path,
+ }
+ if !v.providerPathExists(rp) {
+ v.remoteProviders = append(v.remoteProviders, rp)
+ }
+ }
+ return nil
+}
+
+// AddSecureRemoteProvider adds a remote configuration source.
+// Secure Remote Providers are searched in the order they are added.
+// provider is a string value, "etcd" or "consul" are currently supported.
+// endpoint is the url. etcd requires http://ip:port consul requires ip:port
+// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
+// path is the path in the k/v store to retrieve configuration
+// To retrieve a config file called myapp.json from /configs/myapp.json
+// you should set path to /configs and set config name (SetConfigName()) to
+// "myapp"
+// Secure Remote Providers are implemented with github.com/xordataexchange/crypt
+func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
+ return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
+}
+
+func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
+ if !stringInSlice(provider, SupportedRemoteProviders) {
+ return UnsupportedRemoteProviderError(provider)
+ }
+ if provider != "" && endpoint != "" {
+ jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
+ rp := &defaultRemoteProvider{
+ endpoint: endpoint,
+ provider: provider,
+ path: path,
+ secretKeyring: secretkeyring,
+ }
+ if !v.providerPathExists(rp) {
+ v.remoteProviders = append(v.remoteProviders, rp)
+ }
+ }
+ return nil
+}
+
+func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
+ for _, y := range v.remoteProviders {
+ if reflect.DeepEqual(y, p) {
+ return true
+ }
+ }
+ return false
+}
+
+// searchMap recursively searches for a value for path in source map.
+// Returns nil if not found.
+// Note: This assumes that the path entries and map keys are lower cased.
+func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
+ if len(path) == 0 {
+ return source
+ }
+
+ next, ok := source[path[0]]
+ if ok {
+ // Fast path
+ if len(path) == 1 {
+ return next
+ }
+
+ // Nested case
+ switch next.(type) {
+ case map[interface{}]interface{}:
+ return v.searchMap(cast.ToStringMap(next), path[1:])
+ case map[string]interface{}:
+ // Type assertion is safe here since it is only reached
+ // if the type of `next` is the same as the type being asserted
+ return v.searchMap(next.(map[string]interface{}), path[1:])
+ default:
+ // got a value but nested key expected, return "nil" for not found
+ return nil
+ }
+ }
+ return nil
+}
+
+// searchMapWithPathPrefixes recursively searches for a value for path in source map.
+//
+// While searchMap() considers each path element as a single map key, this
+// function searches for, and prioritizes, merged path elements.
+// e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar"
+// is also defined, this latter value is returned for path ["foo", "bar"].
+//
+// This should be useful only at config level (other maps may not contain dots
+// in their keys).
+//
+// Note: This assumes that the path entries and map keys are lower cased.
+func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} {
+ if len(path) == 0 {
+ return source
+ }
+
+ // search for path prefixes, starting from the longest one
+ for i := len(path); i > 0; i-- {
+ prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
+
+ next, ok := source[prefixKey]
+ if ok {
+ // Fast path
+ if i == len(path) {
+ return next
+ }
+
+ // Nested case
+ var val interface{}
+ switch next.(type) {
+ case map[interface{}]interface{}:
+ val = v.searchMapWithPathPrefixes(cast.ToStringMap(next), path[i:])
+ case map[string]interface{}:
+ // Type assertion is safe here since it is only reached
+ // if the type of `next` is the same as the type being asserted
+ val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:])
+ default:
+ // got a value but nested key expected, do nothing and look for next prefix
+ }
+ if val != nil {
+ return val
+ }
+ }
+ }
+
+ // not found
+ return nil
+}
+
+// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
+// on its path in the map.
+// e.g., if "foo.bar" has a value in the given map, it “shadows”
+// "foo.bar.baz" in a lower-priority map
+func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
+ var parentVal interface{}
+ for i := 1; i < len(path); i++ {
+ parentVal = v.searchMap(m, path[0:i])
+ if parentVal == nil {
+ // not found, no need to add more path elements
+ return ""
+ }
+ switch parentVal.(type) {
+ case map[interface{}]interface{}:
+ continue
+ case map[string]interface{}:
+ continue
+ default:
+ // parentVal is a regular value which shadows "path"
+ return strings.Join(path[0:i], v.keyDelim)
+ }
+ }
+ return ""
+}
+
+// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
+// in a sub-path of the map.
+// e.g., if "foo.bar" has a value in the given map, it “shadows”
+// "foo.bar.baz" in a lower-priority map
+func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
+ // unify input map
+ var m map[string]interface{}
+ switch mi.(type) {
+ case map[string]string, map[string]FlagValue:
+ m = cast.ToStringMap(mi)
+ default:
+ return ""
+ }
+
+ // scan paths
+ var parentKey string
+ for i := 1; i < len(path); i++ {
+ parentKey = strings.Join(path[0:i], v.keyDelim)
+ if _, ok := m[parentKey]; ok {
+ return parentKey
+ }
+ }
+ return ""
+}
+
+// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
+// in the environment, when automatic env is on.
+// e.g., if "foo.bar" has a value in the environment, it “shadows”
+// "foo.bar.baz" in a lower-priority map
+func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
+ var parentKey string
+ var val string
+ for i := 1; i < len(path); i++ {
+ parentKey = strings.Join(path[0:i], v.keyDelim)
+ if val = v.getEnv(v.mergeWithEnvPrefix(parentKey)); val != "" {
+ return parentKey
+ }
+ }
+ return ""
+}
+
+// SetTypeByDefaultValue enables or disables the inference of a key value's
+// type when the Get function is used based upon a key's default value as
+// opposed to the value returned based on the normal fetch logic.
+//
+// For example, if a key has a default value of []string{} and the same key
+// is set via an environment variable to "a b c", a call to the Get function
+// would return a string slice for the key if the key's type is inferred by
+// the default value and the Get function would return:
+//
+// []string {"a", "b", "c"}
+//
+// Otherwise the Get function would return:
+//
+// "a b c"
+func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
+func (v *Viper) SetTypeByDefaultValue(enable bool) {
+ v.typeByDefValue = enable
+}
+
+// GetViper gets the global Viper instance.
+func GetViper() *Viper {
+ return v
+}
+
+// Get can retrieve any value given the key to use.
+// Get is case-insensitive for a key.
+// Get has the behavior of returning the value associated with the first
+// place from where it is set. Viper will check in the following order:
+// override, flag, env, config file, key/value store, default
+//
+// Get returns an interface. For a specific value use one of the Get____ methods.
+func Get(key string) interface{} { return v.Get(key) }
+func (v *Viper) Get(key string) interface{} {
+ lcaseKey := strings.ToLower(key)
+ val := v.find(lcaseKey)
+ if val == nil {
+ return nil
+ }
+
+ valType := val
+ if v.typeByDefValue {
+ // TODO(bep) this branch isn't covered by a single test.
+ path := strings.Split(lcaseKey, v.keyDelim)
+ defVal := v.searchMap(v.defaults, path)
+ if defVal != nil {
+ valType = defVal
+ }
+ }
+
+ switch valType.(type) {
+ case bool:
+ return cast.ToBool(val)
+ case string:
+ return cast.ToString(val)
+ case int64, int32, int16, int8, int:
+ return cast.ToInt(val)
+ case float64, float32:
+ return cast.ToFloat64(val)
+ case time.Time:
+ return cast.ToTime(val)
+ case time.Duration:
+ return cast.ToDuration(val)
+ case []string:
+ return cast.ToStringSlice(val)
+ }
+ return val
+}
+
+// Sub returns new Viper instance representing a sub tree of this instance.
+// Sub is case-insensitive for a key.
+func Sub(key string) *Viper { return v.Sub(key) }
+func (v *Viper) Sub(key string) *Viper {
+ subv := New()
+ data := v.Get(key)
+ if data == nil {
+ return nil
+ }
+
+ if reflect.TypeOf(data).Kind() == reflect.Map {
+ subv.config = cast.ToStringMap(data)
+ return subv
+ }
+ return nil
+}
+
+// GetString returns the value associated with the key as a string.
+func GetString(key string) string { return v.GetString(key) }
+func (v *Viper) GetString(key string) string {
+ return cast.ToString(v.Get(key))
+}
+
+// GetBool returns the value associated with the key as a boolean.
+func GetBool(key string) bool { return v.GetBool(key) }
+func (v *Viper) GetBool(key string) bool {
+ return cast.ToBool(v.Get(key))
+}
+
+// GetInt returns the value associated with the key as an integer.
+func GetInt(key string) int { return v.GetInt(key) }
+func (v *Viper) GetInt(key string) int {
+ return cast.ToInt(v.Get(key))
+}
+
+// GetInt64 returns the value associated with the key as an integer.
+func GetInt64(key string) int64 { return v.GetInt64(key) }
+func (v *Viper) GetInt64(key string) int64 {
+ return cast.ToInt64(v.Get(key))
+}
+
+// GetFloat64 returns the value associated with the key as a float64.
+func GetFloat64(key string) float64 { return v.GetFloat64(key) }
+func (v *Viper) GetFloat64(key string) float64 {
+ return cast.ToFloat64(v.Get(key))
+}
+
+// GetTime returns the value associated with the key as time.
+func GetTime(key string) time.Time { return v.GetTime(key) }
+func (v *Viper) GetTime(key string) time.Time {
+ return cast.ToTime(v.Get(key))
+}
+
+// GetDuration returns the value associated with the key as a duration.
+func GetDuration(key string) time.Duration { return v.GetDuration(key) }
+func (v *Viper) GetDuration(key string) time.Duration {
+ return cast.ToDuration(v.Get(key))
+}
+
+// GetStringSlice returns the value associated with the key as a slice of strings.
+func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
+func (v *Viper) GetStringSlice(key string) []string {
+ return cast.ToStringSlice(v.Get(key))
+}
+
+// GetStringMap returns the value associated with the key as a map of interfaces.
+func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
+func (v *Viper) GetStringMap(key string) map[string]interface{} {
+ return cast.ToStringMap(v.Get(key))
+}
+
+// GetStringMapString returns the value associated with the key as a map of strings.
+func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
+func (v *Viper) GetStringMapString(key string) map[string]string {
+ return cast.ToStringMapString(v.Get(key))
+}
+
+// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
+func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
+func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
+ return cast.ToStringMapStringSlice(v.Get(key))
+}
+
+// GetSizeInBytes returns the size of the value associated with the given key
+// in bytes.
+func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
+func (v *Viper) GetSizeInBytes(key string) uint {
+ sizeStr := cast.ToString(v.Get(key))
+ return parseSizeInBytes(sizeStr)
+}
+
+// UnmarshalKey takes a single key and unmarshals it into a Struct.
+func UnmarshalKey(key string, rawVal interface{}) error { return v.UnmarshalKey(key, rawVal) }
+func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error {
+ return mapstructure.Decode(v.Get(key), rawVal)
+}
+
+// Unmarshal unmarshals the config into a Struct. Make sure that the tags
+// on the fields of the structure are properly set.
+func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) }
+func (v *Viper) Unmarshal(rawVal interface{}) error {
+ err := decode(v.AllSettings(), defaultDecoderConfig(rawVal))
+
+ if err != nil {
+ return err
+ }
+
+ v.insensitiviseMaps()
+
+ return nil
+}
+
+// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot
+// of time.Duration values
+func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig {
+ return &mapstructure.DecoderConfig{
+ Metadata: nil,
+ Result: output,
+ WeaklyTypedInput: true,
+ DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
+ }
+}
+
+// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality
+func decode(input interface{}, config *mapstructure.DecoderConfig) error {
+ decoder, err := mapstructure.NewDecoder(config)
+ if err != nil {
+ return err
+ }
+ return decoder.Decode(input)
+}
+
+// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
+// in the destination struct.
+func (v *Viper) UnmarshalExact(rawVal interface{}) error {
+ config := defaultDecoderConfig(rawVal)
+ config.ErrorUnused = true
+
+ err := decode(v.AllSettings(), config)
+
+ if err != nil {
+ return err
+ }
+
+ v.insensitiviseMaps()
+
+ return nil
+}
+
+// BindPFlags binds a full flag set to the configuration, using each flag's long
+// name as the config key.
+func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) }
+func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
+ return v.BindFlagValues(pflagValueSet{flags})
+}
+
+// BindPFlag binds a specific key to a pflag (as used by cobra).
+// Example (where serverCmd is a Cobra instance):
+//
+// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
+// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
+//
+func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
+func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
+ return v.BindFlagValue(key, pflagValue{flag})
+}
+
+// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long
+// name as the config key.
+func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) }
+func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
+ flags.VisitAll(func(flag FlagValue) {
+ if err = v.BindFlagValue(flag.Name(), flag); err != nil {
+ return
+ }
+ })
+ return nil
+}
+
+// BindFlagValue binds a specific key to a FlagValue.
+// Example(where serverCmd is a Cobra instance):
+//
+// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
+// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
+//
+func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) }
+func (v *Viper) BindFlagValue(key string, flag FlagValue) error {
+ if flag == nil {
+ return fmt.Errorf("flag for %q is nil", key)
+ }
+ v.pflags[strings.ToLower(key)] = flag
+ return nil
+}
+
+// BindEnv binds a Viper key to a ENV variable.
+// ENV variables are case sensitive.
+// If only a key is provided, it will use the env key matching the key, uppercased.
+// EnvPrefix will be used when set when env name is not provided.
+func BindEnv(input ...string) error { return v.BindEnv(input...) }
+func (v *Viper) BindEnv(input ...string) error {
+ var key, envkey string
+ if len(input) == 0 {
+ return fmt.Errorf("BindEnv missing key to bind to")
+ }
+
+ key = strings.ToLower(input[0])
+
+ if len(input) == 1 {
+ envkey = v.mergeWithEnvPrefix(key)
+ } else {
+ envkey = input[1]
+ }
+
+ v.env[key] = envkey
+
+ return nil
+}
+
+// Given a key, find the value.
+// Viper will check in the following order:
+// flag, env, config file, key/value store, default.
+// Viper will check to see if an alias exists first.
+// Note: this assumes a lower-cased key given.
+func (v *Viper) find(lcaseKey string) interface{} {
+
+ var (
+ val interface{}
+ exists bool
+ path = strings.Split(lcaseKey, v.keyDelim)
+ nested = len(path) > 1
+ )
+
+ // compute the path through the nested maps to the nested value
+ if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" {
+ return nil
+ }
+
+ // if the requested key is an alias, then return the proper key
+ lcaseKey = v.realKey(lcaseKey)
+ path = strings.Split(lcaseKey, v.keyDelim)
+ nested = len(path) > 1
+
+ // Set() override first
+ val = v.searchMap(v.override, path)
+ if val != nil {
+ return val
+ }
+ if nested && v.isPathShadowedInDeepMap(path, v.override) != "" {
+ return nil
+ }
+
+ // PFlag override next
+ flag, exists := v.pflags[lcaseKey]
+ if exists && flag.HasChanged() {
+ switch flag.ValueType() {
+ case "int", "int8", "int16", "int32", "int64":
+ return cast.ToInt(flag.ValueString())
+ case "bool":
+ return cast.ToBool(flag.ValueString())
+ case "stringSlice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ return strings.TrimSuffix(s, "]")
+ default:
+ return flag.ValueString()
+ }
+ }
+ if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" {
+ return nil
+ }
+
+ // Env override next
+ if v.automaticEnvApplied {
+ // even if it hasn't been registered, if automaticEnv is used,
+ // check any Get request
+ if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" {
+ return val
+ }
+ if nested && v.isPathShadowedInAutoEnv(path) != "" {
+ return nil
+ }
+ }
+ envkey, exists := v.env[lcaseKey]
+ if exists {
+ if val = v.getEnv(envkey); val != "" {
+ return val
+ }
+ }
+ if nested && v.isPathShadowedInFlatMap(path, v.env) != "" {
+ return nil
+ }
+
+ // Config file next
+ val = v.searchMapWithPathPrefixes(v.config, path)
+ if val != nil {
+ return val
+ }
+ if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
+ return nil
+ }
+
+ // K/V store next
+ val = v.searchMap(v.kvstore, path)
+ if val != nil {
+ return val
+ }
+ if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" {
+ return nil
+ }
+
+ // Default next
+ val = v.searchMap(v.defaults, path)
+ if val != nil {
+ return val
+ }
+ if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" {
+ return nil
+ }
+
+ // last chance: if no other value is returned and a flag does exist for the value,
+ // get the flag's value even if the flag's value has not changed
+ if flag, exists := v.pflags[lcaseKey]; exists {
+ switch flag.ValueType() {
+ case "int", "int8", "int16", "int32", "int64":
+ return cast.ToInt(flag.ValueString())
+ case "bool":
+ return cast.ToBool(flag.ValueString())
+ case "stringSlice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ return strings.TrimSuffix(s, "]")
+ default:
+ return flag.ValueString()
+ }
+ }
+ // last item, no need to check shadowing
+
+ return nil
+}
+
+// IsSet checks to see if the key has been set in any of the data locations.
+// IsSet is case-insensitive for a key.
+func IsSet(key string) bool { return v.IsSet(key) }
+func (v *Viper) IsSet(key string) bool {
+ lcaseKey := strings.ToLower(key)
+ val := v.find(lcaseKey)
+ return val != nil
+}
+
+// AutomaticEnv has Viper check ENV variables for all.
+// keys set in config, default & flags
+func AutomaticEnv() { v.AutomaticEnv() }
+func (v *Viper) AutomaticEnv() {
+ v.automaticEnvApplied = true
+}
+
+// SetEnvKeyReplacer sets the strings.Replacer on the viper object
+// Useful for mapping an environmental variable to a key that does
+// not match it.
+func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
+func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
+ v.envKeyReplacer = r
+}
+
+// Aliases provide another accessor for the same key.
+// This enables one to change a name without breaking the application
+func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
+func (v *Viper) RegisterAlias(alias string, key string) {
+ v.registerAlias(alias, strings.ToLower(key))
+}
+
+func (v *Viper) registerAlias(alias string, key string) {
+ alias = strings.ToLower(alias)
+ if alias != key && alias != v.realKey(key) {
+ _, exists := v.aliases[alias]
+
+ if !exists {
+ // if we alias something that exists in one of the maps to another
+ // name, we'll never be able to get that value using the original
+ // name, so move the config value to the new realkey.
+ if val, ok := v.config[alias]; ok {
+ delete(v.config, alias)
+ v.config[key] = val
+ }
+ if val, ok := v.kvstore[alias]; ok {
+ delete(v.kvstore, alias)
+ v.kvstore[key] = val
+ }
+ if val, ok := v.defaults[alias]; ok {
+ delete(v.defaults, alias)
+ v.defaults[key] = val
+ }
+ if val, ok := v.override[alias]; ok {
+ delete(v.override, alias)
+ v.override[key] = val
+ }
+ v.aliases[alias] = key
+ }
+ } else {
+ jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key))
+ }
+}
+
+func (v *Viper) realKey(key string) string {
+ newkey, exists := v.aliases[key]
+ if exists {
+ jww.DEBUG.Println("Alias", key, "to", newkey)
+ return v.realKey(newkey)
+ }
+ return key
+}
+
+// InConfig checks to see if the given key (or an alias) is in the config file.
+func InConfig(key string) bool { return v.InConfig(key) }
+func (v *Viper) InConfig(key string) bool {
+ // if the requested key is an alias, then return the proper key
+ key = v.realKey(key)
+
+ _, exists := v.config[key]
+ return exists
+}
+
+// SetDefault sets the default value for this key.
+// SetDefault is case-insensitive for a key.
+// Default only used when no value is provided by the user via flag, config or ENV.
+func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
+func (v *Viper) SetDefault(key string, value interface{}) {
+ // If alias passed in, then set the proper default
+ key = v.realKey(strings.ToLower(key))
+ value = toCaseInsensitiveValue(value)
+
+ path := strings.Split(key, v.keyDelim)
+ lastKey := strings.ToLower(path[len(path)-1])
+ deepestMap := deepSearch(v.defaults, path[0:len(path)-1])
+
+ // set innermost value
+ deepestMap[lastKey] = value
+}
+
+// Set sets the value for the key in the override regiser.
+// Set is case-insensitive for a key.
+// Will be used instead of values obtained via
+// flags, config file, ENV, default, or key/value store.
+func Set(key string, value interface{}) { v.Set(key, value) }
+func (v *Viper) Set(key string, value interface{}) {
+ // If alias passed in, then set the proper override
+ key = v.realKey(strings.ToLower(key))
+ value = toCaseInsensitiveValue(value)
+
+ path := strings.Split(key, v.keyDelim)
+ lastKey := strings.ToLower(path[len(path)-1])
+ deepestMap := deepSearch(v.override, path[0:len(path)-1])
+
+ // set innermost value
+ deepestMap[lastKey] = value
+}
+
+// ReadInConfig will discover and load the configuration file from disk
+// and key/value stores, searching in one of the defined paths.
+func ReadInConfig() error { return v.ReadInConfig() }
+func (v *Viper) ReadInConfig() error {
+ jww.INFO.Println("Attempting to read in config file")
+ filename, err := v.getConfigFile()
+ if err != nil {
+ return err
+ }
+
+ if !stringInSlice(v.getConfigType(), SupportedExts) {
+ return UnsupportedConfigError(v.getConfigType())
+ }
+
+ file, err := afero.ReadFile(v.fs, filename)
+ if err != nil {
+ return err
+ }
+
+ config := make(map[string]interface{})
+
+ err = v.unmarshalReader(bytes.NewReader(file), config)
+ if err != nil {
+ return err
+ }
+
+ v.config = config
+ return nil
+}
+
+// MergeInConfig merges a new configuration with an existing config.
+func MergeInConfig() error { return v.MergeInConfig() }
+func (v *Viper) MergeInConfig() error {
+ jww.INFO.Println("Attempting to merge in config file")
+ filename, err := v.getConfigFile()
+ if err != nil {
+ return err
+ }
+
+ if !stringInSlice(v.getConfigType(), SupportedExts) {
+ return UnsupportedConfigError(v.getConfigType())
+ }
+
+ file, err := afero.ReadFile(v.fs, filename)
+ if err != nil {
+ return err
+ }
+
+ return v.MergeConfig(bytes.NewReader(file))
+}
+
+// ReadConfig will read a configuration file, setting existing keys to nil if the
+// key does not exist in the file.
+func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
+func (v *Viper) ReadConfig(in io.Reader) error {
+ v.config = make(map[string]interface{})
+ return v.unmarshalReader(in, v.config)
+}
+
+// MergeConfig merges a new configuration with an existing config.
+func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
+func (v *Viper) MergeConfig(in io.Reader) error {
+ if v.config == nil {
+ v.config = make(map[string]interface{})
+ }
+ cfg := make(map[string]interface{})
+ if err := v.unmarshalReader(in, cfg); err != nil {
+ return err
+ }
+ mergeMaps(cfg, v.config, nil)
+ return nil
+}
+
+func keyExists(k string, m map[string]interface{}) string {
+ lk := strings.ToLower(k)
+ for mk := range m {
+ lmk := strings.ToLower(mk)
+ if lmk == lk {
+ return mk
+ }
+ }
+ return ""
+}
+
+func castToMapStringInterface(
+ src map[interface{}]interface{}) map[string]interface{} {
+ tgt := map[string]interface{}{}
+ for k, v := range src {
+ tgt[fmt.Sprintf("%v", k)] = v
+ }
+ return tgt
+}
+
+func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
+ tgt := map[string]interface{}{}
+ for k, v := range src {
+ tgt[k] = v
+ }
+ return tgt
+}
+
+func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} {
+ tgt := map[string]interface{}{}
+ for k, v := range src {
+ tgt[k] = v
+ }
+ return tgt
+}
+
+// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's
+// insistence on parsing nested structures as `map[interface{}]interface{}`
+// instead of using a `string` as the key for nest structures beyond one level
+// deep. Both map types are supported as there is a go-yaml fork that uses
+// `map[string]interface{}` instead.
+func mergeMaps(
+ src, tgt map[string]interface{}, itgt map[interface{}]interface{}) {
+ for sk, sv := range src {
+ tk := keyExists(sk, tgt)
+ if tk == "" {
+ jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv)
+ tgt[sk] = sv
+ if itgt != nil {
+ itgt[sk] = sv
+ }
+ continue
+ }
+
+ tv, ok := tgt[tk]
+ if !ok {
+ jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv)
+ tgt[sk] = sv
+ if itgt != nil {
+ itgt[sk] = sv
+ }
+ continue
+ }
+
+ svType := reflect.TypeOf(sv)
+ tvType := reflect.TypeOf(tv)
+ if svType != tvType {
+ jww.ERROR.Printf(
+ "svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v",
+ sk, svType, tvType, sv, tv)
+ continue
+ }
+
+ jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v",
+ sk, svType, tvType, sv, tv)
+
+ switch ttv := tv.(type) {
+ case map[interface{}]interface{}:
+ jww.TRACE.Printf("merging maps (must convert)")
+ tsv := sv.(map[interface{}]interface{})
+ ssv := castToMapStringInterface(tsv)
+ stv := castToMapStringInterface(ttv)
+ mergeMaps(ssv, stv, ttv)
+ case map[string]interface{}:
+ jww.TRACE.Printf("merging maps")
+ mergeMaps(sv.(map[string]interface{}), ttv, nil)
+ default:
+ jww.TRACE.Printf("setting value")
+ tgt[tk] = sv
+ if itgt != nil {
+ itgt[tk] = sv
+ }
+ }
+ }
+}
+
+// ReadRemoteConfig attempts to get configuration from a remote source
+// and read it in the remote configuration registry.
+func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
+func (v *Viper) ReadRemoteConfig() error {
+ return v.getKeyValueConfig()
+}
+
+func WatchRemoteConfig() error { return v.WatchRemoteConfig() }
+func (v *Viper) WatchRemoteConfig() error {
+ return v.watchKeyValueConfig()
+}
+
+func (v *Viper) WatchRemoteConfigOnChannel() error {
+ return v.watchKeyValueConfigOnChannel()
+}
+
+// Unmarshall a Reader into a map.
+// Should probably be an unexported function.
+func unmarshalReader(in io.Reader, c map[string]interface{}) error {
+ return v.unmarshalReader(in, c)
+}
+
+func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
+ return unmarshallConfigReader(in, c, v.getConfigType())
+}
+
+func (v *Viper) insensitiviseMaps() {
+ insensitiviseMap(v.config)
+ insensitiviseMap(v.defaults)
+ insensitiviseMap(v.override)
+ insensitiviseMap(v.kvstore)
+}
+
+// Retrieve the first found remote configuration.
+func (v *Viper) getKeyValueConfig() error {
+ if RemoteConfig == nil {
+ return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
+ }
+
+ for _, rp := range v.remoteProviders {
+ val, err := v.getRemoteConfig(rp)
+ if err != nil {
+ continue
+ }
+ v.kvstore = val
+ return nil
+ }
+ return RemoteConfigError("No Files Found")
+}
+
+func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
+ reader, err := RemoteConfig.Get(provider)
+ if err != nil {
+ return nil, err
+ }
+ err = v.unmarshalReader(reader, v.kvstore)
+ return v.kvstore, err
+}
+
+// Retrieve the first found remote configuration.
+func (v *Viper) watchKeyValueConfigOnChannel() error {
+ for _, rp := range v.remoteProviders {
+ respc, _ := RemoteConfig.WatchChannel(rp)
+ //Todo: Add quit channel
+ go func(rc <-chan *RemoteResponse) {
+ for {
+ b := <-rc
+ reader := bytes.NewReader(b.Value)
+ v.unmarshalReader(reader, v.kvstore)
+ }
+ }(respc)
+ return nil
+ }
+ return RemoteConfigError("No Files Found")
+}
+
+// Retrieve the first found remote configuration.
+func (v *Viper) watchKeyValueConfig() error {
+ for _, rp := range v.remoteProviders {
+ val, err := v.watchRemoteConfig(rp)
+ if err != nil {
+ continue
+ }
+ v.kvstore = val
+ return nil
+ }
+ return RemoteConfigError("No Files Found")
+}
+
+func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
+ reader, err := RemoteConfig.Watch(provider)
+ if err != nil {
+ return nil, err
+ }
+ err = v.unmarshalReader(reader, v.kvstore)
+ return v.kvstore, err
+}
+
+// AllKeys returns all keys holding a value, regardless of where they are set.
+// Nested keys are returned with a v.keyDelim (= ".") separator
+func AllKeys() []string { return v.AllKeys() }
+func (v *Viper) AllKeys() []string {
+ m := map[string]bool{}
+ // add all paths, by order of descending priority to ensure correct shadowing
+ m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "")
+ m = v.flattenAndMergeMap(m, v.override, "")
+ m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags))
+ m = v.mergeFlatMap(m, castMapStringToMapInterface(v.env))
+ m = v.flattenAndMergeMap(m, v.config, "")
+ m = v.flattenAndMergeMap(m, v.kvstore, "")
+ m = v.flattenAndMergeMap(m, v.defaults, "")
+
+ // convert set of paths to list
+ a := []string{}
+ for x := range m {
+ a = append(a, x)
+ }
+ return a
+}
+
+// flattenAndMergeMap recursively flattens the given map into a map[string]bool
+// of key paths (used as a set, easier to manipulate than a []string):
+// - each path is merged into a single key string, delimited with v.keyDelim (= ".")
+// - if a path is shadowed by an earlier value in the initial shadow map,
+// it is skipped.
+// The resulting set of paths is merged to the given shadow set at the same time.
+func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
+ if shadow != nil && prefix != "" && shadow[prefix] {
+ // prefix is shadowed => nothing more to flatten
+ return shadow
+ }
+ if shadow == nil {
+ shadow = make(map[string]bool)
+ }
+
+ var m2 map[string]interface{}
+ if prefix != "" {
+ prefix += v.keyDelim
+ }
+ for k, val := range m {
+ fullKey := prefix + k
+ switch val.(type) {
+ case map[string]interface{}:
+ m2 = val.(map[string]interface{})
+ case map[interface{}]interface{}:
+ m2 = cast.ToStringMap(val)
+ default:
+ // immediate value
+ shadow[strings.ToLower(fullKey)] = true
+ continue
+ }
+ // recursively merge to shadow map
+ shadow = v.flattenAndMergeMap(shadow, m2, fullKey)
+ }
+ return shadow
+}
+
+// mergeFlatMap merges the given maps, excluding values of the second map
+// shadowed by values from the first map.
+func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool {
+ // scan keys
+outer:
+ for k, _ := range m {
+ path := strings.Split(k, v.keyDelim)
+ // scan intermediate paths
+ var parentKey string
+ for i := 1; i < len(path); i++ {
+ parentKey = strings.Join(path[0:i], v.keyDelim)
+ if shadow[parentKey] {
+ // path is shadowed, continue
+ continue outer
+ }
+ }
+ // add key
+ shadow[strings.ToLower(k)] = true
+ }
+ return shadow
+}
+
+// AllSettings merges all settings and returns them as a map[string]interface{}.
+func AllSettings() map[string]interface{} { return v.AllSettings() }
+func (v *Viper) AllSettings() map[string]interface{} {
+ m := map[string]interface{}{}
+ // start from the list of keys, and construct the map one value at a time
+ for _, k := range v.AllKeys() {
+ value := v.Get(k)
+ if value == nil {
+ // should not happen, since AllKeys() returns only keys holding a value,
+ // check just in case anything changes
+ continue
+ }
+ path := strings.Split(k, v.keyDelim)
+ lastKey := strings.ToLower(path[len(path)-1])
+ deepestMap := deepSearch(m, path[0:len(path)-1])
+ // set innermost value
+ deepestMap[lastKey] = value
+ }
+ return m
+}
+
+// SetFs sets the filesystem to use to read configuration.
+func SetFs(fs afero.Fs) { v.SetFs(fs) }
+func (v *Viper) SetFs(fs afero.Fs) {
+ v.fs = fs
+}
+
+// SetConfigName sets name for the config file.
+// Does not include extension.
+func SetConfigName(in string) { v.SetConfigName(in) }
+func (v *Viper) SetConfigName(in string) {
+ if in != "" {
+ v.configName = in
+ v.configFile = ""
+ }
+}
+
+// SetConfigType sets the type of the configuration returned by the
+// remote source, e.g. "json".
+func SetConfigType(in string) { v.SetConfigType(in) }
+func (v *Viper) SetConfigType(in string) {
+ if in != "" {
+ v.configType = in
+ }
+}
+
+func (v *Viper) getConfigType() string {
+ if v.configType != "" {
+ return v.configType
+ }
+
+ cf, err := v.getConfigFile()
+ if err != nil {
+ return ""
+ }
+
+ ext := filepath.Ext(cf)
+
+ if len(ext) > 1 {
+ return ext[1:]
+ }
+
+ return ""
+}
+
+func (v *Viper) getConfigFile() (string, error) {
+ // if explicitly set, then use it
+ if v.configFile != "" {
+ return v.configFile, nil
+ }
+
+ cf, err := v.findConfigFile()
+ if err != nil {
+ return "", err
+ }
+
+ v.configFile = cf
+ return v.getConfigFile()
+}
+
+func (v *Viper) searchInPath(in string) (filename string) {
+ jww.DEBUG.Println("Searching for config in ", in)
+ for _, ext := range SupportedExts {
+ jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext))
+ if b, _ := exists(filepath.Join(in, v.configName+"."+ext)); b {
+ jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext))
+ return filepath.Join(in, v.configName+"."+ext)
+ }
+ }
+
+ return ""
+}
+
+// Search all configPaths for any config file.
+// Returns the first path that exists (and is a config file).
+func (v *Viper) findConfigFile() (string, error) {
+
+ jww.INFO.Println("Searching for config in ", v.configPaths)
+
+ for _, cp := range v.configPaths {
+ file := v.searchInPath(cp)
+ if file != "" {
+ return file, nil
+ }
+ }
+ return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
+}
+
+// Debug prints all configuration registries for debugging
+// purposes.
+func Debug() { v.Debug() }
+func (v *Viper) Debug() {
+ fmt.Printf("Aliases:\n%#v\n", v.aliases)
+ fmt.Printf("Override:\n%#v\n", v.override)
+ fmt.Printf("PFlags:\n%#v\n", v.pflags)
+ fmt.Printf("Env:\n%#v\n", v.env)
+ fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore)
+ fmt.Printf("Config:\n%#v\n", v.config)
+ fmt.Printf("Defaults:\n%#v\n", v.defaults)
+}
diff --git a/vendor/github.com/spf13/viper/viper_test.go b/vendor/github.com/spf13/viper/viper_test.go
new file mode 100644
index 000000000..cd7b65cbd
--- /dev/null
+++ b/vendor/github.com/spf13/viper/viper_test.go
@@ -0,0 +1,1154 @@
+// Copyright © 2014 Steve Francia <spf@spf13.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package viper
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/spf13/cast"
+
+ "github.com/spf13/pflag"
+ "github.com/stretchr/testify/assert"
+)
+
+var yamlExample = []byte(`Hacker: true
+name: steve
+hobbies:
+- skateboarding
+- snowboarding
+- go
+clothing:
+ jacket: leather
+ trousers: denim
+ pants:
+ size: large
+age: 35
+eyes : brown
+beard: true
+`)
+
+var yamlExampleWithExtras = []byte(`Existing: true
+Bogus: true
+`)
+
+type testUnmarshalExtra struct {
+ Existing bool
+}
+
+var tomlExample = []byte(`
+title = "TOML Example"
+
+[owner]
+organization = "MongoDB"
+Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
+dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
+
+var jsonExample = []byte(`{
+"id": "0001",
+"type": "donut",
+"name": "Cake",
+"ppu": 0.55,
+"batters": {
+ "batter": [
+ { "type": "Regular" },
+ { "type": "Chocolate" },
+ { "type": "Blueberry" },
+ { "type": "Devil's Food" }
+ ]
+ }
+}`)
+
+var hclExample = []byte(`
+id = "0001"
+type = "donut"
+name = "Cake"
+ppu = 0.55
+foos {
+ foo {
+ key = 1
+ }
+ foo {
+ key = 2
+ }
+ foo {
+ key = 3
+ }
+ foo {
+ key = 4
+ }
+}`)
+
+var propertiesExample = []byte(`
+p_id: 0001
+p_type: donut
+p_name: Cake
+p_ppu: 0.55
+p_batters.batter.type: Regular
+`)
+
+var remoteExample = []byte(`{
+"id":"0002",
+"type":"cronut",
+"newkey":"remote"
+}`)
+
+func initConfigs() {
+ Reset()
+ var r io.Reader
+ SetConfigType("yaml")
+ r = bytes.NewReader(yamlExample)
+ unmarshalReader(r, v.config)
+
+ SetConfigType("json")
+ r = bytes.NewReader(jsonExample)
+ unmarshalReader(r, v.config)
+
+ SetConfigType("hcl")
+ r = bytes.NewReader(hclExample)
+ unmarshalReader(r, v.config)
+
+ SetConfigType("properties")
+ r = bytes.NewReader(propertiesExample)
+ unmarshalReader(r, v.config)
+
+ SetConfigType("toml")
+ r = bytes.NewReader(tomlExample)
+ unmarshalReader(r, v.config)
+
+ SetConfigType("json")
+ remote := bytes.NewReader(remoteExample)
+ unmarshalReader(remote, v.kvstore)
+}
+
+func initConfig(typ, config string) {
+ Reset()
+ SetConfigType(typ)
+ r := strings.NewReader(config)
+
+ if err := unmarshalReader(r, v.config); err != nil {
+ panic(err)
+ }
+}
+
+func initYAML() {
+ initConfig("yaml", string(yamlExample))
+}
+
+func initJSON() {
+ Reset()
+ SetConfigType("json")
+ r := bytes.NewReader(jsonExample)
+
+ unmarshalReader(r, v.config)
+}
+
+func initProperties() {
+ Reset()
+ SetConfigType("properties")
+ r := bytes.NewReader(propertiesExample)
+
+ unmarshalReader(r, v.config)
+}
+
+func initTOML() {
+ Reset()
+ SetConfigType("toml")
+ r := bytes.NewReader(tomlExample)
+
+ unmarshalReader(r, v.config)
+}
+
+func initHcl() {
+ Reset()
+ SetConfigType("hcl")
+ r := bytes.NewReader(hclExample)
+
+ unmarshalReader(r, v.config)
+}
+
+// make directories for testing
+func initDirs(t *testing.T) (string, string, func()) {
+
+ var (
+ testDirs = []string{`a a`, `b`, `c\c`, `D_`}
+ config = `improbable`
+ )
+
+ root, err := ioutil.TempDir("", "")
+
+ cleanup := true
+ defer func() {
+ if cleanup {
+ os.Chdir("..")
+ os.RemoveAll(root)
+ }
+ }()
+
+ assert.Nil(t, err)
+
+ err = os.Chdir(root)
+ assert.Nil(t, err)
+
+ for _, dir := range testDirs {
+ err = os.Mkdir(dir, 0750)
+ assert.Nil(t, err)
+
+ err = ioutil.WriteFile(
+ path.Join(dir, config+".toml"),
+ []byte("key = \"value is "+dir+"\"\n"),
+ 0640)
+ assert.Nil(t, err)
+ }
+
+ cleanup = false
+ return root, config, func() {
+ os.Chdir("..")
+ os.RemoveAll(root)
+ }
+}
+
+//stubs for PFlag Values
+type stringValue string
+
+func newStringValue(val string, p *string) *stringValue {
+ *p = val
+ return (*stringValue)(p)
+}
+
+func (s *stringValue) Set(val string) error {
+ *s = stringValue(val)
+ return nil
+}
+
+func (s *stringValue) Type() string {
+ return "string"
+}
+
+func (s *stringValue) String() string {
+ return fmt.Sprintf("%s", *s)
+}
+
+func TestBasics(t *testing.T) {
+ SetConfigFile("/tmp/config.yaml")
+ filename, err := v.getConfigFile()
+ assert.Equal(t, "/tmp/config.yaml", filename)
+ assert.NoError(t, err)
+}
+
+func TestDefault(t *testing.T) {
+ SetDefault("age", 45)
+ assert.Equal(t, 45, Get("age"))
+
+ SetDefault("clothing.jacket", "slacks")
+ assert.Equal(t, "slacks", Get("clothing.jacket"))
+
+ SetConfigType("yaml")
+ err := ReadConfig(bytes.NewBuffer(yamlExample))
+
+ assert.NoError(t, err)
+ assert.Equal(t, "leather", Get("clothing.jacket"))
+}
+
+func TestUnmarshalling(t *testing.T) {
+ SetConfigType("yaml")
+ r := bytes.NewReader(yamlExample)
+
+ unmarshalReader(r, v.config)
+ assert.True(t, InConfig("name"))
+ assert.False(t, InConfig("state"))
+ assert.Equal(t, "steve", Get("name"))
+ assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
+ assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
+ assert.Equal(t, 35, Get("age"))
+}
+
+func TestUnmarshalExact(t *testing.T) {
+ vip := New()
+ target := &testUnmarshalExtra{}
+ vip.SetConfigType("yaml")
+ r := bytes.NewReader(yamlExampleWithExtras)
+ vip.ReadConfig(r)
+ err := vip.UnmarshalExact(target)
+ if err == nil {
+ t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
+ }
+}
+
+func TestOverrides(t *testing.T) {
+ Set("age", 40)
+ assert.Equal(t, 40, Get("age"))
+}
+
+func TestDefaultPost(t *testing.T) {
+ assert.NotEqual(t, "NYC", Get("state"))
+ SetDefault("state", "NYC")
+ assert.Equal(t, "NYC", Get("state"))
+}
+
+func TestAliases(t *testing.T) {
+ RegisterAlias("years", "age")
+ assert.Equal(t, 40, Get("years"))
+ Set("years", 45)
+ assert.Equal(t, 45, Get("age"))
+}
+
+func TestAliasInConfigFile(t *testing.T) {
+ // the config file specifies "beard". If we make this an alias for
+ // "hasbeard", we still want the old config file to work with beard.
+ RegisterAlias("beard", "hasbeard")
+ assert.Equal(t, true, Get("hasbeard"))
+ Set("hasbeard", false)
+ assert.Equal(t, false, Get("beard"))
+}
+
+func TestYML(t *testing.T) {
+ initYAML()
+ assert.Equal(t, "steve", Get("name"))
+}
+
+func TestJSON(t *testing.T) {
+ initJSON()
+ assert.Equal(t, "0001", Get("id"))
+}
+
+func TestProperties(t *testing.T) {
+ initProperties()
+ assert.Equal(t, "0001", Get("p_id"))
+}
+
+func TestTOML(t *testing.T) {
+ initTOML()
+ assert.Equal(t, "TOML Example", Get("title"))
+}
+
+func TestHCL(t *testing.T) {
+ initHcl()
+ assert.Equal(t, "0001", Get("id"))
+ assert.Equal(t, 0.55, Get("ppu"))
+ assert.Equal(t, "donut", Get("type"))
+ assert.Equal(t, "Cake", Get("name"))
+ Set("id", "0002")
+ assert.Equal(t, "0002", Get("id"))
+ assert.NotEqual(t, "cronut", Get("type"))
+}
+
+func TestRemotePrecedence(t *testing.T) {
+ initJSON()
+
+ remote := bytes.NewReader(remoteExample)
+ assert.Equal(t, "0001", Get("id"))
+ unmarshalReader(remote, v.kvstore)
+ assert.Equal(t, "0001", Get("id"))
+ assert.NotEqual(t, "cronut", Get("type"))
+ assert.Equal(t, "remote", Get("newkey"))
+ Set("newkey", "newvalue")
+ assert.NotEqual(t, "remote", Get("newkey"))
+ assert.Equal(t, "newvalue", Get("newkey"))
+ Set("newkey", "remote")
+}
+
+func TestEnv(t *testing.T) {
+ initJSON()
+
+ BindEnv("id")
+ BindEnv("f", "FOOD")
+
+ os.Setenv("ID", "13")
+ os.Setenv("FOOD", "apple")
+ os.Setenv("NAME", "crunk")
+
+ assert.Equal(t, "13", Get("id"))
+ assert.Equal(t, "apple", Get("f"))
+ assert.Equal(t, "Cake", Get("name"))
+
+ AutomaticEnv()
+
+ assert.Equal(t, "crunk", Get("name"))
+
+}
+
+func TestEnvPrefix(t *testing.T) {
+ initJSON()
+
+ SetEnvPrefix("foo") // will be uppercased automatically
+ BindEnv("id")
+ BindEnv("f", "FOOD") // not using prefix
+
+ os.Setenv("FOO_ID", "13")
+ os.Setenv("FOOD", "apple")
+ os.Setenv("FOO_NAME", "crunk")
+
+ assert.Equal(t, "13", Get("id"))
+ assert.Equal(t, "apple", Get("f"))
+ assert.Equal(t, "Cake", Get("name"))
+
+ AutomaticEnv()
+
+ assert.Equal(t, "crunk", Get("name"))
+}
+
+func TestAutoEnv(t *testing.T) {
+ Reset()
+
+ AutomaticEnv()
+ os.Setenv("FOO_BAR", "13")
+ assert.Equal(t, "13", Get("foo_bar"))
+}
+
+func TestAutoEnvWithPrefix(t *testing.T) {
+ Reset()
+
+ AutomaticEnv()
+ SetEnvPrefix("Baz")
+ os.Setenv("BAZ_BAR", "13")
+ assert.Equal(t, "13", Get("bar"))
+}
+
+func TestSetEnvReplacer(t *testing.T) {
+ Reset()
+
+ AutomaticEnv()
+ os.Setenv("REFRESH_INTERVAL", "30s")
+
+ replacer := strings.NewReplacer("-", "_")
+ SetEnvKeyReplacer(replacer)
+
+ assert.Equal(t, "30s", Get("refresh-interval"))
+}
+
+func TestAllKeys(t *testing.T) {
+ initConfigs()
+
+ ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"}
+ dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
+ all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}}
+
+ var allkeys sort.StringSlice
+ allkeys = AllKeys()
+ allkeys.Sort()
+ ks.Sort()
+
+ assert.Equal(t, ks, allkeys)
+ assert.Equal(t, all, AllSettings())
+}
+
+func TestAllKeysWithEnv(t *testing.T) {
+ v := New()
+
+ // bind and define environment variables (including a nested one)
+ v.BindEnv("id")
+ v.BindEnv("foo.bar")
+ v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
+ os.Setenv("ID", "13")
+ os.Setenv("FOO_BAR", "baz")
+
+ expectedKeys := sort.StringSlice{"id", "foo.bar"}
+ expectedKeys.Sort()
+ keys := sort.StringSlice(v.AllKeys())
+ keys.Sort()
+ assert.Equal(t, expectedKeys, keys)
+}
+
+func TestAliasesOfAliases(t *testing.T) {
+ Set("Title", "Checking Case")
+ RegisterAlias("Foo", "Bar")
+ RegisterAlias("Bar", "Title")
+ assert.Equal(t, "Checking Case", Get("FOO"))
+}
+
+func TestRecursiveAliases(t *testing.T) {
+ RegisterAlias("Baz", "Roo")
+ RegisterAlias("Roo", "baz")
+}
+
+func TestUnmarshal(t *testing.T) {
+ SetDefault("port", 1313)
+ Set("name", "Steve")
+ Set("duration", "1s1ms")
+
+ type config struct {
+ Port int
+ Name string
+ Duration time.Duration
+ }
+
+ var C config
+
+ err := Unmarshal(&C)
+ if err != nil {
+ t.Fatalf("unable to decode into struct, %v", err)
+ }
+
+ assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C)
+
+ Set("port", 1234)
+ err = Unmarshal(&C)
+ if err != nil {
+ t.Fatalf("unable to decode into struct, %v", err)
+ }
+ assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C)
+}
+
+func TestBindPFlags(t *testing.T) {
+ v := New() // create independent Viper object
+ flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
+
+ var testValues = map[string]*string{
+ "host": nil,
+ "port": nil,
+ "endpoint": nil,
+ }
+
+ var mutatedTestValues = map[string]string{
+ "host": "localhost",
+ "port": "6060",
+ "endpoint": "/public",
+ }
+
+ for name := range testValues {
+ testValues[name] = flagSet.String(name, "", "test")
+ }
+
+ err := v.BindPFlags(flagSet)
+ if err != nil {
+ t.Fatalf("error binding flag set, %v", err)
+ }
+
+ flagSet.VisitAll(func(flag *pflag.Flag) {
+ flag.Value.Set(mutatedTestValues[flag.Name])
+ flag.Changed = true
+ })
+
+ for name, expected := range mutatedTestValues {
+ assert.Equal(t, expected, v.Get(name))
+ }
+
+}
+
+func TestBindPFlag(t *testing.T) {
+ var testString = "testing"
+ var testValue = newStringValue(testString, &testString)
+
+ flag := &pflag.Flag{
+ Name: "testflag",
+ Value: testValue,
+ Changed: false,
+ }
+
+ BindPFlag("testvalue", flag)
+
+ assert.Equal(t, testString, Get("testvalue"))
+
+ flag.Value.Set("testing_mutate")
+ flag.Changed = true //hack for pflag usage
+
+ assert.Equal(t, "testing_mutate", Get("testvalue"))
+
+}
+
+func TestBoundCaseSensitivity(t *testing.T) {
+ assert.Equal(t, "brown", Get("eyes"))
+
+ BindEnv("eYEs", "TURTLE_EYES")
+ os.Setenv("TURTLE_EYES", "blue")
+
+ assert.Equal(t, "blue", Get("eyes"))
+
+ var testString = "green"
+ var testValue = newStringValue(testString, &testString)
+
+ flag := &pflag.Flag{
+ Name: "eyeballs",
+ Value: testValue,
+ Changed: true,
+ }
+
+ BindPFlag("eYEs", flag)
+ assert.Equal(t, "green", Get("eyes"))
+
+}
+
+func TestSizeInBytes(t *testing.T) {
+ input := map[string]uint{
+ "": 0,
+ "b": 0,
+ "12 bytes": 0,
+ "200000000000gb": 0,
+ "12 b": 12,
+ "43 MB": 43 * (1 << 20),
+ "10mb": 10 * (1 << 20),
+ "1gb": 1 << 30,
+ }
+
+ for str, expected := range input {
+ assert.Equal(t, expected, parseSizeInBytes(str), str)
+ }
+}
+
+func TestFindsNestedKeys(t *testing.T) {
+ initConfigs()
+ dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
+
+ Set("super", map[string]interface{}{
+ "deep": map[string]interface{}{
+ "nested": "value",
+ },
+ })
+
+ expected := map[string]interface{}{
+ "super": map[string]interface{}{
+ "deep": map[string]interface{}{
+ "nested": "value",
+ },
+ },
+ "super.deep": map[string]interface{}{
+ "nested": "value",
+ },
+ "super.deep.nested": "value",
+ "owner.organization": "MongoDB",
+ "batters.batter": []interface{}{
+ map[string]interface{}{
+ "type": "Regular",
+ },
+ map[string]interface{}{
+ "type": "Chocolate",
+ },
+ map[string]interface{}{
+ "type": "Blueberry",
+ },
+ map[string]interface{}{
+ "type": "Devil's Food",
+ },
+ },
+ "hobbies": []interface{}{
+ "skateboarding", "snowboarding", "go",
+ },
+ "title": "TOML Example",
+ "newkey": "remote",
+ "batters": map[string]interface{}{
+ "batter": []interface{}{
+ map[string]interface{}{
+ "type": "Regular",
+ },
+ map[string]interface{}{
+ "type": "Chocolate",
+ }, map[string]interface{}{
+ "type": "Blueberry",
+ }, map[string]interface{}{
+ "type": "Devil's Food",
+ },
+ },
+ },
+ "eyes": "brown",
+ "age": 35,
+ "owner": map[string]interface{}{
+ "organization": "MongoDB",
+ "bio": "MongoDB Chief Developer Advocate & Hacker at Large",
+ "dob": dob,
+ },
+ "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
+ "type": "donut",
+ "id": "0001",
+ "name": "Cake",
+ "hacker": true,
+ "ppu": 0.55,
+ "clothing": map[string]interface{}{
+ "jacket": "leather",
+ "trousers": "denim",
+ "pants": map[string]interface{}{
+ "size": "large",
+ },
+ },
+ "clothing.jacket": "leather",
+ "clothing.pants.size": "large",
+ "clothing.trousers": "denim",
+ "owner.dob": dob,
+ "beard": true,
+ "foos": []map[string]interface{}{
+ map[string]interface{}{
+ "foo": []map[string]interface{}{
+ map[string]interface{}{
+ "key": 1,
+ },
+ map[string]interface{}{
+ "key": 2,
+ },
+ map[string]interface{}{
+ "key": 3,
+ },
+ map[string]interface{}{
+ "key": 4,
+ },
+ },
+ },
+ },
+ }
+
+ for key, expectedValue := range expected {
+
+ assert.Equal(t, expectedValue, v.Get(key))
+ }
+
+}
+
+func TestReadBufConfig(t *testing.T) {
+ v := New()
+ v.SetConfigType("yaml")
+ v.ReadConfig(bytes.NewBuffer(yamlExample))
+ t.Log(v.AllKeys())
+
+ assert.True(t, v.InConfig("name"))
+ assert.False(t, v.InConfig("state"))
+ assert.Equal(t, "steve", v.Get("name"))
+ assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
+ assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
+ assert.Equal(t, 35, v.Get("age"))
+}
+
+func TestIsSet(t *testing.T) {
+ v := New()
+ v.SetConfigType("yaml")
+ v.ReadConfig(bytes.NewBuffer(yamlExample))
+ assert.True(t, v.IsSet("clothing.jacket"))
+ assert.False(t, v.IsSet("clothing.jackets"))
+ assert.False(t, v.IsSet("helloworld"))
+ v.Set("helloworld", "fubar")
+ assert.True(t, v.IsSet("helloworld"))
+}
+
+func TestDirsSearch(t *testing.T) {
+
+ root, config, cleanup := initDirs(t)
+ defer cleanup()
+
+ v := New()
+ v.SetConfigName(config)
+ v.SetDefault(`key`, `default`)
+
+ entries, err := ioutil.ReadDir(root)
+ for _, e := range entries {
+ if e.IsDir() {
+ v.AddConfigPath(e.Name())
+ }
+ }
+
+ err = v.ReadInConfig()
+ assert.Nil(t, err)
+
+ assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`))
+}
+
+func TestWrongDirsSearchNotFound(t *testing.T) {
+
+ _, config, cleanup := initDirs(t)
+ defer cleanup()
+
+ v := New()
+ v.SetConfigName(config)
+ v.SetDefault(`key`, `default`)
+
+ v.AddConfigPath(`whattayoutalkingbout`)
+ v.AddConfigPath(`thispathaintthere`)
+
+ err := v.ReadInConfig()
+ assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
+
+ // Even though config did not load and the error might have
+ // been ignored by the client, the default still loads
+ assert.Equal(t, `default`, v.GetString(`key`))
+}
+
+func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
+
+ _, config, cleanup := initDirs(t)
+ defer cleanup()
+
+ v := New()
+ v.SetConfigName(config)
+ v.SetDefault(`key`, `default`)
+
+ v.AddConfigPath(`whattayoutalkingbout`)
+ v.AddConfigPath(`thispathaintthere`)
+
+ err := v.MergeInConfig()
+ assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
+
+ // Even though config did not load and the error might have
+ // been ignored by the client, the default still loads
+ assert.Equal(t, `default`, v.GetString(`key`))
+}
+
+func TestSub(t *testing.T) {
+ v := New()
+ v.SetConfigType("yaml")
+ v.ReadConfig(bytes.NewBuffer(yamlExample))
+
+ subv := v.Sub("clothing")
+ assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
+
+ subv = v.Sub("clothing.pants")
+ assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
+
+ subv = v.Sub("clothing.pants.size")
+ assert.Equal(t, (*Viper)(nil), subv)
+
+ subv = v.Sub("missing.key")
+ assert.Equal(t, (*Viper)(nil), subv)
+}
+
+var yamlMergeExampleTgt = []byte(`
+hello:
+ pop: 37890
+ lagrenum: 765432101234567
+ world:
+ - us
+ - uk
+ - fr
+ - de
+`)
+
+var yamlMergeExampleSrc = []byte(`
+hello:
+ pop: 45000
+ lagrenum: 7654321001234567
+ universe:
+ - mw
+ - ad
+fu: bar
+`)
+
+func TestMergeConfig(t *testing.T) {
+ v := New()
+ v.SetConfigType("yml")
+ if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
+ t.Fatal(err)
+ }
+
+ if pop := v.GetInt("hello.pop"); pop != 37890 {
+ t.Fatalf("pop != 37890, = %d", pop)
+ }
+
+ if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 {
+ t.Fatalf("lagrenum != 765432101234567, = %d", pop)
+ }
+
+ if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) {
+ t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop)
+ }
+
+ if world := v.GetStringSlice("hello.world"); len(world) != 4 {
+ t.Fatalf("len(world) != 4, = %d", len(world))
+ }
+
+ if fu := v.GetString("fu"); fu != "" {
+ t.Fatalf("fu != \"\", = %s", fu)
+ }
+
+ if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
+ t.Fatal(err)
+ }
+
+ if pop := v.GetInt("hello.pop"); pop != 45000 {
+ t.Fatalf("pop != 45000, = %d", pop)
+ }
+
+ if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 {
+ t.Fatalf("lagrenum != 7654321001234567, = %d", pop)
+ }
+
+ if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) {
+ t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop)
+ }
+
+ if world := v.GetStringSlice("hello.world"); len(world) != 4 {
+ t.Fatalf("len(world) != 4, = %d", len(world))
+ }
+
+ if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
+ t.Fatalf("len(universe) != 2, = %d", len(universe))
+ }
+
+ if fu := v.GetString("fu"); fu != "bar" {
+ t.Fatalf("fu != \"bar\", = %s", fu)
+ }
+}
+
+func TestMergeConfigNoMerge(t *testing.T) {
+ v := New()
+ v.SetConfigType("yml")
+ if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
+ t.Fatal(err)
+ }
+
+ if pop := v.GetInt("hello.pop"); pop != 37890 {
+ t.Fatalf("pop != 37890, = %d", pop)
+ }
+
+ if world := v.GetStringSlice("hello.world"); len(world) != 4 {
+ t.Fatalf("len(world) != 4, = %d", len(world))
+ }
+
+ if fu := v.GetString("fu"); fu != "" {
+ t.Fatalf("fu != \"\", = %s", fu)
+ }
+
+ if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
+ t.Fatal(err)
+ }
+
+ if pop := v.GetInt("hello.pop"); pop != 45000 {
+ t.Fatalf("pop != 45000, = %d", pop)
+ }
+
+ if world := v.GetStringSlice("hello.world"); len(world) != 0 {
+ t.Fatalf("len(world) != 0, = %d", len(world))
+ }
+
+ if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
+ t.Fatalf("len(universe) != 2, = %d", len(universe))
+ }
+
+ if fu := v.GetString("fu"); fu != "bar" {
+ t.Fatalf("fu != \"bar\", = %s", fu)
+ }
+}
+
+func TestUnmarshalingWithAliases(t *testing.T) {
+ v := New()
+ v.SetDefault("ID", 1)
+ v.Set("name", "Steve")
+ v.Set("lastname", "Owen")
+
+ v.RegisterAlias("UserID", "ID")
+ v.RegisterAlias("Firstname", "name")
+ v.RegisterAlias("Surname", "lastname")
+
+ type config struct {
+ ID int
+ FirstName string
+ Surname string
+ }
+
+ var C config
+ err := v.Unmarshal(&C)
+ if err != nil {
+ t.Fatalf("unable to decode into struct, %v", err)
+ }
+
+ assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
+}
+
+func TestSetConfigNameClearsFileCache(t *testing.T) {
+ SetConfigFile("/tmp/config.yaml")
+ SetConfigName("default")
+ f, err := v.getConfigFile()
+ if err == nil {
+ t.Fatalf("config file cache should have been cleared")
+ }
+ assert.Empty(t, f)
+}
+
+func TestShadowedNestedValue(t *testing.T) {
+
+ config := `name: steve
+clothing:
+ jacket: leather
+ trousers: denim
+ pants:
+ size: large
+`
+ initConfig("yaml", config)
+
+ assert.Equal(t, "steve", GetString("name"))
+
+ polyester := "polyester"
+ SetDefault("clothing.shirt", polyester)
+ SetDefault("clothing.jacket.price", 100)
+
+ assert.Equal(t, "leather", GetString("clothing.jacket"))
+ assert.Nil(t, Get("clothing.jacket.price"))
+ assert.Equal(t, polyester, GetString("clothing.shirt"))
+
+ clothingSettings := AllSettings()["clothing"].(map[string]interface{})
+ assert.Equal(t, "leather", clothingSettings["jacket"])
+ assert.Equal(t, polyester, clothingSettings["shirt"])
+}
+
+func TestDotParameter(t *testing.T) {
+ initJSON()
+ // shoud take precedence over batters defined in jsonExample
+ r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
+ unmarshalReader(r, v.config)
+
+ actual := Get("batters.batter")
+ expected := []interface{}{map[string]interface{}{"type": "Small"}}
+ assert.Equal(t, expected, actual)
+}
+
+func TestCaseInsensitive(t *testing.T) {
+ for _, config := range []struct {
+ typ string
+ content string
+ }{
+ {"yaml", `
+aBcD: 1
+eF:
+ gH: 2
+ iJk: 3
+ Lm:
+ nO: 4
+ P:
+ Q: 5
+ R: 6
+`},
+ {"json", `{
+ "aBcD": 1,
+ "eF": {
+ "iJk": 3,
+ "Lm": {
+ "P": {
+ "Q": 5,
+ "R": 6
+ },
+ "nO": 4
+ },
+ "gH": 2
+ }
+}`},
+ {"toml", `aBcD = 1
+[eF]
+gH = 2
+iJk = 3
+[eF.Lm]
+nO = 4
+[eF.Lm.P]
+Q = 5
+R = 6
+`},
+ } {
+ doTestCaseInsensitive(t, config.typ, config.content)
+ }
+}
+
+func TestCaseInsensitiveSet(t *testing.T) {
+ Reset()
+ m1 := map[string]interface{}{
+ "Foo": 32,
+ "Bar": map[interface{}]interface {
+ }{
+ "ABc": "A",
+ "cDE": "B"},
+ }
+
+ m2 := map[string]interface{}{
+ "Foo": 52,
+ "Bar": map[interface{}]interface {
+ }{
+ "bCd": "A",
+ "eFG": "B"},
+ }
+
+ Set("Given1", m1)
+ Set("Number1", 42)
+
+ SetDefault("Given2", m2)
+ SetDefault("Number2", 52)
+
+ // Verify SetDefault
+ if v := Get("number2"); v != 52 {
+ t.Fatalf("Expected 52 got %q", v)
+ }
+
+ if v := Get("given2.foo"); v != 52 {
+ t.Fatalf("Expected 52 got %q", v)
+ }
+
+ if v := Get("given2.bar.bcd"); v != "A" {
+ t.Fatalf("Expected A got %q", v)
+ }
+
+ if _, ok := m2["Foo"]; !ok {
+ t.Fatal("Input map changed")
+ }
+
+ // Verify Set
+ if v := Get("number1"); v != 42 {
+ t.Fatalf("Expected 42 got %q", v)
+ }
+
+ if v := Get("given1.foo"); v != 32 {
+ t.Fatalf("Expected 32 got %q", v)
+ }
+
+ if v := Get("given1.bar.abc"); v != "A" {
+ t.Fatalf("Expected A got %q", v)
+ }
+
+ if _, ok := m1["Foo"]; !ok {
+ t.Fatal("Input map changed")
+ }
+}
+
+func doTestCaseInsensitive(t *testing.T, typ, config string) {
+ initConfig(typ, config)
+ Set("RfD", true)
+ assert.Equal(t, true, Get("rfd"))
+ assert.Equal(t, true, Get("rFD"))
+ assert.Equal(t, 1, cast.ToInt(Get("abcd")))
+ assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
+ assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
+ assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
+ assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
+ assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
+
+}
+
+func BenchmarkGetBool(b *testing.B) {
+ key := "BenchmarkGetBool"
+ v = New()
+ v.Set(key, true)
+
+ for i := 0; i < b.N; i++ {
+ if !v.GetBool(key) {
+ b.Fatal("GetBool returned false")
+ }
+ }
+}
+
+func BenchmarkGet(b *testing.B) {
+ key := "BenchmarkGet"
+ v = New()
+ v.Set(key, true)
+
+ for i := 0; i < b.N; i++ {
+ if !v.Get(key).(bool) {
+ b.Fatal("Get returned false")
+ }
+ }
+}
+
+// This is the "perfect result" for the above.
+func BenchmarkGetBoolFromMap(b *testing.B) {
+ m := make(map[string]bool)
+ key := "BenchmarkGetBool"
+ m[key] = true
+
+ for i := 0; i < b.N; i++ {
+ if !m[key] {
+ b.Fatal("Map value was false")
+ }
+ }
+}