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: script:
- go install ./... - go install ./...
- diff -u <(echo -n) <(gofmt -d .)
- go test -v ./... - go test -v ./...
after_success: after_success:

View file

@ -6,18 +6,19 @@ Many Go projects are built using Viper including:
* [Hugo](http://gohugo.io) * [Hugo](http://gohugo.io)
* [EMC RexRay](http://rexray.readthedocs.org/en/stable/) * [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) * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
* [Docker Notary](https://github.com/docker/Notary) * [Docker Notary](https://github.com/docker/Notary)
* [BloomApi](https://www.bloomapi.com/) * [BloomApi](https://www.bloomapi.com/)
* [doctl](https://github.com/digitalocean/doctl) * [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) [![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? ## 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 to work within an application, and can handle all types of configuration needs
and formats. It supports: and formats. It supports:
@ -68,7 +69,7 @@ Viper configuration keys are case insensitive.
### Establishing Defaults ### Establishing Defaults
A good configuration system will support default values. A default value is not 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. config file, environment variable, remote configuration or flag.
Examples: 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()`** **Make sure you add all of the configPaths prior to calling `WatchConfig()`**
```go ```go
viper.WatchConfig() viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) { viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name) fmt.Println("Config file changed:", e.Name)
}) })
``` ```
### Reading Config from io.Reader ### 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 it is accessed. This means you can bind as early as you want, even in an
`init()` function. `init()` function.
The `BindPFlag()` method provides this functionality. For individual flags, the `BindPFlag()` method provides this functionality.
Example: Example:
@ -245,6 +246,19 @@ serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) 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 [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/) 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 package from the standard library. The pflag package can handle the flags
@ -263,15 +277,23 @@ import (
) )
func main() { func main() {
// using standard library "flag" package
flag.Int("flagname", 1234, "help message for flagname")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse() pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
i := viper.GetInt("flagname") // retrieve value from viper
... ...
} }
``` ```
#### Flag interfaces #### 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: `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 ## 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: The following functions and methods exist:
* `Get(key string) : interface{}` * `Get(key string) : interface{}`
@ -531,7 +553,7 @@ func NewCache(cfg *Viper) *Cache {...}
``` ```
which creates a cache based on config information formatted as `subv`. 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 ```go
cfg1 := viper.Sub("app.cache1") 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 to use a single central repository for their configuration, the viper package
provides this. It is similar to a singleton. 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. style approach.
### Working with multiple vipers ### Working with multiple vipers
You can also create many different vipers for use in your application. Each will 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 different config file, key value store, etc. All of the functions that viper
package supports are mirrored as methods on a 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 flag.Changed = true //hack for pflag usage
assert.Equal(t, "testing_mutate", Get("testvalue")) 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 { if err != nil {
return nil, err return nil, err
} }
resp,err := cm.Get(rp.Path()) resp, err := cm.Get(rp.Path())
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bytes.NewReader(resp), nil return bytes.NewReader(resp), nil
} }
func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) { func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
cm, err := getConfigManager(rp) cm, err := getConfigManager(rp)
if err != nil { if err != nil {
@ -50,10 +51,10 @@ func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *vi
viperResponsCh := make(chan *viper.RemoteResponse) viperResponsCh := make(chan *viper.RemoteResponse)
cryptoResponseCh := cm.Watch(rp.Path(), quit) cryptoResponseCh := cm.Watch(rp.Path(), quit)
// need this function to convert the Channel response form crypt.Response to viper.Response // 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 { for {
select { select {
case <- quitwc: case <-quitwc:
quit <- true quit <- true
return return
case resp := <-cr: case resp := <-cr:
@ -65,15 +66,12 @@ func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *vi
} }
} }
}(cryptoResponseCh,viperResponsCh,quitwc,quit) }(cryptoResponseCh, viperResponsCh, quitwc, quit)
return viperResponsCh,quitwc
return viperResponsCh, quitwc
} }
func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) { func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
var cm crypt.ConfigManager var cm crypt.ConfigManager
var err error var err error
@ -99,7 +97,6 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
return nil, err return nil, err
} }
return cm, nil return cm, nil
} }
func init() { func init() {

View file

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

View file

@ -69,8 +69,7 @@ func (str UnsupportedConfigError) Error() string {
} }
// UnsupportedRemoteProviderError denotes encountering an unsupported remote // UnsupportedRemoteProviderError denotes encountering an unsupported remote
// provider. Currently only etcd and Consul are // provider. Currently only etcd and Consul are supported.
// supported.
type UnsupportedRemoteProviderError string type UnsupportedRemoteProviderError string
// Error returns the formatted remote provider error. // 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 // SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths // Viper will use this and not check any of the config paths.
func SetConfigFile(in string) { v.SetConfigFile(in) } func SetConfigFile(in string) { v.SetConfigFile(in) }
func (v *Viper) SetConfigFile(in string) { func (v *Viper) SetConfigFile(in string) {
if in != "" { if in != "" {
@ -293,8 +292,8 @@ func (v *Viper) SetConfigFile(in string) {
} }
// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use. // SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
// E.g. if your prefix is "spf", the env registry // E.g. if your prefix is "spf", the env registry will look for env
// will look for env. variables that start with "SPF_" // variables that start with "SPF_".
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) } func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
func (v *Viper) SetEnvPrefix(in string) { func (v *Viper) SetEnvPrefix(in string) {
if in != "" { 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 // TODO: should getEnv logic be moved into find(). Can generalize the use of
// rewriting keys many things, Ex: Get('someKey') -> some_key // 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 // 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 // key. This allows env vars which have different keys than the config object
// keys // keys.
func (v *Viper) getEnv(key string) string { func (v *Viper) getEnv(key string) string {
if v.envKeyReplacer != nil { if v.envKeyReplacer != nil {
key = v.envKeyReplacer.Replace(key) key = v.envKeyReplacer.Replace(key)
@ -324,7 +323,7 @@ func (v *Viper) getEnv(key string) string {
return os.Getenv(key) 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 ConfigFileUsed() string { return v.ConfigFileUsed() }
func (v *Viper) ConfigFileUsed() string { return v.configFile } func (v *Viper) ConfigFileUsed() string { return v.configFile }
@ -597,15 +596,14 @@ func (v *Viper) Get(key string) interface{} {
return nil return nil
} }
valType := val
if v.typeByDefValue { if v.typeByDefValue {
// TODO(bep) this branch isn't covered by a single test. // TODO(bep) this branch isn't covered by a single test.
valType := val
path := strings.Split(lcaseKey, v.keyDelim) path := strings.Split(lcaseKey, v.keyDelim)
defVal := v.searchMap(v.defaults, path) defVal := v.searchMap(v.defaults, path)
if defVal != nil { if defVal != nil {
valType = defVal valType = defVal
} }
}
switch valType.(type) { switch valType.(type) {
case bool: case bool:
@ -623,6 +621,8 @@ func (v *Viper) Get(key string) interface{} {
case []string: case []string:
return cast.ToStringSlice(val) return cast.ToStringSlice(val)
} }
}
return val return val
} }
@ -856,7 +856,7 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
} }
// BindFlagValue binds a specific key to a FlagValue. // 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") // serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port")) // Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
@ -1329,7 +1329,7 @@ func (v *Viper) WatchRemoteConfigOnChannel() error {
return v.watchKeyValueConfigOnChannel() return v.watchKeyValueConfigOnChannel()
} }
// Unmarshall a Reader into a map. // Unmarshal a Reader into a map.
// Should probably be an unexported function. // Should probably be an unexported function.
func unmarshalReader(in io.Reader, c map[string]interface{}) error { func unmarshalReader(in io.Reader, c map[string]interface{}) error {
return v.unmarshalReader(in, c) return v.unmarshalReader(in, c)
@ -1588,7 +1588,6 @@ func (v *Viper) searchInPath(in string) (filename string) {
// Search all configPaths for any config file. // Search all configPaths for any config file.
// Returns the first path that exists (and is a config file). // Returns the first path that exists (and is a config file).
func (v *Viper) findConfigFile() (string, error) { func (v *Viper) findConfigFile() (string, error) {
jww.INFO.Println("Searching for config in ", v.configPaths) jww.INFO.Println("Searching for config in ", v.configPaths)
for _, cp := range v.configPaths { for _, cp := range v.configPaths {