2014-04-05 01:21:59 +04:00
|
|
|
// 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.
|
|
|
|
|
2014-09-28 01:03:00 +04:00
|
|
|
// Viper is a application configuration system.
|
|
|
|
// It believes that applications can be configured a variety of ways
|
2014-10-24 23:38:01 +04:00
|
|
|
// via flags, ENVIRONMENT variables, configuration files retrieved
|
|
|
|
// from the file system, or a remote key/value store.
|
2014-09-28 01:03:00 +04:00
|
|
|
|
|
|
|
// Each item takes precedence over the item below it:
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// overrides
|
2014-09-28 01:03:00 +04:00
|
|
|
// flag
|
|
|
|
// env
|
|
|
|
// config
|
2014-10-24 23:38:01 +04:00
|
|
|
// key/value store
|
2014-09-28 01:03:00 +04:00
|
|
|
// default
|
|
|
|
|
2014-04-05 01:21:59 +04:00
|
|
|
package viper
|
|
|
|
|
|
|
|
import (
|
2015-09-08 10:25:36 +03:00
|
|
|
"bufio"
|
2014-04-05 01:21:59 +04:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2015-11-10 07:22:04 +03:00
|
|
|
"log"
|
2014-04-05 01:21:59 +04:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2014-10-24 23:38:01 +04:00
|
|
|
"reflect"
|
2014-04-05 09:19:39 +04:00
|
|
|
"strings"
|
2014-04-05 01:21:59 +04:00
|
|
|
"time"
|
|
|
|
|
2015-09-08 10:25:36 +03:00
|
|
|
"github.com/BurntSushi/toml"
|
2014-04-05 01:21:59 +04:00
|
|
|
"github.com/kr/pretty"
|
2014-06-27 01:58:55 +04:00
|
|
|
"github.com/mitchellh/mapstructure"
|
2015-07-30 23:44:12 +03:00
|
|
|
"github.com/spf13/cast"
|
2014-04-05 01:21:59 +04:00
|
|
|
jww "github.com/spf13/jwalterweatherman"
|
2014-06-27 20:29:37 +04:00
|
|
|
"github.com/spf13/pflag"
|
2015-11-10 07:22:04 +03:00
|
|
|
"gopkg.in/fsnotify.v1"
|
2014-04-05 01:21:59 +04:00
|
|
|
)
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
var v *Viper
|
2014-12-05 05:55:51 +03:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
v = New()
|
|
|
|
}
|
|
|
|
|
2015-05-30 22:28:33 +03:00
|
|
|
type remoteConfigFactory interface {
|
|
|
|
Get(rp RemoteProvider) (io.Reader, error)
|
|
|
|
Watch(rp RemoteProvider) (io.Reader, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoteConfig is optional, see the remote package
|
|
|
|
var RemoteConfig remoteConfigFactory
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Denotes encountering an unsupported
|
|
|
|
// configuration filetype.
|
2014-12-05 05:55:51 +03:00
|
|
|
type UnsupportedConfigError string
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the formatted configuration error.
|
2014-12-05 05:55:51 +03:00
|
|
|
func (str UnsupportedConfigError) Error() string {
|
|
|
|
return fmt.Sprintf("Unsupported Config Type %q", string(str))
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Denotes encountering an unsupported remote
|
2015-11-25 22:51:57 +03:00
|
|
|
// provider. Currently only etcd and Consul are
|
2015-04-02 00:08:42 +03:00
|
|
|
// supported.
|
2014-12-05 05:55:51 +03:00
|
|
|
type UnsupportedRemoteProviderError string
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the formatted remote provider error.
|
2014-12-05 05:55:51 +03:00
|
|
|
func (str UnsupportedRemoteProviderError) Error() string {
|
|
|
|
return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str))
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Denotes encountering an error while trying to
|
|
|
|
// pull the configuration from the remote provider.
|
2014-12-05 05:55:51 +03:00
|
|
|
type RemoteConfigError string
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the formatted remote provider error
|
2014-12-05 05:55:51 +03:00
|
|
|
func (rce RemoteConfigError) Error() string {
|
|
|
|
return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
|
|
|
|
}
|
|
|
|
|
2015-08-02 03:37:27 +03:00
|
|
|
// Denotes failing to find configuration file.
|
|
|
|
type ConfigFileNotFoundError struct {
|
|
|
|
name, locations string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the formatted configuration error.
|
|
|
|
func (fnfe ConfigFileNotFoundError) Error() string {
|
|
|
|
return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations)
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// 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",
|
2015-12-02 13:15:48 +03:00
|
|
|
// "endpoint": "https://localhost"
|
2015-04-02 00:08:42 +03:00
|
|
|
// }
|
|
|
|
// Config : {
|
|
|
|
// "user": "root"
|
2015-12-02 13:15:48 +03:00
|
|
|
// "secret": "defaultsecret"
|
2015-04-02 00:08:42 +03:00
|
|
|
// }
|
|
|
|
// Env : {
|
|
|
|
// "secret": "somesecretkey"
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// The resulting config will have the following values:
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// "secret": "somesecretkey",
|
|
|
|
// "user": "root",
|
|
|
|
// "endpoint": "https://localhost"
|
|
|
|
// }
|
2015-02-17 17:22:37 +03:00
|
|
|
type Viper struct {
|
2015-04-26 22:02:19 +03:00
|
|
|
// Delimiter that separates a list of keys
|
|
|
|
// used to access a nested value in one go
|
|
|
|
keyDelim string
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
// A set of paths to look for the config file in
|
|
|
|
configPaths []string
|
|
|
|
|
|
|
|
// A set of remote providers to search for the configuration
|
2015-05-30 22:28:33 +03:00
|
|
|
remoteProviders []*defaultRemoteProvider
|
2014-12-05 05:55:51 +03:00
|
|
|
|
|
|
|
// Name of file to look for inside the path
|
|
|
|
configName string
|
|
|
|
configFile string
|
|
|
|
configType string
|
2014-12-23 02:31:11 +03:00
|
|
|
envPrefix string
|
2014-12-05 05:55:51 +03:00
|
|
|
|
2014-12-23 06:47:25 +03:00
|
|
|
automaticEnvApplied bool
|
2015-03-06 22:21:17 +03:00
|
|
|
envKeyReplacer *strings.Replacer
|
2014-12-23 06:47:25 +03:00
|
|
|
|
[110] Default Values Specify Type
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
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
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
2015-08-29 18:54:20 +03:00
|
|
|
config map[string]interface{}
|
|
|
|
override map[string]interface{}
|
|
|
|
defaults map[string]interface{}
|
|
|
|
kvstore map[string]interface{}
|
2015-12-10 21:14:17 +03:00
|
|
|
pflags map[string]FlagValue
|
[110] Default Values Specify Type
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
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
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
2015-08-29 18:54:20 +03:00
|
|
|
env map[string]string
|
|
|
|
aliases map[string]string
|
|
|
|
typeByDefValue bool
|
2015-11-10 07:22:04 +03:00
|
|
|
|
|
|
|
onConfigChange func(fsnotify.Event)
|
2014-12-05 05:55:51 +03:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns an initialized Viper instance.
|
2015-02-17 17:22:37 +03:00
|
|
|
func New() *Viper {
|
|
|
|
v := new(Viper)
|
2015-04-26 22:02:19 +03:00
|
|
|
v.keyDelim = "."
|
2014-12-05 05:55:51 +03:00
|
|
|
v.configName = "config"
|
|
|
|
v.config = make(map[string]interface{})
|
|
|
|
v.override = make(map[string]interface{})
|
|
|
|
v.defaults = make(map[string]interface{})
|
|
|
|
v.kvstore = make(map[string]interface{})
|
2015-12-10 21:14:17 +03:00
|
|
|
v.pflags = make(map[string]FlagValue)
|
2014-12-05 05:55:51 +03:00
|
|
|
v.env = make(map[string]string)
|
|
|
|
v.aliases = make(map[string]string)
|
[110] Default Values Specify Type
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
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
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
2015-08-29 18:54:20 +03:00
|
|
|
v.typeByDefValue = false
|
2014-12-05 05:55:51 +03:00
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2015-02-19 18:39:44 +03:00
|
|
|
// 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()
|
2015-12-12 01:51:11 +03:00
|
|
|
SupportedExts = []string{"json", "toml", "yaml", "yml", "hcl"}
|
2015-02-19 18:39:44 +03:00
|
|
|
SupportedRemoteProviders = []string{"etcd", "consul"}
|
|
|
|
}
|
|
|
|
|
2015-05-30 22:28:33 +03:00
|
|
|
type defaultRemoteProvider struct {
|
2014-10-24 23:38:01 +04:00
|
|
|
provider string
|
|
|
|
endpoint string
|
|
|
|
path string
|
|
|
|
secretKeyring string
|
|
|
|
}
|
|
|
|
|
2015-05-30 22:28:33 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Universally supported extensions.
|
2015-12-12 01:51:11 +03:00
|
|
|
var SupportedExts []string = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"}
|
2014-04-05 01:21:59 +04:00
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Universally supported remote providers.
|
2014-12-05 05:55:51 +03:00
|
|
|
var SupportedRemoteProviders []string = []string{"etcd", "consul"}
|
2014-04-05 01:21:59 +04:00
|
|
|
|
2015-11-10 07:22:04 +03:00
|
|
|
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()
|
|
|
|
|
|
|
|
done := make(chan bool)
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case event := <-watcher.Events:
|
|
|
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
|
|
|
err := v.ReadInConfig()
|
|
|
|
if err != nil {
|
|
|
|
log.Println("error:", err)
|
|
|
|
}
|
|
|
|
v.onConfigChange(event)
|
|
|
|
}
|
|
|
|
case err := <-watcher.Errors:
|
|
|
|
log.Println("error:", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if v.configFile != "" {
|
|
|
|
watcher.Add(v.configFile)
|
|
|
|
} else {
|
|
|
|
for _, x := range v.configPaths {
|
|
|
|
err = watcher.Add(x)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
<-done
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2014-07-11 18:42:07 +04:00
|
|
|
// Explicitly define the path, name and extension of the config file
|
|
|
|
// Viper will use this and not check any of the config paths
|
2014-12-05 05:55:51 +03:00
|
|
|
func SetConfigFile(in string) { v.SetConfigFile(in) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) SetConfigFile(in string) {
|
2014-04-05 01:21:59 +04:00
|
|
|
if in != "" {
|
2014-12-05 05:55:51 +03:00
|
|
|
v.configFile = in
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-23 02:31:11 +03:00
|
|
|
// Define a prefix that ENVIRONMENT variables will use.
|
2015-04-02 00:08:42 +03:00
|
|
|
// E.g. if your prefix is "spf", the env registry
|
|
|
|
// will look for env. variables that start with "SPF_"
|
2014-12-23 02:31:11 +03:00
|
|
|
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) SetEnvPrefix(in string) {
|
2014-12-23 02:31:11 +03:00
|
|
|
if in != "" {
|
|
|
|
v.envPrefix = in
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) mergeWithEnvPrefix(in string) string {
|
2014-12-23 02:31:11 +03:00
|
|
|
if v.envPrefix != "" {
|
2014-12-23 06:47:25 +03:00
|
|
|
return strings.ToUpper(v.envPrefix + "_" + in)
|
2014-12-23 02:31:11 +03:00
|
|
|
}
|
|
|
|
|
2014-12-23 06:47:25 +03:00
|
|
|
return strings.ToUpper(in)
|
2014-12-23 02:31:11 +03:00
|
|
|
}
|
|
|
|
|
2015-03-06 22:21:17 +03:00
|
|
|
// 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 s 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
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) getEnv(key string) string {
|
2015-03-06 22:21:17 +03:00
|
|
|
if v.envKeyReplacer != nil {
|
|
|
|
key = v.envKeyReplacer.Replace(key)
|
|
|
|
}
|
|
|
|
return os.Getenv(key)
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Return the file used to populate the config registry
|
2014-12-05 05:55:51 +03:00
|
|
|
func ConfigFileUsed() string { return v.ConfigFileUsed() }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) ConfigFileUsed() string { return v.configFile }
|
2014-04-08 07:35:40 +04:00
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
// Add a path for Viper to search for the config file in.
|
2014-07-11 18:42:07 +04:00
|
|
|
// Can be called multiple times to define multiple search paths.
|
2014-12-05 05:55:51 +03:00
|
|
|
func AddConfigPath(in string) { v.AddConfigPath(in) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) AddConfigPath(in string) {
|
2014-04-05 01:21:59 +04:00
|
|
|
if in != "" {
|
|
|
|
absin := absPathify(in)
|
|
|
|
jww.INFO.Println("adding", absin, "to paths to search")
|
2014-12-05 05:55:51 +03:00
|
|
|
if !stringInSlice(absin, v.configPaths) {
|
|
|
|
v.configPaths = append(v.configPaths, absin)
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-24 23:38:01 +04:00
|
|
|
// 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 {
|
2014-12-05 05:55:51 +03:00
|
|
|
return v.AddRemoteProvider(provider, endpoint, path)
|
|
|
|
}
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
|
2014-10-24 23:38:01 +04:00
|
|
|
if !stringInSlice(provider, SupportedRemoteProviders) {
|
|
|
|
return UnsupportedRemoteProviderError(provider)
|
|
|
|
}
|
|
|
|
if provider != "" && endpoint != "" {
|
|
|
|
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
2015-05-30 22:28:33 +03:00
|
|
|
rp := &defaultRemoteProvider{
|
2014-10-24 23:38:01 +04:00
|
|
|
endpoint: endpoint,
|
|
|
|
provider: provider,
|
2014-10-27 19:21:03 +03:00
|
|
|
path: path,
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
if !v.providerPathExists(rp) {
|
|
|
|
v.remoteProviders = append(v.remoteProviders, rp)
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
2014-10-27 19:21:03 +03:00
|
|
|
func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
|
2014-12-05 05:55:51 +03:00
|
|
|
return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
|
2014-10-24 23:38:01 +04:00
|
|
|
if !stringInSlice(provider, SupportedRemoteProviders) {
|
|
|
|
return UnsupportedRemoteProviderError(provider)
|
|
|
|
}
|
|
|
|
if provider != "" && endpoint != "" {
|
|
|
|
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
2015-05-30 22:28:33 +03:00
|
|
|
rp := &defaultRemoteProvider{
|
2015-06-22 02:19:00 +03:00
|
|
|
endpoint: endpoint,
|
|
|
|
provider: provider,
|
|
|
|
path: path,
|
|
|
|
secretKeyring: secretkeyring,
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
if !v.providerPathExists(rp) {
|
|
|
|
v.remoteProviders = append(v.remoteProviders, rp)
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-05-30 22:28:33 +03:00
|
|
|
func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
|
2014-12-05 05:55:51 +03:00
|
|
|
for _, y := range v.remoteProviders {
|
2014-10-24 23:38:01 +04:00
|
|
|
if reflect.DeepEqual(y, p) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2015-04-26 22:02:19 +03:00
|
|
|
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
|
|
|
|
|
|
|
|
if len(path) == 0 {
|
|
|
|
return source
|
|
|
|
}
|
|
|
|
|
2015-10-27 01:52:14 +03:00
|
|
|
var ok bool
|
|
|
|
var next interface{}
|
|
|
|
for k, v := range source {
|
|
|
|
if strings.ToLower(k) == strings.ToLower(path[0]) {
|
|
|
|
ok = true
|
|
|
|
next = v
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ok {
|
2015-04-26 22:02:19 +03:00
|
|
|
switch next.(type) {
|
2015-05-27 18:15:02 +03:00
|
|
|
case map[interface{}]interface{}:
|
|
|
|
return v.searchMap(cast.ToStringMap(next), path[1:])
|
2015-04-26 22:02:19 +03:00
|
|
|
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:
|
|
|
|
return next
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[110] Default Values Specify Type
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
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
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
2015-08-29 18:54:20 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2014-12-05 19:04:40 +03:00
|
|
|
// Viper is essentially repository for configurations
|
|
|
|
// Get can retrieve any value given the key to use
|
|
|
|
// 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:
|
2015-04-02 00:08:42 +03:00
|
|
|
// override, flag, env, config file, key/value store, default
|
2014-12-05 19:04:40 +03:00
|
|
|
//
|
|
|
|
// Get returns an interface. For a specific value use one of the Get____ methods.
|
|
|
|
func Get(key string) interface{} { return v.Get(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) Get(key string) interface{} {
|
2015-04-26 22:02:19 +03:00
|
|
|
path := strings.Split(key, v.keyDelim)
|
|
|
|
|
[110] Default Values Specify Type
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
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
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
2015-08-29 18:54:20 +03:00
|
|
|
lcaseKey := strings.ToLower(key)
|
|
|
|
val := v.find(lcaseKey)
|
2014-12-05 19:04:40 +03:00
|
|
|
|
|
|
|
if val == nil {
|
2015-10-27 01:52:14 +03:00
|
|
|
source := v.find(strings.ToLower(path[0]))
|
2015-11-10 01:58:46 +03:00
|
|
|
if source != nil {
|
|
|
|
if reflect.TypeOf(source).Kind() == reflect.Map {
|
|
|
|
val = v.searchMap(cast.ToStringMap(source), path[1:])
|
|
|
|
}
|
2015-04-26 22:02:19 +03:00
|
|
|
}
|
2015-11-10 01:58:46 +03:00
|
|
|
}
|
2015-04-26 22:02:19 +03:00
|
|
|
|
2015-11-10 01:58:46 +03:00
|
|
|
// 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 val == nil {
|
|
|
|
if flag, exists := v.pflags[lcaseKey]; exists {
|
|
|
|
jww.TRACE.Println(key, "get pflag default", val)
|
2015-12-10 21:14:17 +03:00
|
|
|
switch flag.ValueType() {
|
2015-11-10 01:58:46 +03:00
|
|
|
case "int", "int8", "int16", "int32", "int64":
|
2015-12-10 21:14:17 +03:00
|
|
|
val = cast.ToInt(flag.ValueString())
|
2015-11-10 01:58:46 +03:00
|
|
|
case "bool":
|
2015-12-10 21:14:17 +03:00
|
|
|
val = cast.ToBool(flag.ValueString())
|
2015-11-10 01:58:46 +03:00
|
|
|
default:
|
2015-12-10 21:14:17 +03:00
|
|
|
val = flag.ValueString()
|
2015-11-10 01:58:46 +03:00
|
|
|
}
|
2015-04-26 22:02:19 +03:00
|
|
|
}
|
2014-12-05 19:04:40 +03:00
|
|
|
}
|
|
|
|
|
2015-11-10 01:58:46 +03:00
|
|
|
if val == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
[110] Default Values Specify Type
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
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
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
2015-08-29 18:54:20 +03:00
|
|
|
var valType interface{}
|
|
|
|
if !v.typeByDefValue {
|
|
|
|
valType = val
|
|
|
|
} else {
|
|
|
|
defVal, defExists := v.defaults[lcaseKey]
|
|
|
|
if defExists {
|
|
|
|
valType = defVal
|
|
|
|
} else {
|
|
|
|
valType = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch valType.(type) {
|
2014-12-05 19:04:40 +03:00
|
|
|
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)
|
2015-02-19 06:03:20 +03:00
|
|
|
case time.Duration:
|
|
|
|
return cast.ToDuration(val)
|
2014-12-05 19:04:40 +03:00
|
|
|
case []string:
|
[110] Default Values Specify Type
This patch adds a feature, if enabled, will infer a value's type from
its default value no matter from where else the value is set. This is
particularly important when working with environment variables. For
example:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
}
When this program is executed the following is emitted:
[0]akutz@pax:ex$ ./ex1
v1 []string [a b c]
v2 string a b c
[0]akutz@pax:ex$
You may wonder, why is this important? Just use the GetStringSlice
function. Well, it *becomes* important when dealing with marshaling.
If we update the above program to this:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d := &Data{}
viper.Marshal(d)
print("d.MyKey", d.MyKey)
}
Now we can see the issue when we execute the updated program:
[0]akutz@pax:ex$ ./ex2
v1 []string [a b c]
v2 string a b c
d.MyKey []string []
[0]akutz@pax:ex$
The marshalled data structure's field MyKey is empty when in fact it
should have a string slice equal to, in value, []string {"a", "b",
"c"}.
The problem is that viper's Marshal function calls AllSettings which
ultimately uses the Get function. The Get function does try to infer
the value's type, but it does so using the type of the value retrieved
using this logic:
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
While the above order is the one we want when retrieving the values,
this patch enables users to decide if it's the order they want to be
used when inferring a value's type. To that end the function
SetTypeByDefaultValue is introduced. When SetTypeByDefaultValue(true)
is called, a call to the Get function will now first check a key's
default value, if set, when inferring a value's type. This is
demonstrated using a modified version of the same program above:
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Data struct {
MyKey []string
}
func print(name string, val interface{}) {
fmt.Printf("%-15[1]s%-15[2]T%[2]v\n", name, val)
}
func main() {
viper.BindEnv("mykey", "MYPREFIX_MYKEY")
viper.SetDefault("mykey", []string{})
os.Setenv("MYPREFIX_MYKEY", "a b c")
v1 := viper.GetStringSlice("mykey")
v2 := viper.Get("mykey")
print("v1", v1)
print("v2", v2)
d1 := &Data{}
viper.Marshal(d1)
print("d1.MyKey", d1.MyKey)
viper.SetTypeByDefaultValue(true)
d2 := &Data{}
viper.Marshal(d2)
print("d2.MyKey", d2.MyKey)
}
Now the following is emitted:
[0]akutz@pax:ex$ ./ex3
v1 []string [a b c]
v2 string a b c
d1.MyKey []string []
d2.MyKey []string [a b c]
[0]akutz@pax:ex$
2015-08-29 18:54:20 +03:00
|
|
|
return cast.ToStringSlice(val)
|
2014-12-05 19:04:40 +03:00
|
|
|
}
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2015-12-24 14:44:44 +03:00
|
|
|
// Returns new Viper instance representing a sub tree of this instance
|
|
|
|
func Sub(key string) *Viper { return v.Sub(key) }
|
|
|
|
func (v *Viper) Sub(key string) *Viper {
|
2015-12-25 07:29:33 +03:00
|
|
|
subv := New()
|
|
|
|
data := v.Get(key)
|
|
|
|
if reflect.TypeOf(data).Kind() == reflect.Map {
|
|
|
|
subv.config = cast.ToStringMap(data)
|
|
|
|
return subv
|
|
|
|
} else {
|
2015-12-24 14:44:44 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as a string
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetString(key string) string { return v.GetString(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetString(key string) string {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToString(v.Get(key))
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key asa boolean
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetBool(key string) bool { return v.GetBool(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetBool(key string) bool {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToBool(v.Get(key))
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as an integer
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetInt(key string) int { return v.GetInt(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetInt(key string) int {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToInt(v.Get(key))
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as a float64
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetFloat64(key string) float64 { return v.GetFloat64(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetFloat64(key string) float64 {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToFloat64(v.Get(key))
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as time
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetTime(key string) time.Time { return v.GetTime(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetTime(key string) time.Time {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToTime(v.Get(key))
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as a duration
|
2015-02-19 06:03:20 +03:00
|
|
|
func GetDuration(key string) time.Duration { return v.GetDuration(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetDuration(key string) time.Duration {
|
2015-02-19 06:03:20 +03:00
|
|
|
return cast.ToDuration(v.Get(key))
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as a slice of strings
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetStringSlice(key string) []string { return v.GetStringSlice(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetStringSlice(key string) []string {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToStringSlice(v.Get(key))
|
2014-04-05 09:19:39 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as a map of interfaces
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetStringMap(key string) map[string]interface{} {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToStringMap(v.Get(key))
|
2014-04-05 09:19:39 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the value associated with the key as a map of strings
|
2014-12-05 05:55:51 +03:00
|
|
|
func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetStringMapString(key string) map[string]string {
|
2014-12-09 15:42:09 +03:00
|
|
|
return cast.ToStringMapString(v.Get(key))
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-08-03 19:42:26 +03:00
|
|
|
// Returns the value associated with the key as a map to a slice of strings.
|
2015-07-30 23:43:18 +03:00
|
|
|
func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
|
2015-07-30 23:46:38 +03:00
|
|
|
func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
|
2015-07-30 23:27:34 +03:00
|
|
|
return cast.ToStringMapStringSlice(v.Get(key))
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Returns the size of the value associated with the given key
|
|
|
|
// in bytes.
|
2015-03-01 00:03:22 +03:00
|
|
|
func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) GetSizeInBytes(key string) uint {
|
2015-03-01 00:03:22 +03:00
|
|
|
sizeStr := cast.ToString(v.Get(key))
|
|
|
|
return parseSizeInBytes(sizeStr)
|
|
|
|
}
|
|
|
|
|
2015-08-24 06:40:56 +03:00
|
|
|
// 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 {
|
2014-12-09 15:42:09 +03:00
|
|
|
return mapstructure.Decode(v.Get(key), rawVal)
|
2014-06-27 01:58:55 +04:00
|
|
|
}
|
|
|
|
|
2015-08-24 06:40:56 +03:00
|
|
|
// Unmarshals the config into a Struct. Make sure that the tags
|
2015-04-02 00:08:42 +03:00
|
|
|
// on the fields of the structure are properly set.
|
2015-08-24 06:40:56 +03:00
|
|
|
func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) }
|
|
|
|
func (v *Viper) Unmarshal(rawVal interface{}) error {
|
2015-02-17 07:42:08 +03:00
|
|
|
err := mapstructure.WeakDecode(v.AllSettings(), rawVal)
|
|
|
|
|
2014-10-24 23:38:01 +04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-05 15:35:21 +04:00
|
|
|
|
2015-01-22 10:43:42 +03:00
|
|
|
v.insensitiviseMaps()
|
2014-08-05 15:35:21 +04:00
|
|
|
|
|
|
|
return nil
|
2014-06-27 01:58:55 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 04:38:54 +03:00
|
|
|
// Bind a full flag set to the configuration, using each flag's long
|
|
|
|
// name as the config key.
|
|
|
|
func BindPFlags(flags *pflag.FlagSet) (err error) { return v.BindPFlags(flags) }
|
|
|
|
func (v *Viper) BindPFlags(flags *pflag.FlagSet) (err error) {
|
2015-12-10 21:14:17 +03:00
|
|
|
return v.BindFlagValues(pflagValueSet{flags})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bind 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) (err error) { return v.BindPFlag(key, flag) }
|
|
|
|
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) (err error) {
|
|
|
|
return v.BindFlagValue(key, pflagValue{flag})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bind a full FlagValue set to the configuration, using each flag's long
|
|
|
|
// name as the config key.
|
|
|
|
func BindFlagValues(flags FlagValueSet) (err 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 {
|
2015-04-02 04:38:54 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
2015-11-10 01:58:46 +03:00
|
|
|
return nil
|
2015-04-02 04:38:54 +03:00
|
|
|
}
|
|
|
|
|
2015-12-10 21:14:17 +03:00
|
|
|
// Bind a specific key to a FlagValue.
|
2015-04-02 00:08:42 +03:00
|
|
|
// Example(where serverCmd is a Cobra instance):
|
2014-07-11 18:42:07 +04:00
|
|
|
//
|
|
|
|
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
|
2015-12-10 21:14:17 +03:00
|
|
|
// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
|
2014-07-11 18:42:07 +04:00
|
|
|
//
|
2015-12-10 21:14:17 +03:00
|
|
|
func BindFlagValue(key string, flag FlagValue) (err error) { return v.BindFlagValue(key, flag) }
|
|
|
|
func (v *Viper) BindFlagValue(key string, flag FlagValue) (err error) {
|
2014-06-27 20:29:37 +04:00
|
|
|
if flag == nil {
|
|
|
|
return fmt.Errorf("flag for %q is nil", key)
|
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
v.pflags[strings.ToLower(key)] = flag
|
2014-06-27 20:29:37 +04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
// Binds a Viper key to a ENV variable
|
2014-09-28 01:03:00 +04:00
|
|
|
// ENV variables are case sensitive
|
|
|
|
// If only a key is provided, it will use the env key matching the key, uppercased.
|
2014-12-23 02:31:11 +03:00
|
|
|
// EnvPrefix will be used when set when env name is not provided.
|
2014-12-05 05:55:51 +03:00
|
|
|
func BindEnv(input ...string) (err error) { return v.BindEnv(input...) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) BindEnv(input ...string) (err error) {
|
2014-09-28 01:03:00 +04:00
|
|
|
var key, envkey string
|
|
|
|
if len(input) == 0 {
|
|
|
|
return fmt.Errorf("BindEnv missing key to bind to")
|
|
|
|
}
|
|
|
|
|
2014-10-10 00:39:24 +04:00
|
|
|
key = strings.ToLower(input[0])
|
2014-09-28 01:03:00 +04:00
|
|
|
|
|
|
|
if len(input) == 1 {
|
2014-12-23 06:47:25 +03:00
|
|
|
envkey = v.mergeWithEnvPrefix(key)
|
2014-09-28 01:03:00 +04:00
|
|
|
} else {
|
|
|
|
envkey = input[1]
|
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
v.env[key] = envkey
|
2014-09-28 01:03:00 +04:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-12-05 19:04:40 +03:00
|
|
|
// 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
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) find(key string) interface{} {
|
2014-04-05 01:21:59 +04:00
|
|
|
var val interface{}
|
|
|
|
var exists bool
|
|
|
|
|
2014-04-05 09:19:39 +04:00
|
|
|
// if the requested key is an alias, then return the proper key
|
2014-12-05 05:55:51 +03:00
|
|
|
key = v.realKey(key)
|
2014-04-05 01:21:59 +04:00
|
|
|
|
2014-06-27 20:29:37 +04:00
|
|
|
// PFlag Override first
|
2014-12-05 05:55:51 +03:00
|
|
|
flag, exists := v.pflags[key]
|
2015-12-10 21:14:17 +03:00
|
|
|
if exists && flag.HasChanged() {
|
|
|
|
jww.TRACE.Println(key, "found in override (via pflag):", flag.ValueString())
|
|
|
|
switch flag.ValueType() {
|
2015-11-10 01:58:46 +03:00
|
|
|
case "int", "int8", "int16", "int32", "int64":
|
2015-12-10 21:14:17 +03:00
|
|
|
return cast.ToInt(flag.ValueString())
|
2015-11-10 01:58:46 +03:00
|
|
|
case "bool":
|
2015-12-10 21:14:17 +03:00
|
|
|
return cast.ToBool(flag.ValueString())
|
2015-11-10 01:58:46 +03:00
|
|
|
default:
|
2015-12-10 21:14:17 +03:00
|
|
|
return flag.ValueString()
|
2014-06-27 20:29:37 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
val, exists = v.override[key]
|
2014-04-05 01:21:59 +04:00
|
|
|
if exists {
|
|
|
|
jww.TRACE.Println(key, "found in override:", val)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2014-12-23 06:47:25 +03:00
|
|
|
if v.automaticEnvApplied {
|
|
|
|
// even if it hasn't been registered, if automaticEnv is used,
|
|
|
|
// check any Get request
|
2015-03-06 22:21:17 +03:00
|
|
|
if val = v.getEnv(v.mergeWithEnvPrefix(key)); val != "" {
|
2014-12-23 06:47:25 +03:00
|
|
|
jww.TRACE.Println(key, "found in environment with val:", val)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
envkey, exists := v.env[key]
|
2014-09-28 01:03:00 +04:00
|
|
|
if exists {
|
|
|
|
jww.TRACE.Println(key, "registered as env var", envkey)
|
2015-03-06 22:21:17 +03:00
|
|
|
if val = v.getEnv(envkey); val != "" {
|
2014-11-07 21:14:27 +03:00
|
|
|
jww.TRACE.Println(envkey, "found in environment with val:", val)
|
2014-09-28 01:03:00 +04:00
|
|
|
return val
|
|
|
|
} else {
|
|
|
|
jww.TRACE.Println(envkey, "env value unset:")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
val, exists = v.config[key]
|
2014-04-05 01:21:59 +04:00
|
|
|
if exists {
|
|
|
|
jww.TRACE.Println(key, "found in config:", val)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2015-10-14 01:31:32 +03:00
|
|
|
// Test for nested config parameter
|
|
|
|
if strings.Contains(key, v.keyDelim) {
|
|
|
|
path := strings.Split(key, v.keyDelim)
|
|
|
|
|
|
|
|
source := v.find(path[0])
|
|
|
|
if source != nil {
|
|
|
|
if reflect.TypeOf(source).Kind() == reflect.Map {
|
|
|
|
val := v.searchMap(cast.ToStringMap(source), path[1:])
|
|
|
|
jww.TRACE.Println(key, "found in nested config:", val)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
val, exists = v.kvstore[key]
|
2014-10-24 23:38:01 +04:00
|
|
|
if exists {
|
|
|
|
jww.TRACE.Println(key, "found in key/value store:", val)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
val, exists = v.defaults[key]
|
2014-04-05 01:21:59 +04:00
|
|
|
if exists {
|
|
|
|
jww.TRACE.Println(key, "found in defaults:", val)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-12-05 19:04:40 +03:00
|
|
|
// Check to see if the key has been set in any of the data locations
|
2014-12-05 05:55:51 +03:00
|
|
|
func IsSet(key string) bool { return v.IsSet(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) IsSet(key string) bool {
|
2015-11-30 02:16:21 +03:00
|
|
|
path := strings.Split(key, v.keyDelim)
|
|
|
|
|
|
|
|
lcaseKey := strings.ToLower(key)
|
|
|
|
val := v.find(lcaseKey)
|
|
|
|
|
|
|
|
if val == nil {
|
|
|
|
source := v.find(strings.ToLower(path[0]))
|
|
|
|
if source != nil {
|
|
|
|
if reflect.TypeOf(source).Kind() == reflect.Map {
|
|
|
|
val = v.searchMap(cast.ToStringMap(source), path[1:])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return val != nil
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
// Have Viper check ENV variables for all
|
2014-09-28 01:01:11 +04:00
|
|
|
// keys set in config, default & flags
|
2014-12-05 05:55:51 +03:00
|
|
|
func AutomaticEnv() { v.AutomaticEnv() }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) AutomaticEnv() {
|
2014-12-23 06:47:25 +03:00
|
|
|
v.automaticEnvApplied = true
|
2014-09-28 01:01:11 +04:00
|
|
|
}
|
|
|
|
|
2015-03-06 22:21:17 +03:00
|
|
|
// SetEnvKeyReplacer sets the strings.Replacer on the viper object
|
2015-04-02 00:08:42 +03:00
|
|
|
// Useful for mapping an environmental variable to a key that does
|
|
|
|
// not match it.
|
2015-03-06 22:21:17 +03:00
|
|
|
func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
|
2015-03-06 22:21:17 +03:00
|
|
|
v.envKeyReplacer = r
|
|
|
|
}
|
|
|
|
|
2014-07-11 18:42:07 +04:00
|
|
|
// Aliases provide another accessor for the same key.
|
|
|
|
// This enables one to change a name without breaking the application
|
2014-12-05 05:55:51 +03:00
|
|
|
func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) RegisterAlias(alias string, key string) {
|
2014-12-05 05:55:51 +03:00
|
|
|
v.registerAlias(alias, strings.ToLower(key))
|
2014-04-05 09:19:39 +04:00
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) registerAlias(alias string, key string) {
|
2014-04-05 09:19:39 +04:00
|
|
|
alias = strings.ToLower(alias)
|
2014-12-05 05:55:51 +03:00
|
|
|
if alias != key && alias != v.realKey(key) {
|
|
|
|
_, exists := v.aliases[alias]
|
2014-08-05 15:35:21 +04:00
|
|
|
|
2014-04-05 09:19:39 +04:00
|
|
|
if !exists {
|
2014-08-05 15:35:21 +04:00
|
|
|
// 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.
|
2014-12-05 05:55:51 +03:00
|
|
|
if val, ok := v.config[alias]; ok {
|
|
|
|
delete(v.config, alias)
|
|
|
|
v.config[key] = val
|
2014-08-05 15:35:21 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
if val, ok := v.kvstore[alias]; ok {
|
|
|
|
delete(v.kvstore, alias)
|
|
|
|
v.kvstore[key] = val
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
if val, ok := v.defaults[alias]; ok {
|
|
|
|
delete(v.defaults, alias)
|
|
|
|
v.defaults[key] = val
|
2014-08-05 15:35:21 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
if val, ok := v.override[alias]; ok {
|
|
|
|
delete(v.override, alias)
|
|
|
|
v.override[key] = val
|
2014-08-05 15:35:21 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
v.aliases[alias] = key
|
2014-04-05 09:19:39 +04:00
|
|
|
}
|
|
|
|
} else {
|
2014-12-05 05:55:51 +03:00
|
|
|
jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key))
|
2014-04-05 09:19:39 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) realKey(key string) string {
|
2014-12-05 05:55:51 +03:00
|
|
|
newkey, exists := v.aliases[key]
|
2014-04-05 09:19:39 +04:00
|
|
|
if exists {
|
|
|
|
jww.DEBUG.Println("Alias", key, "to", newkey)
|
2014-12-05 05:55:51 +03:00
|
|
|
return v.realKey(newkey)
|
2014-04-05 09:19:39 +04:00
|
|
|
} else {
|
|
|
|
return key
|
|
|
|
}
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 19:04:40 +03:00
|
|
|
// Check to see if the given key (or an alias) is in the config file
|
2014-12-05 05:55:51 +03:00
|
|
|
func InConfig(key string) bool { return v.InConfig(key) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) InConfig(key string) bool {
|
2014-04-05 09:19:39 +04:00
|
|
|
// if the requested key is an alias, then return the proper key
|
2014-12-05 05:55:51 +03:00
|
|
|
key = v.realKey(key)
|
2014-04-05 09:19:39 +04:00
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
_, exists := v.config[key]
|
2014-04-05 01:21:59 +04:00
|
|
|
return exists
|
|
|
|
}
|
|
|
|
|
2015-09-08 10:25:36 +03:00
|
|
|
// Save configuration to file
|
|
|
|
func SaveConfig() error { return v.SaveConfig() }
|
|
|
|
func (v *Viper) SaveConfig() error {
|
|
|
|
|
|
|
|
jww.INFO.Println("Attempting to write config into the file")
|
|
|
|
if !stringInSlice(v.getConfigType(), SupportedExts) {
|
|
|
|
return UnsupportedConfigError(v.getConfigType())
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(v.getConfigFile())
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
w := bufio.NewWriter(f)
|
|
|
|
|
|
|
|
if err := toml.NewEncoder(w).Encode(v.AllSettings()); err != nil {
|
|
|
|
jww.FATAL.Println("Panic while writing into the file")
|
|
|
|
}
|
|
|
|
w.Flush()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-11 18:42:07 +04:00
|
|
|
// Set the default value for this key.
|
|
|
|
// Default only used when no value is provided by the user via flag, config or ENV.
|
2014-12-05 05:55:51 +03:00
|
|
|
func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) SetDefault(key string, value interface{}) {
|
2014-04-05 01:21:59 +04:00
|
|
|
// If alias passed in, then set the proper default
|
2014-12-05 05:55:51 +03:00
|
|
|
key = v.realKey(strings.ToLower(key))
|
|
|
|
v.defaults[key] = value
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Sets the value for the key in the override regiser.
|
2014-10-24 23:38:01 +04:00
|
|
|
// Will be used instead of values obtained via
|
2015-04-02 00:08:42 +03:00
|
|
|
// flags, config file, ENV, default, or key/value store
|
2014-12-05 05:55:51 +03:00
|
|
|
func Set(key string, value interface{}) { v.Set(key, value) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) Set(key string, value interface{}) {
|
2014-04-05 01:21:59 +04:00
|
|
|
// If alias passed in, then set the proper override
|
2014-12-05 05:55:51 +03:00
|
|
|
key = v.realKey(strings.ToLower(key))
|
|
|
|
v.override[key] = value
|
2014-04-09 02:57:45 +04:00
|
|
|
}
|
|
|
|
|
2014-07-11 18:42:07 +04:00
|
|
|
// Viper will discover and load the configuration file from disk
|
2014-10-24 23:38:01 +04:00
|
|
|
// and key/value stores, searching in one of the defined paths.
|
2014-12-05 05:55:51 +03:00
|
|
|
func ReadInConfig() error { return v.ReadInConfig() }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) ReadInConfig() error {
|
2014-04-05 01:21:59 +04:00
|
|
|
jww.INFO.Println("Attempting to read in config file")
|
2014-12-05 05:55:51 +03:00
|
|
|
if !stringInSlice(v.getConfigType(), SupportedExts) {
|
|
|
|
return UnsupportedConfigError(v.getConfigType())
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
file, err := ioutil.ReadFile(v.getConfigFile())
|
2014-04-09 02:57:45 +04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
2014-04-09 02:57:45 +04:00
|
|
|
|
2015-04-26 22:08:10 +03:00
|
|
|
v.config = make(map[string]interface{})
|
|
|
|
|
2015-08-24 06:40:56 +03:00
|
|
|
return v.unmarshalReader(bytes.NewReader(file), v.config)
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
|
2015-11-12 23:20:40 +03:00
|
|
|
// 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")
|
|
|
|
if !stringInSlice(v.getConfigType(), SupportedExts) {
|
|
|
|
return UnsupportedConfigError(v.getConfigType())
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := ioutil.ReadFile(v.getConfigFile())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return v.MergeConfig(bytes.NewReader(file))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Viper will read a configuration file, setting existing keys to nil if the
|
|
|
|
// key does not exist in the file.
|
2015-05-14 12:40:59 +03:00
|
|
|
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
|
|
|
|
func (v *Viper) ReadConfig(in io.Reader) error {
|
2015-05-08 12:13:33 +03:00
|
|
|
v.config = make(map[string]interface{})
|
2015-08-24 06:40:56 +03:00
|
|
|
return v.unmarshalReader(in, v.config)
|
2015-05-08 12:13:33 +03:00
|
|
|
}
|
|
|
|
|
2015-11-12 23:20:40 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-14 12:40:59 +03:00
|
|
|
// func ReadBufConfig(buf *bytes.Buffer) error { return v.ReadBufConfig(buf) }
|
|
|
|
// func (v *Viper) ReadBufConfig(buf *bytes.Buffer) error {
|
|
|
|
// v.config = make(map[string]interface{})
|
2015-08-24 06:40:56 +03:00
|
|
|
// return v.unmarshalReader(buf, v.config)
|
2015-05-14 12:40:59 +03:00
|
|
|
// }
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Attempts to get configuration from a remote source
|
|
|
|
// and read it in the remote configuration registry.
|
2014-12-05 05:55:51 +03:00
|
|
|
func ReadRemoteConfig() error { return v.ReadRemoteConfig() }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) ReadRemoteConfig() error {
|
2014-12-05 05:55:51 +03:00
|
|
|
err := v.getKeyValueConfig()
|
2014-10-27 17:14:45 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
|
2015-05-08 12:13:33 +03:00
|
|
|
func WatchRemoteConfig() error { return v.WatchRemoteConfig() }
|
|
|
|
func (v *Viper) WatchRemoteConfig() error {
|
|
|
|
err := v.watchKeyValueConfig()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-08-24 06:40:56 +03:00
|
|
|
// Unmarshall a Reader into a map
|
2014-12-05 19:04:40 +03:00
|
|
|
// Should probably be an unexported function
|
2015-08-24 06:40:56 +03:00
|
|
|
func unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
|
|
|
return v.unmarshalReader(in, c)
|
2015-08-02 04:32:35 +03:00
|
|
|
}
|
|
|
|
|
2015-08-24 06:40:56 +03:00
|
|
|
func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
|
|
|
return unmarshallConfigReader(in, c, v.getConfigType())
|
2014-08-05 15:35:21 +04:00
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) insensitiviseMaps() {
|
2015-01-22 10:43:42 +03:00
|
|
|
insensitiviseMap(v.config)
|
|
|
|
insensitiviseMap(v.defaults)
|
|
|
|
insensitiviseMap(v.override)
|
|
|
|
insensitiviseMap(v.kvstore)
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// retrieve the first found remote configuration
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) getKeyValueConfig() error {
|
2015-05-30 22:28:33 +03:00
|
|
|
if RemoteConfig == nil {
|
|
|
|
return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
|
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
for _, rp := range v.remoteProviders {
|
|
|
|
val, err := v.getRemoteConfig(rp)
|
2014-10-24 23:38:01 +04:00
|
|
|
if err != nil {
|
2014-10-26 16:42:03 +03:00
|
|
|
continue
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
2014-12-05 05:55:51 +03:00
|
|
|
v.kvstore = val
|
2014-10-26 16:42:03 +03:00
|
|
|
return nil
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
2014-10-27 18:03:11 +03:00
|
|
|
return RemoteConfigError("No Files Found")
|
|
|
|
}
|
|
|
|
|
2015-05-30 22:28:33 +03:00
|
|
|
func (v *Viper) getRemoteConfig(provider *defaultRemoteProvider) (map[string]interface{}, error) {
|
2014-10-26 16:42:03 +03:00
|
|
|
|
2015-05-30 22:28:33 +03:00
|
|
|
reader, err := RemoteConfig.Get(provider)
|
2014-10-26 16:42:03 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-08-24 06:40:56 +03:00
|
|
|
err = v.unmarshalReader(reader, v.kvstore)
|
2014-12-05 05:55:51 +03:00
|
|
|
return v.kvstore, err
|
2015-05-08 12:13:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
}
|
|
|
|
|
2015-05-30 22:28:33 +03:00
|
|
|
func (v *Viper) watchRemoteConfig(provider *defaultRemoteProvider) (map[string]interface{}, error) {
|
|
|
|
reader, err := RemoteConfig.Watch(provider)
|
2015-05-08 12:13:33 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-08-24 06:40:56 +03:00
|
|
|
err = v.unmarshalReader(reader, v.kvstore)
|
2015-05-08 12:13:33 +03:00
|
|
|
return v.kvstore, err
|
2014-10-26 16:42:03 +03:00
|
|
|
}
|
|
|
|
|
2014-12-05 19:04:40 +03:00
|
|
|
// Return all keys regardless where they are set
|
2014-12-05 05:55:51 +03:00
|
|
|
func AllKeys() []string { return v.AllKeys() }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) AllKeys() []string {
|
2014-09-28 01:00:51 +04:00
|
|
|
m := map[string]struct{}{}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
for key, _ := range v.defaults {
|
2015-10-29 20:57:16 +03:00
|
|
|
m[strings.ToLower(key)] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
for key, _ := range v.pflags {
|
|
|
|
m[strings.ToLower(key)] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
for key, _ := range v.env {
|
|
|
|
m[strings.ToLower(key)] = struct{}{}
|
2014-09-28 01:00:51 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
for key, _ := range v.config {
|
2015-10-29 20:57:16 +03:00
|
|
|
m[strings.ToLower(key)] = struct{}{}
|
2014-09-28 01:00:51 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
for key, _ := range v.kvstore {
|
2015-10-29 20:57:16 +03:00
|
|
|
m[strings.ToLower(key)] = struct{}{}
|
2014-10-24 23:38:01 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
for key, _ := range v.override {
|
2015-10-29 20:57:16 +03:00
|
|
|
m[strings.ToLower(key)] = struct{}{}
|
2014-09-28 01:00:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
a := []string{}
|
|
|
|
for x, _ := range m {
|
|
|
|
a = append(a, x)
|
|
|
|
}
|
|
|
|
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
2014-12-05 19:04:40 +03:00
|
|
|
// Return all settings as a map[string]interface{}
|
2014-12-05 05:55:51 +03:00
|
|
|
func AllSettings() map[string]interface{} { return v.AllSettings() }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) AllSettings() map[string]interface{} {
|
2014-09-28 01:00:51 +04:00
|
|
|
m := map[string]interface{}{}
|
2014-12-05 05:55:51 +03:00
|
|
|
for _, x := range v.AllKeys() {
|
|
|
|
m[x] = v.Get(x)
|
2014-09-28 01:00:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2014-07-11 18:42:07 +04:00
|
|
|
// Name for the config file.
|
|
|
|
// Does not include extension.
|
2014-12-05 05:55:51 +03:00
|
|
|
func SetConfigName(in string) { v.SetConfigName(in) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) SetConfigName(in string) {
|
2014-04-05 01:21:59 +04:00
|
|
|
if in != "" {
|
2014-12-05 05:55:51 +03:00
|
|
|
v.configName = in
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Sets the type of the configuration returned by the
|
|
|
|
// remote source, e.g. "json".
|
2014-12-05 05:55:51 +03:00
|
|
|
func SetConfigType(in string) { v.SetConfigType(in) }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) SetConfigType(in string) {
|
2014-04-05 01:21:59 +04:00
|
|
|
if in != "" {
|
2014-12-05 05:55:51 +03:00
|
|
|
v.configType = in
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) getConfigType() string {
|
2014-12-05 05:55:51 +03:00
|
|
|
if v.configType != "" {
|
|
|
|
return v.configType
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
cf := v.getConfigFile()
|
2014-11-13 22:43:51 +03:00
|
|
|
ext := filepath.Ext(cf)
|
2014-04-05 01:21:59 +04:00
|
|
|
|
|
|
|
if len(ext) > 1 {
|
|
|
|
return ext[1:]
|
|
|
|
} else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) getConfigFile() string {
|
2014-04-05 01:21:59 +04:00
|
|
|
// if explicitly set, then use it
|
2014-12-05 05:55:51 +03:00
|
|
|
if v.configFile != "" {
|
|
|
|
return v.configFile
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
cf, err := v.findConfigFile()
|
2014-04-05 01:21:59 +04:00
|
|
|
if err != nil {
|
2014-04-09 02:57:45 +04:00
|
|
|
return ""
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
2014-04-09 02:57:45 +04:00
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
v.configFile = cf
|
|
|
|
return v.getConfigFile()
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) searchInPath(in string) (filename string) {
|
2014-04-05 01:21:59 +04:00
|
|
|
jww.DEBUG.Println("Searching for config in ", in)
|
|
|
|
for _, ext := range SupportedExts {
|
2014-12-05 05:55:51 +03:00
|
|
|
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)
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2014-12-05 19:04:40 +03:00
|
|
|
// search all configPaths for any config file.
|
|
|
|
// Returns the first path that exists (and is a config file)
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) findConfigFile() (string, error) {
|
2015-08-02 03:37:27 +03:00
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
jww.INFO.Println("Searching for config in ", v.configPaths)
|
2014-04-05 01:21:59 +04:00
|
|
|
|
2014-12-05 05:55:51 +03:00
|
|
|
for _, cp := range v.configPaths {
|
|
|
|
file := v.searchInPath(cp)
|
2014-04-05 01:21:59 +04:00
|
|
|
if file != "" {
|
|
|
|
return file, nil
|
|
|
|
}
|
|
|
|
}
|
2015-08-02 03:37:27 +03:00
|
|
|
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
|
2014-06-26 01:23:00 +04:00
|
|
|
}
|
|
|
|
|
2015-04-02 00:08:42 +03:00
|
|
|
// Prints all configuration registries for debugging
|
|
|
|
// purposes.
|
2014-12-05 05:55:51 +03:00
|
|
|
func Debug() { v.Debug() }
|
2015-02-17 17:22:37 +03:00
|
|
|
func (v *Viper) Debug() {
|
2014-05-30 00:48:24 +04:00
|
|
|
fmt.Println("Aliases:")
|
2014-12-05 05:55:51 +03:00
|
|
|
pretty.Println(v.aliases)
|
2015-05-01 22:14:41 +03:00
|
|
|
fmt.Println("Override:")
|
|
|
|
pretty.Println(v.override)
|
2015-04-02 04:39:09 +03:00
|
|
|
fmt.Println("PFlags")
|
|
|
|
pretty.Println(v.pflags)
|
2015-05-01 22:14:41 +03:00
|
|
|
fmt.Println("Env:")
|
|
|
|
pretty.Println(v.env)
|
|
|
|
fmt.Println("Key/Value Store:")
|
|
|
|
pretty.Println(v.kvstore)
|
|
|
|
fmt.Println("Config:")
|
|
|
|
pretty.Println(v.config)
|
|
|
|
fmt.Println("Defaults:")
|
|
|
|
pretty.Println(v.defaults)
|
2014-04-05 01:21:59 +04:00
|
|
|
}
|