New upstream version 1.0.0+git20171109.4dddf7c
Dr. Tobias Quathamer
6 years ago
16 | 16 | |
17 | 17 | script: |
18 | 18 | - go install ./... |
19 | - diff -u <(echo -n) <(gofmt -d .) | |
19 | 20 | - go test -v ./... |
20 | 21 | |
21 | 22 | after_success: |
5 | 5 | |
6 | 6 | * [Hugo](http://gohugo.io) |
7 | 7 | * [EMC RexRay](http://rexray.readthedocs.org/en/stable/) |
8 | * [Imgur's Incus](https://github.com/Imgur/incus) | |
8 | * [Imgur’s Incus](https://github.com/Imgur/incus) | |
9 | 9 | * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) |
10 | 10 | * [Docker Notary](https://github.com/docker/Notary) |
11 | 11 | * [BloomApi](https://www.bloomapi.com/) |
12 | 12 | * [doctl](https://github.com/digitalocean/doctl) |
13 | * [Clairctl](https://github.com/jgsqware/clairctl) | |
13 | 14 | |
14 | 15 | [![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) |
15 | 16 | |
16 | 17 | |
17 | 18 | ## What is Viper? |
18 | 19 | |
19 | Viper is a complete configuration solution for go applications including 12 factor apps. It is designed | |
20 | Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed | |
20 | 21 | to work within an application, and can handle all types of configuration needs |
21 | 22 | and formats. It supports: |
22 | 23 | |
67 | 68 | ### Establishing Defaults |
68 | 69 | |
69 | 70 | A good configuration system will support default values. A default value is not |
70 | required for a key, but it's useful in the event that a key hasn’t been set via | |
71 | required for a key, but it’s useful in the event that a key hasn’t been set via | |
71 | 72 | config file, environment variable, remote configuration or flag. |
72 | 73 | |
73 | 74 | Examples: |
115 | 116 | **Make sure you add all of the configPaths prior to calling `WatchConfig()`** |
116 | 117 | |
117 | 118 | ```go |
118 | viper.WatchConfig() | |
119 | viper.OnConfigChange(func(e fsnotify.Event) { | |
120 | fmt.Println("Config file changed:", e.Name) | |
121 | }) | |
119 | viper.WatchConfig() | |
120 | viper.OnConfigChange(func(e fsnotify.Event) { | |
121 | fmt.Println("Config file changed:", e.Name) | |
122 | }) | |
122 | 123 | ``` |
123 | 124 | |
124 | 125 | ### Reading Config from io.Reader |
183 | 184 | * `AutomaticEnv()` |
184 | 185 | * `BindEnv(string...) : error` |
185 | 186 | * `SetEnvPrefix(string)` |
186 | * `SetEnvReplacer(string...) *strings.Replacer` | |
187 | * `SetEnvKeyReplacer(string...) *strings.Replacer` | |
187 | 188 | |
188 | 189 | _When working with ENV variables, it’s important to recognize that Viper |
189 | 190 | treats ENV variables as case sensitive._ |
210 | 211 | check for a environment variable with a name matching the key uppercased and |
211 | 212 | prefixed with the `EnvPrefix` if set. |
212 | 213 | |
213 | `SetEnvReplacer` allows you to use a `strings.Replacer` object to rewrite Env | |
214 | `SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env | |
214 | 215 | keys to an extent. This is useful if you want to use `-` or something in your |
215 | 216 | `Get()` calls, but want your environmental variables to use `_` delimiters. An |
216 | 217 | example of using it can be found in `viper_test.go`. |
235 | 236 | it is accessed. This means you can bind as early as you want, even in an |
236 | 237 | `init()` function. |
237 | 238 | |
238 | The `BindPFlag()` method provides this functionality. | |
239 | For individual flags, the `BindPFlag()` method provides this functionality. | |
239 | 240 | |
240 | 241 | Example: |
241 | 242 | |
242 | 243 | ```go |
243 | 244 | serverCmd.Flags().Int("port", 1138, "Port to run Application server on") |
244 | 245 | viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) |
246 | ``` | |
247 | ||
248 | You can also bind an existing set of pflags (pflag.FlagSet): | |
249 | ||
250 | Example: | |
251 | ||
252 | ```go | |
253 | pflag.Int("flagname", 1234, "help message for flagname") | |
254 | ||
255 | pflag.Parse() | |
256 | viper.BindPFlags(pflag.CommandLine) | |
257 | ||
258 | i := viper.GetInt("flagname") // retrieve values from viper instead of pflag | |
245 | 259 | ``` |
246 | 260 | |
247 | 261 | The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude |
262 | 276 | ) |
263 | 277 | |
264 | 278 | func main() { |
279 | ||
280 | // using standard library "flag" package | |
281 | flag.Int("flagname", 1234, "help message for flagname") | |
282 | ||
265 | 283 | pflag.CommandLine.AddGoFlagSet(flag.CommandLine) |
266 | 284 | pflag.Parse() |
267 | ... | |
285 | viper.BindPFlags(pflag.CommandLine) | |
286 | ||
287 | i := viper.GetInt("flagname") // retrieve value from viper | |
288 | ||
289 | ... | |
268 | 290 | } |
269 | 291 | ``` |
270 | 292 | |
271 | 293 | #### Flag interfaces |
272 | 294 | |
273 | Viper provides two Go interfaces to bind other flag systems if you don't use `Pflags`. | |
295 | Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`. | |
274 | 296 | |
275 | 297 | `FlagValue` represents a single flag. This is a very simple example on how to implement this interface: |
276 | 298 | |
400 | 422 | |
401 | 423 | ## Getting Values From Viper |
402 | 424 | |
403 | In Viper, there are a few ways to get a value depending on the value's type. | |
425 | In Viper, there are a few ways to get a value depending on the value’s type. | |
404 | 426 | The following functions and methods exist: |
405 | 427 | |
406 | 428 | * `Get(key string) : interface{}` |
530 | 552 | ``` |
531 | 553 | |
532 | 554 | which creates a cache based on config information formatted as `subv`. |
533 | Now it's easy to create these 2 caches separately as: | |
555 | Now it’s easy to create these 2 caches separately as: | |
534 | 556 | |
535 | 557 | ```go |
536 | 558 | cfg1 := viper.Sub("app.cache1") |
61 | 61 | flag.Changed = true //hack for pflag usage |
62 | 62 | |
63 | 63 | assert.Equal(t, "testing_mutate", Get("testvalue")) |
64 | ||
65 | 64 | } |
32 | 32 | if err != nil { |
33 | 33 | return nil, err |
34 | 34 | } |
35 | resp,err := cm.Get(rp.Path()) | |
35 | resp, err := cm.Get(rp.Path()) | |
36 | 36 | if err != nil { |
37 | 37 | return nil, err |
38 | 38 | } |
39 | 39 | |
40 | 40 | return bytes.NewReader(resp), nil |
41 | 41 | } |
42 | ||
42 | 43 | func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) { |
43 | 44 | cm, err := getConfigManager(rp) |
44 | 45 | if err != nil { |
46 | 47 | } |
47 | 48 | quit := make(chan bool) |
48 | 49 | quitwc := make(chan bool) |
49 | viperResponsCh := make(chan *viper.RemoteResponse) | |
50 | viperResponsCh := make(chan *viper.RemoteResponse) | |
50 | 51 | cryptoResponseCh := cm.Watch(rp.Path(), quit) |
51 | 52 | // need this function to convert the Channel response form crypt.Response to viper.Response |
52 | go func(cr <-chan *crypt.Response,vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) { | |
53 | go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) { | |
53 | 54 | for { |
54 | 55 | select { |
55 | case <- quitwc: | |
56 | case <-quitwc: | |
56 | 57 | quit <- true |
57 | 58 | return |
58 | 59 | case resp := <-cr: |
64 | 65 | } |
65 | 66 | |
66 | 67 | } |
67 | }(cryptoResponseCh,viperResponsCh,quitwc,quit) | |
68 | }(cryptoResponseCh, viperResponsCh, quitwc, quit) | |
68 | 69 | |
69 | return viperResponsCh,quitwc | |
70 | ||
70 | return viperResponsCh, quitwc | |
71 | 71 | } |
72 | 72 | |
73 | ||
74 | 73 | func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) { |
75 | ||
76 | 74 | var cm crypt.ConfigManager |
77 | 75 | var err error |
78 | 76 | |
98 | 96 | return nil, err |
99 | 97 | } |
100 | 98 | return cm, nil |
101 | ||
102 | 99 | } |
103 | 100 | |
104 | 101 | func init() { |
23 | 23 | "github.com/hashicorp/hcl" |
24 | 24 | "github.com/magiconair/properties" |
25 | 25 | toml "github.com/pelletier/go-toml" |
26 | "github.com/spf13/afero" | |
26 | 27 | "github.com/spf13/cast" |
27 | 28 | jww "github.com/spf13/jwalterweatherman" |
28 | 29 | "gopkg.in/yaml.v2" |
120 | 121 | } |
121 | 122 | |
122 | 123 | // Check if File / Directory Exists |
123 | func exists(path string) (bool, error) { | |
124 | _, err := v.fs.Stat(path) | |
124 | func exists(fs afero.Fs, path string) (bool, error) { | |
125 | _, err := fs.Stat(path) | |
125 | 126 | if err == nil { |
126 | 127 | return true, nil |
127 | 128 | } |
15 | 15 | ) |
16 | 16 | |
17 | 17 | func TestCopyAndInsensitiviseMap(t *testing.T) { |
18 | ||
19 | 18 | var ( |
20 | 19 | given = map[string]interface{}{ |
21 | 20 | "Foo": 32, |
68 | 68 | } |
69 | 69 | |
70 | 70 | // UnsupportedRemoteProviderError denotes encountering an unsupported remote |
71 | // provider. Currently only etcd and Consul are | |
72 | // supported. | |
71 | // provider. Currently only etcd and Consul are supported. | |
73 | 72 | type UnsupportedRemoteProviderError string |
74 | 73 | |
75 | 74 | // Error returns the formatted remote provider error. |
282 | 281 | }() |
283 | 282 | } |
284 | 283 | |
285 | // SetConfigFile explicitly defines the path, name and extension of the config file | |
286 | // Viper will use this and not check any of the config paths | |
284 | // SetConfigFile explicitly defines the path, name and extension of the config file. | |
285 | // Viper will use this and not check any of the config paths. | |
287 | 286 | func SetConfigFile(in string) { v.SetConfigFile(in) } |
288 | 287 | func (v *Viper) SetConfigFile(in string) { |
289 | 288 | if in != "" { |
292 | 291 | } |
293 | 292 | |
294 | 293 | // SetEnvPrefix defines a prefix that ENVIRONMENT variables will use. |
295 | // E.g. if your prefix is "spf", the env registry | |
296 | // will look for env. variables that start with "SPF_" | |
294 | // E.g. if your prefix is "spf", the env registry will look for env | |
295 | // variables that start with "SPF_". | |
297 | 296 | func SetEnvPrefix(in string) { v.SetEnvPrefix(in) } |
298 | 297 | func (v *Viper) SetEnvPrefix(in string) { |
299 | 298 | if in != "" { |
311 | 310 | |
312 | 311 | // TODO: should getEnv logic be moved into find(). Can generalize the use of |
313 | 312 | // rewriting keys many things, Ex: Get('someKey') -> some_key |
314 | // (cammel case to snake case for JSON keys perhaps) | |
313 | // (camel case to snake case for JSON keys perhaps) | |
315 | 314 | |
316 | 315 | // getEnv is a wrapper around os.Getenv which replaces characters in the original |
317 | // key. This allows env vars which have different keys then the config object | |
318 | // keys | |
316 | // key. This allows env vars which have different keys than the config object | |
317 | // keys. | |
319 | 318 | func (v *Viper) getEnv(key string) string { |
320 | 319 | if v.envKeyReplacer != nil { |
321 | 320 | key = v.envKeyReplacer.Replace(key) |
323 | 322 | return os.Getenv(key) |
324 | 323 | } |
325 | 324 | |
326 | // ConfigFileUsed returns the file used to populate the config registry | |
325 | // ConfigFileUsed returns the file used to populate the config registry. | |
327 | 326 | func ConfigFileUsed() string { return v.ConfigFileUsed() } |
328 | 327 | func (v *Viper) ConfigFileUsed() string { return v.configFile } |
329 | 328 | |
747 | 746 | } |
748 | 747 | |
749 | 748 | // defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot |
750 | // of time.Duration values | |
749 | // of time.Duration values & string slices | |
751 | 750 | func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig { |
752 | 751 | return &mapstructure.DecoderConfig{ |
753 | 752 | Metadata: nil, |
754 | 753 | Result: output, |
755 | 754 | WeaklyTypedInput: true, |
756 | DecodeHook: mapstructure.StringToTimeDurationHookFunc(), | |
755 | DecodeHook: mapstructure.ComposeDecodeHookFunc( | |
756 | mapstructure.StringToTimeDurationHookFunc(), | |
757 | mapstructure.StringToSliceHookFunc(","), | |
758 | ), | |
757 | 759 | } |
758 | 760 | } |
759 | 761 | |
814 | 816 | } |
815 | 817 | |
816 | 818 | // BindFlagValue binds a specific key to a FlagValue. |
817 | // Example(where serverCmd is a Cobra instance): | |
819 | // Example (where serverCmd is a Cobra instance): | |
818 | 820 | // |
819 | 821 | // serverCmd.Flags().Int("port", 1138, "Port to run Application server on") |
820 | 822 | // Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port")) |
1287 | 1289 | return v.watchKeyValueConfigOnChannel() |
1288 | 1290 | } |
1289 | 1291 | |
1290 | // Unmarshall a Reader into a map. | |
1292 | // Unmarshal a Reader into a map. | |
1291 | 1293 | // Should probably be an unexported function. |
1292 | 1294 | func unmarshalReader(in io.Reader, c map[string]interface{}) error { |
1293 | 1295 | return v.unmarshalReader(in, c) |
1534 | 1536 | jww.DEBUG.Println("Searching for config in ", in) |
1535 | 1537 | for _, ext := range SupportedExts { |
1536 | 1538 | jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) |
1537 | if b, _ := exists(filepath.Join(in, v.configName+"."+ext)); b { | |
1539 | if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { | |
1538 | 1540 | jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) |
1539 | 1541 | return filepath.Join(in, v.configName+"."+ext) |
1540 | 1542 | } |
1546 | 1548 | // Search all configPaths for any config file. |
1547 | 1549 | // Returns the first path that exists (and is a config file). |
1548 | 1550 | func (v *Viper) findConfigFile() (string, error) { |
1549 | ||
1550 | 1551 | jww.INFO.Println("Searching for config in ", v.configPaths) |
1551 | 1552 | |
1552 | 1553 | for _, cp := range v.configPaths { |