Merge branch 'master' into feature/unmarshal-with-meta

This commit is contained in:
Adam Sherwood 2017-08-30 11:35:51 -07:00
commit 9cf6bd7828
6 changed files with 73 additions and 56 deletions

View file

@ -17,6 +17,7 @@ matrix:
script:
- go install ./...
- diff -u <(echo -n) <(gofmt -d .)
- go test -v ./...
after_success:

View file

@ -6,18 +6,19 @@ Many Go projects are built using Viper including:
* [Hugo](http://gohugo.io)
* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)
* [Imgur's Incus](https://github.com/Imgur/incus)
* [Imgurs Incus](https://github.com/Imgur/incus)
* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
* [Docker Notary](https://github.com/docker/Notary)
* [BloomApi](https://www.bloomapi.com/)
* [doctl](https://github.com/digitalocean/doctl)
* [Clairctl](https://github.com/jgsqware/clairctl)
[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper)
## What is Viper?
Viper is a complete configuration solution for go applications including 12 factor apps. It is designed
Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed
to work within an application, and can handle all types of configuration needs
and formats. It supports:
@ -68,7 +69,7 @@ Viper configuration keys are case insensitive.
### Establishing Defaults
A good configuration system will support default values. A default value is not
required for a key, but it's useful in the event that a key hasnt been set via
required for a key, but its useful in the event that a key hasnt been set via
config file, environment variable, remote configuration or flag.
Examples:
@ -116,10 +117,10 @@ Optionally you can provide a function for Viper to run each time a change occurs
**Make sure you add all of the configPaths prior to calling `WatchConfig()`**
```go
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
```
### Reading Config from io.Reader
@ -236,7 +237,7 @@ Like `BindEnv`, the value is not set when the binding method is called, but when
it is accessed. This means you can bind as early as you want, even in an
`init()` function.
The `BindPFlag()` method provides this functionality.
For individual flags, the `BindPFlag()` method provides this functionality.
Example:
@ -245,6 +246,19 @@ serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
```
You can also bind an existing set of pflags (pflag.FlagSet):
Example:
```go
pflag.Int("flagname", 1234, "help message for flagname")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
i := viper.GetInt("flagname") // retrieve values from viper instead of pflag
```
The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude
the use of other packages that use the [flag](https://golang.org/pkg/flag/)
package from the standard library. The pflag package can handle the flags
@ -263,15 +277,23 @@ import (
)
func main() {
// using standard library "flag" package
flag.Int("flagname", 1234, "help message for flagname")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
...
viper.BindPFlags(pflag.CommandLine)
i := viper.GetInt("flagname") // retrieve value from viper
...
}
```
#### Flag interfaces
Viper provides two Go interfaces to bind other flag systems if you don't use `Pflags`.
Viper provides two Go interfaces to bind other flag systems if you dont use `Pflags`.
`FlagValue` represents a single flag. This is a very simple example on how to implement this interface:
@ -401,7 +423,7 @@ go func(){
## Getting Values From Viper
In Viper, there are a few ways to get a value depending on the value's type.
In Viper, there are a few ways to get a value depending on the values type.
The following functions and methods exist:
* `Get(key string) : interface{}`
@ -531,7 +553,7 @@ func NewCache(cfg *Viper) *Cache {...}
```
which creates a cache based on config information formatted as `subv`.
Now it's easy to create these 2 caches separately as:
Now its easy to create these 2 caches separately as:
```go
cfg1 := viper.Sub("app.cache1")
@ -575,13 +597,13 @@ initialization needed to begin using Viper. Since most applications will want
to use a single central repository for their configuration, the viper package
provides this. It is similar to a singleton.
In all of the examples above, they demonstrate using viper in it's singleton
In all of the examples above, they demonstrate using viper in its singleton
style approach.
### Working with multiple vipers
You can also create many different vipers for use in your application. Each will
have its own unique set of configurations and values. Each can read from a
have its own unique set of configurations and values. Each can read from a
different config file, key value store, etc. All of the functions that viper
package supports are mirrored as methods on a viper.

View file

@ -62,5 +62,4 @@ func TestBindFlagValue(t *testing.T) {
flag.Changed = true //hack for pflag usage
assert.Equal(t, "testing_mutate", Get("testvalue"))
}

View file

@ -33,13 +33,14 @@ func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error)
if err != nil {
return nil, err
}
resp,err := cm.Get(rp.Path())
resp, err := cm.Get(rp.Path())
if err != nil {
return nil, err
}
return bytes.NewReader(resp), nil
}
func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
cm, err := getConfigManager(rp)
if err != nil {
@ -47,13 +48,13 @@ func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *vi
}
quit := make(chan bool)
quitwc := make(chan bool)
viperResponsCh := make(chan *viper.RemoteResponse)
viperResponsCh := make(chan *viper.RemoteResponse)
cryptoResponseCh := cm.Watch(rp.Path(), quit)
// need this function to convert the Channel response form crypt.Response to viper.Response
go func(cr <-chan *crypt.Response,vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
for {
select {
case <- quitwc:
case <-quitwc:
quit <- true
return
case resp := <-cr:
@ -65,15 +66,12 @@ func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *vi
}
}
}(cryptoResponseCh,viperResponsCh,quitwc,quit)
return viperResponsCh,quitwc
}(cryptoResponseCh, viperResponsCh, quitwc, quit)
return viperResponsCh, quitwc
}
func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
var cm crypt.ConfigManager
var err error
@ -99,7 +97,6 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
return nil, err
}
return cm, nil
}
func init() {

View file

@ -16,7 +16,6 @@ import (
)
func TestCopyAndInsensitiviseMap(t *testing.T) {
var (
given = map[string]interface{}{
"Foo": 32,

View file

@ -69,8 +69,7 @@ func (str UnsupportedConfigError) Error() string {
}
// UnsupportedRemoteProviderError denotes encountering an unsupported remote
// provider. Currently only etcd and Consul are
// supported.
// provider. Currently only etcd and Consul are supported.
type UnsupportedRemoteProviderError string
// Error returns the formatted remote provider error.
@ -283,8 +282,8 @@ func (v *Viper) WatchConfig() {
}()
}
// SetConfigFile explicitly defines the path, name and extension of the config file
// Viper will use this and not check any of the config paths
// SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths.
func SetConfigFile(in string) { v.SetConfigFile(in) }
func (v *Viper) SetConfigFile(in string) {
if in != "" {
@ -293,8 +292,8 @@ func (v *Viper) SetConfigFile(in string) {
}
// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
// E.g. if your prefix is "spf", the env registry
// will look for env. variables that start with "SPF_"
// E.g. if your prefix is "spf", the env registry will look for env
// variables that start with "SPF_".
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
func (v *Viper) SetEnvPrefix(in string) {
if in != "" {
@ -312,11 +311,11 @@ func (v *Viper) mergeWithEnvPrefix(in string) string {
// 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)
// (camel case to snake case for JSON keys perhaps)
// getEnv is a wrapper around os.Getenv which replaces characters in the original
// key. This allows env vars which have different keys then the config object
// keys
// key. This allows env vars which have different keys than the config object
// keys.
func (v *Viper) getEnv(key string) string {
if v.envKeyReplacer != nil {
key = v.envKeyReplacer.Replace(key)
@ -324,7 +323,7 @@ func (v *Viper) getEnv(key string) string {
return os.Getenv(key)
}
// ConfigFileUsed returns the file used to populate the config registry
// ConfigFileUsed returns the file used to populate the config registry.
func ConfigFileUsed() string { return v.ConfigFileUsed() }
func (v *Viper) ConfigFileUsed() string { return v.configFile }
@ -597,32 +596,33 @@ func (v *Viper) Get(key string) interface{} {
return nil
}
valType := val
if v.typeByDefValue {
// TODO(bep) this branch isn't covered by a single test.
valType := val
path := strings.Split(lcaseKey, v.keyDelim)
defVal := v.searchMap(v.defaults, path)
if defVal != nil {
valType = defVal
}
switch valType.(type) {
case bool:
return cast.ToBool(val)
case string:
return cast.ToString(val)
case int64, int32, int16, int8, int:
return cast.ToInt(val)
case float64, float32:
return cast.ToFloat64(val)
case time.Time:
return cast.ToTime(val)
case time.Duration:
return cast.ToDuration(val)
case []string:
return cast.ToStringSlice(val)
}
}
switch valType.(type) {
case bool:
return cast.ToBool(val)
case string:
return cast.ToString(val)
case int64, int32, int16, int8, int:
return cast.ToInt(val)
case float64, float32:
return cast.ToFloat64(val)
case time.Time:
return cast.ToTime(val)
case time.Duration:
return cast.ToDuration(val)
case []string:
return cast.ToStringSlice(val)
}
return val
}
@ -856,7 +856,7 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
}
// BindFlagValue binds a specific key to a FlagValue.
// Example(where serverCmd is a Cobra instance):
// Example (where serverCmd is a Cobra instance):
//
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
@ -1329,7 +1329,7 @@ func (v *Viper) WatchRemoteConfigOnChannel() error {
return v.watchKeyValueConfigOnChannel()
}
// Unmarshall a Reader into a map.
// Unmarshal a Reader into a map.
// Should probably be an unexported function.
func unmarshalReader(in io.Reader, c map[string]interface{}) error {
return v.unmarshalReader(in, c)
@ -1588,7 +1588,6 @@ func (v *Viper) searchInPath(in string) (filename string) {
// Search all configPaths for any config file.
// Returns the first path that exists (and is a config file).
func (v *Viper) findConfigFile() (string, error) {
jww.INFO.Println("Searching for config in ", v.configPaths)
for _, cp := range v.configPaths {