mirror of https://github.com/spf13/viper.git
Merge remote-tracking branch 'spf13/master'
This commit is contained in:
commit
d027b73aef
30
README.md
30
README.md
|
@ -1,13 +1,13 @@
|
||||||
viper [![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper)
|
![viper logo](https://cloud.githubusercontent.com/assets/173412/10886745/998df88a-8151-11e5-9448-4736db51020d.png)
|
||||||
=====
|
|
||||||
|
|
||||||
[![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)
|
Go configuration with fangs!
|
||||||
|
|
||||||
|
[![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)
|
||||||
|
|
||||||
Go configuration with fangs
|
|
||||||
|
|
||||||
## What is Viper?
|
## What is Viper?
|
||||||
|
|
||||||
Viper is a complete configuration solution for go applications. It is designed
|
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
|
to work within an application, and can handle all types of configuration needs
|
||||||
and formats. It supports:
|
and formats. It supports:
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ Viper is here to help with that.
|
||||||
|
|
||||||
Viper does the following for you:
|
Viper does the following for you:
|
||||||
|
|
||||||
1. Find, load, and marshal a configuration file in JSON, TOML, or YAML.
|
1. Find, load, and unmarshal a configuration file in JSON, TOML, or YAML.
|
||||||
2. Provide a mechanism to set default values for your different
|
2. Provide a mechanism to set default values for your different
|
||||||
configuration options.
|
configuration options.
|
||||||
3. Provide a mechanism to set override values for options specified through
|
3. Provide a mechanism to set override values for options specified through
|
||||||
|
@ -272,8 +272,8 @@ runtime_viper.SetConfigType("yaml") // because there is no file extension in a s
|
||||||
// read from remote config the first time.
|
// read from remote config the first time.
|
||||||
err := runtime_viper.ReadRemoteConfig()
|
err := runtime_viper.ReadRemoteConfig()
|
||||||
|
|
||||||
// marshal config
|
// unmarshal config
|
||||||
runtime_viper.Marshal(&runtime_conf)
|
runtime_viper.Unmarshal(&runtime_conf)
|
||||||
|
|
||||||
// open a goroutine to wath remote changes forever
|
// open a goroutine to wath remote changes forever
|
||||||
go func(){
|
go func(){
|
||||||
|
@ -287,9 +287,9 @@ go func(){
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshal new config into our runtime config struct. you can also use channel
|
// unmarshal new config into our runtime config struct. you can also use channel
|
||||||
// to implement a signal to notify the system of the changes
|
// to implement a signal to notify the system of the changes
|
||||||
runtime_viper.Marshal(&runtime_conf)
|
runtime_viper.Unmarshal(&runtime_conf)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
```
|
```
|
||||||
|
@ -389,15 +389,15 @@ will be returned instead. E.g.
|
||||||
GetString("datastore.metric.host") //returns "0.0.0.0"
|
GetString("datastore.metric.host") //returns "0.0.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Marshaling
|
### Unmarshaling
|
||||||
|
|
||||||
You also have the option of Marshaling all or a specific value to a struct, map,
|
You also have the option of Unmarshaling all or a specific value to a struct, map,
|
||||||
etc.
|
etc.
|
||||||
|
|
||||||
There are two methods to do this:
|
There are two methods to do this:
|
||||||
|
|
||||||
* `Marshal(rawVal interface{}) : error`
|
* `Unmarshal(rawVal interface{}) : error`
|
||||||
* `MarshalKey(key string, rawVal interface{}) : error`
|
* `UnmarshalKey(key string, rawVal interface{}) : error`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ type config struct {
|
||||||
|
|
||||||
var C config
|
var C config
|
||||||
|
|
||||||
err := Marshal(&C)
|
err := Unmarshal(&C)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to decode into struct, %v", err)
|
t.Fatalf("unable to decode into struct, %v", err)
|
||||||
}
|
}
|
||||||
|
|
2
util.go
2
util.go
|
@ -129,7 +129,7 @@ func findCWD() (string, error) {
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error {
|
func unmarshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
buf.ReadFrom(in)
|
buf.ReadFrom(in)
|
||||||
|
|
||||||
|
|
92
viper.go
92
viper.go
|
@ -143,13 +143,14 @@ type Viper struct {
|
||||||
automaticEnvApplied bool
|
automaticEnvApplied bool
|
||||||
envKeyReplacer *strings.Replacer
|
envKeyReplacer *strings.Replacer
|
||||||
|
|
||||||
config map[string]interface{}
|
config map[string]interface{}
|
||||||
override map[string]interface{}
|
override map[string]interface{}
|
||||||
defaults map[string]interface{}
|
defaults map[string]interface{}
|
||||||
kvstore map[string]interface{}
|
kvstore map[string]interface{}
|
||||||
pflags map[string]*pflag.Flag
|
pflags map[string]*pflag.Flag
|
||||||
env map[string]string
|
env map[string]string
|
||||||
aliases map[string]string
|
aliases map[string]string
|
||||||
|
typeByDefValue bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an initialized Viper instance.
|
// Returns an initialized Viper instance.
|
||||||
|
@ -164,6 +165,7 @@ func New() *Viper {
|
||||||
v.pflags = make(map[string]*pflag.Flag)
|
v.pflags = make(map[string]*pflag.Flag)
|
||||||
v.env = make(map[string]string)
|
v.env = make(map[string]string)
|
||||||
v.aliases = make(map[string]string)
|
v.aliases = make(map[string]string)
|
||||||
|
v.typeByDefValue = false
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -368,6 +370,25 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
// Viper is essentially repository for configurations
|
// Viper is essentially repository for configurations
|
||||||
// Get can retrieve any value given the key to use
|
// Get can retrieve any value given the key to use
|
||||||
// Get has the behavior of returning the value associated with the first
|
// Get has the behavior of returning the value associated with the first
|
||||||
|
@ -379,7 +400,8 @@ func Get(key string) interface{} { return v.Get(key) }
|
||||||
func (v *Viper) Get(key string) interface{} {
|
func (v *Viper) Get(key string) interface{} {
|
||||||
path := strings.Split(key, v.keyDelim)
|
path := strings.Split(key, v.keyDelim)
|
||||||
|
|
||||||
val := v.find(strings.ToLower(key))
|
lcaseKey := strings.ToLower(key)
|
||||||
|
val := v.find(lcaseKey)
|
||||||
|
|
||||||
if val == nil {
|
if val == nil {
|
||||||
source := v.find(path[0])
|
source := v.find(path[0])
|
||||||
|
@ -392,7 +414,19 @@ func (v *Viper) Get(key string) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch val.(type) {
|
var valType interface{}
|
||||||
|
if !v.typeByDefValue {
|
||||||
|
valType = val
|
||||||
|
} else {
|
||||||
|
defVal, defExists := v.defaults[lcaseKey]
|
||||||
|
if defExists {
|
||||||
|
valType = defVal
|
||||||
|
} else {
|
||||||
|
valType = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch valType.(type) {
|
||||||
case bool:
|
case bool:
|
||||||
return cast.ToBool(val)
|
return cast.ToBool(val)
|
||||||
case string:
|
case string:
|
||||||
|
@ -406,7 +440,7 @@ func (v *Viper) Get(key string) interface{} {
|
||||||
case time.Duration:
|
case time.Duration:
|
||||||
return cast.ToDuration(val)
|
return cast.ToDuration(val)
|
||||||
case []string:
|
case []string:
|
||||||
return val
|
return cast.ToStringSlice(val)
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
@ -465,6 +499,12 @@ func (v *Viper) GetStringMapString(key string) map[string]string {
|
||||||
return cast.ToStringMapString(v.Get(key))
|
return cast.ToStringMapString(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the size of the value associated with the given key
|
// Returns the size of the value associated with the given key
|
||||||
// in bytes.
|
// in bytes.
|
||||||
func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
|
func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) }
|
||||||
|
@ -473,16 +513,16 @@ func (v *Viper) GetSizeInBytes(key string) uint {
|
||||||
return parseSizeInBytes(sizeStr)
|
return parseSizeInBytes(sizeStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a single key and marshals it into a Struct
|
// Takes a single key and unmarshals it into a Struct
|
||||||
func MarshalKey(key string, rawVal interface{}) error { return v.MarshalKey(key, rawVal) }
|
func UnmarshalKey(key string, rawVal interface{}) error { return v.UnmarshalKey(key, rawVal) }
|
||||||
func (v *Viper) MarshalKey(key string, rawVal interface{}) error {
|
func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error {
|
||||||
return mapstructure.Decode(v.Get(key), rawVal)
|
return mapstructure.Decode(v.Get(key), rawVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshals the config into a Struct. Make sure that the tags
|
// Unmarshals the config into a Struct. Make sure that the tags
|
||||||
// on the fields of the structure are properly set.
|
// on the fields of the structure are properly set.
|
||||||
func Marshal(rawVal interface{}) error { return v.Marshal(rawVal) }
|
func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) }
|
||||||
func (v *Viper) Marshal(rawVal interface{}) error {
|
func (v *Viper) Unmarshal(rawVal interface{}) error {
|
||||||
err := mapstructure.WeakDecode(v.AllSettings(), rawVal)
|
err := mapstructure.WeakDecode(v.AllSettings(), rawVal)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -748,19 +788,19 @@ func (v *Viper) ReadInConfig() error {
|
||||||
|
|
||||||
v.config = make(map[string]interface{})
|
v.config = make(map[string]interface{})
|
||||||
|
|
||||||
return v.marshalReader(bytes.NewReader(file), v.config)
|
return v.unmarshalReader(bytes.NewReader(file), v.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
|
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
|
||||||
func (v *Viper) ReadConfig(in io.Reader) error {
|
func (v *Viper) ReadConfig(in io.Reader) error {
|
||||||
v.config = make(map[string]interface{})
|
v.config = make(map[string]interface{})
|
||||||
return v.marshalReader(in, v.config)
|
return v.unmarshalReader(in, v.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func ReadBufConfig(buf *bytes.Buffer) error { return v.ReadBufConfig(buf) }
|
// func ReadBufConfig(buf *bytes.Buffer) error { return v.ReadBufConfig(buf) }
|
||||||
// func (v *Viper) ReadBufConfig(buf *bytes.Buffer) error {
|
// func (v *Viper) ReadBufConfig(buf *bytes.Buffer) error {
|
||||||
// v.config = make(map[string]interface{})
|
// v.config = make(map[string]interface{})
|
||||||
// return v.marshalReader(buf, v.config)
|
// return v.unmarshalReader(buf, v.config)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Attempts to get configuration from a remote source
|
// Attempts to get configuration from a remote source
|
||||||
|
@ -783,14 +823,14 @@ func (v *Viper) WatchRemoteConfig() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshall a Reader into a map
|
// Unmarshall a Reader into a map
|
||||||
// Should probably be an unexported function
|
// Should probably be an unexported function
|
||||||
func marshalReader(in io.Reader, c map[string]interface{}) error {
|
func unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||||
return v.marshalReader(in, c)
|
return v.unmarshalReader(in, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) marshalReader(in io.Reader, c map[string]interface{}) error {
|
func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||||
return marshallConfigReader(in, c, v.getConfigType())
|
return unmarshallConfigReader(in, c, v.getConfigType())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) insensitiviseMaps() {
|
func (v *Viper) insensitiviseMaps() {
|
||||||
|
@ -823,7 +863,7 @@ func (v *Viper) getRemoteConfig(provider *defaultRemoteProvider) (map[string]int
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = v.marshalReader(reader, v.kvstore)
|
err = v.unmarshalReader(reader, v.kvstore)
|
||||||
return v.kvstore, err
|
return v.kvstore, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -845,7 +885,7 @@ func (v *Viper) watchRemoteConfig(provider *defaultRemoteProvider) (map[string]i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = v.marshalReader(reader, v.kvstore)
|
err = v.unmarshalReader(reader, v.kvstore)
|
||||||
return v.kvstore, err
|
return v.kvstore, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,23 +78,23 @@ func initConfigs() {
|
||||||
Reset()
|
Reset()
|
||||||
SetConfigType("yaml")
|
SetConfigType("yaml")
|
||||||
r := bytes.NewReader(yamlExample)
|
r := bytes.NewReader(yamlExample)
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
|
|
||||||
SetConfigType("json")
|
SetConfigType("json")
|
||||||
r = bytes.NewReader(jsonExample)
|
r = bytes.NewReader(jsonExample)
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
|
|
||||||
SetConfigType("properties")
|
SetConfigType("properties")
|
||||||
r = bytes.NewReader(propertiesExample)
|
r = bytes.NewReader(propertiesExample)
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
|
|
||||||
SetConfigType("toml")
|
SetConfigType("toml")
|
||||||
r = bytes.NewReader(tomlExample)
|
r = bytes.NewReader(tomlExample)
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
|
|
||||||
SetConfigType("json")
|
SetConfigType("json")
|
||||||
remote := bytes.NewReader(remoteExample)
|
remote := bytes.NewReader(remoteExample)
|
||||||
marshalReader(remote, v.kvstore)
|
unmarshalReader(remote, v.kvstore)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initYAML() {
|
func initYAML() {
|
||||||
|
@ -102,7 +102,7 @@ func initYAML() {
|
||||||
SetConfigType("yaml")
|
SetConfigType("yaml")
|
||||||
r := bytes.NewReader(yamlExample)
|
r := bytes.NewReader(yamlExample)
|
||||||
|
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initJSON() {
|
func initJSON() {
|
||||||
|
@ -110,7 +110,7 @@ func initJSON() {
|
||||||
SetConfigType("json")
|
SetConfigType("json")
|
||||||
r := bytes.NewReader(jsonExample)
|
r := bytes.NewReader(jsonExample)
|
||||||
|
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initProperties() {
|
func initProperties() {
|
||||||
|
@ -118,7 +118,7 @@ func initProperties() {
|
||||||
SetConfigType("properties")
|
SetConfigType("properties")
|
||||||
r := bytes.NewReader(propertiesExample)
|
r := bytes.NewReader(propertiesExample)
|
||||||
|
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTOML() {
|
func initTOML() {
|
||||||
|
@ -126,7 +126,7 @@ func initTOML() {
|
||||||
SetConfigType("toml")
|
SetConfigType("toml")
|
||||||
r := bytes.NewReader(tomlExample)
|
r := bytes.NewReader(tomlExample)
|
||||||
|
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make directories for testing
|
// make directories for testing
|
||||||
|
@ -201,11 +201,11 @@ func TestDefault(t *testing.T) {
|
||||||
assert.Equal(t, 45, Get("age"))
|
assert.Equal(t, 45, Get("age"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalling(t *testing.T) {
|
func TestUnmarshalling(t *testing.T) {
|
||||||
SetConfigType("yaml")
|
SetConfigType("yaml")
|
||||||
r := bytes.NewReader(yamlExample)
|
r := bytes.NewReader(yamlExample)
|
||||||
|
|
||||||
marshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
assert.True(t, InConfig("name"))
|
assert.True(t, InConfig("name"))
|
||||||
assert.False(t, InConfig("state"))
|
assert.False(t, InConfig("state"))
|
||||||
assert.Equal(t, "steve", Get("name"))
|
assert.Equal(t, "steve", Get("name"))
|
||||||
|
@ -266,7 +266,7 @@ func TestRemotePrecedence(t *testing.T) {
|
||||||
|
|
||||||
remote := bytes.NewReader(remoteExample)
|
remote := bytes.NewReader(remoteExample)
|
||||||
assert.Equal(t, "0001", Get("id"))
|
assert.Equal(t, "0001", Get("id"))
|
||||||
marshalReader(remote, v.kvstore)
|
unmarshalReader(remote, v.kvstore)
|
||||||
assert.Equal(t, "0001", Get("id"))
|
assert.Equal(t, "0001", Get("id"))
|
||||||
assert.NotEqual(t, "cronut", Get("type"))
|
assert.NotEqual(t, "cronut", Get("type"))
|
||||||
assert.Equal(t, "remote", Get("newkey"))
|
assert.Equal(t, "remote", Get("newkey"))
|
||||||
|
@ -378,7 +378,7 @@ func TestRecursiveAliases(t *testing.T) {
|
||||||
RegisterAlias("Roo", "baz")
|
RegisterAlias("Roo", "baz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshal(t *testing.T) {
|
func TestUnmarshal(t *testing.T) {
|
||||||
SetDefault("port", 1313)
|
SetDefault("port", 1313)
|
||||||
Set("name", "Steve")
|
Set("name", "Steve")
|
||||||
|
|
||||||
|
@ -389,7 +389,7 @@ func TestMarshal(t *testing.T) {
|
||||||
|
|
||||||
var C config
|
var C config
|
||||||
|
|
||||||
err := Marshal(&C)
|
err := Unmarshal(&C)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to decode into struct, %v", err)
|
t.Fatalf("unable to decode into struct, %v", err)
|
||||||
}
|
}
|
||||||
|
@ -397,7 +397,7 @@ func TestMarshal(t *testing.T) {
|
||||||
assert.Equal(t, &C, &config{Name: "Steve", Port: 1313})
|
assert.Equal(t, &C, &config{Name: "Steve", Port: 1313})
|
||||||
|
|
||||||
Set("port", 1234)
|
Set("port", 1234)
|
||||||
err = Marshal(&C)
|
err = Unmarshal(&C)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to decode into struct, %v", err)
|
t.Fatalf("unable to decode into struct, %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue