Codebase list golang-github-spf13-viper / 56056cb
Update upstream source from tag 'upstream/1.3.1' Update to upstream version '1.3.1' with Debian dir 010bd00e7728dff865abc72548ddd9bf85a89537 Anthony Fok 5 years ago
7 changed file(s) with 338 addition(s) and 58 deletion(s). Raw diff Collapse all Expand all
2020
2121 *.exe
2222 *.test
23 *.bench
23 *.bench
24
25 .vscode
26
27 # exclude dependencies in the `/vendor` folder
28 vendor
00 go_import_path: github.com/spf13/viper
11
22 language: go
3
4 env:
5 global:
6 - GO111MODULE="on"
7
38 go:
4 - 1.10.x
59 - 1.11.x
610 - tip
711
178178 ### Working with Environment Variables
179179
180180 Viper has full support for environment variables. This enables 12 factor
181 applications out of the box. There are four methods that exist to aid working
181 applications out of the box. There are five methods that exist to aid working
182182 with ENV:
183183
184184 * `AutomaticEnv()`
185185 * `BindEnv(string...) : error`
186186 * `SetEnvPrefix(string)`
187187 * `SetEnvKeyReplacer(string...) *strings.Replacer`
188 * `AllowEmptyEnvVar(bool)`
188189
189190 _When working with ENV variables, it’s important to recognize that Viper
190191 treats ENV variables as case sensitive._
215216 keys to an extent. This is useful if you want to use `-` or something in your
216217 `Get()` calls, but want your environmental variables to use `_` delimiters. An
217218 example of using it can be found in `viper_test.go`.
219
220 By default empty environment variables are considered unset and will fall back to
221 the next configuration source. To treat empty environment variables as set, use
222 the `AllowEmptyEnv` method.
218223
219224 #### Env example
220225
00 module github.com/spf13/viper
11
22 require (
3 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
4 github.com/coreos/etcd v3.3.10+incompatible // indirect
5 github.com/coreos/go-etcd v2.0.0+incompatible // indirect
6 github.com/coreos/go-semver v0.2.0 // indirect
37 github.com/fsnotify/fsnotify v1.4.7
48 github.com/hashicorp/hcl v1.0.0
59 github.com/magiconair/properties v1.8.0
6 github.com/mitchellh/mapstructure v1.0.0
10 github.com/mitchellh/mapstructure v1.1.2
711 github.com/pelletier/go-toml v1.2.0
812 github.com/spf13/afero v1.1.2
9 github.com/spf13/cast v1.2.0
13 github.com/spf13/cast v1.3.0
1014 github.com/spf13/jwalterweatherman v1.0.0
11 github.com/spf13/pflag v1.0.2
12 golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 // indirect
15 github.com/spf13/pflag v1.0.3
16 github.com/stretchr/testify v1.2.2
17 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 // indirect
18 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77
19 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect
20 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a // indirect
1321 golang.org/x/text v0.3.0 // indirect
14 gopkg.in/yaml.v2 v2.2.1
22 gopkg.in/yaml.v2 v2.2.2
1523 )
0 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA=
1 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
2 github.com/coreos/etcd v3.3.10+incompatible h1:KjVWqrZ5U0wa3CxY2AxlH6/UcB+PK2td1DcsYhA+HRs=
3 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
4 github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo=
5 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
6 github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
7 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
8 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
09 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
110 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
211 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
413 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
514 github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
615 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
7 github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
8 github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
16 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
17 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
918 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
1019 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
20 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
21 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1122 github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
1223 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
13 github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
14 github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
24 github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
25 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
1526 github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
1627 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
17 github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
18 github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
19 golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg=
20 golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
28 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
29 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
30 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
31 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
32 github.com/stretchr/testify v1.2.3-0.20181014000028-04af85275a5c h1:03OmljzZYsezlgAfa+f/cY8E8XXPiFh5bgANMhUlDI4=
33 github.com/stretchr/testify v1.2.3-0.20181014000028-04af85275a5c/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
34 github.com/stretchr/testify v1.2.3-0.20181115233458-8019298d9fa5 h1:ixuBiBNIIQ3RKRSZy9B0DgaqreXG6NDHrbwAFGg8Mwk=
35 github.com/stretchr/testify v1.2.3-0.20181115233458-8019298d9fa5/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
36 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648=
37 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
38 github.com/xordataexchange/crypt v0.0.2 h1:VBfFXTpEwLq2hzs42qCHOyKw5AqEm9DYGqBuINmzUZY=
39 github.com/xordataexchange/crypt v0.0.2/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
40 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=
41 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
42 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
43 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
44 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
45 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
2146 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
2247 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
2348 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24 gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
25 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
49 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
50 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
2929 "path/filepath"
3030 "reflect"
3131 "strings"
32 "sync"
3233 "time"
3334
3435 yaml "gopkg.in/yaml.v2"
185186
186187 automaticEnvApplied bool
187188 envKeyReplacer *strings.Replacer
189 allowEmptyEnv bool
188190
189191 config map[string]interface{}
190192 override map[string]interface{}
275277 }
276278
277279 func WatchConfig() { v.WatchConfig() }
280
278281 func (v *Viper) WatchConfig() {
282 initWG := sync.WaitGroup{}
283 initWG.Add(1)
279284 go func() {
280285 watcher, err := fsnotify.NewWatcher()
281286 if err != nil {
282287 log.Fatal(err)
283288 }
284289 defer watcher.Close()
285
286290 // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
287291 filename, err := v.getConfigFile()
288292 if err != nil {
289 log.Println("error:", err)
293 log.Printf("error: %v\n", err)
290294 return
291295 }
292296
293297 configFile := filepath.Clean(filename)
294298 configDir, _ := filepath.Split(configFile)
295
296 done := make(chan bool)
299 realConfigFile, _ := filepath.EvalSymlinks(filename)
300
301 eventsWG := sync.WaitGroup{}
302 eventsWG.Add(1)
297303 go func() {
298304 for {
299305 select {
300 case event := <-watcher.Events:
301 // we only care about the config file
302 if filepath.Clean(event.Name) == configFile {
303 if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
304 err := v.ReadInConfig()
305 if err != nil {
306 log.Println("error:", err)
307 }
308 if v.onConfigChange != nil {
309 v.onConfigChange(event)
310 }
306 case event, ok := <-watcher.Events:
307 if !ok { // 'Events' channel is closed
308 eventsWG.Done()
309 return
310 }
311 currentConfigFile, _ := filepath.EvalSymlinks(filename)
312 // we only care about the config file with the following cases:
313 // 1 - if the config file was modified or created
314 // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
315 const writeOrCreateMask = fsnotify.Write | fsnotify.Create
316 if (filepath.Clean(event.Name) == configFile &&
317 event.Op&writeOrCreateMask != 0) ||
318 (currentConfigFile != "" && currentConfigFile != realConfigFile) {
319 realConfigFile = currentConfigFile
320 err := v.ReadInConfig()
321 if err != nil {
322 log.Printf("error reading config file: %v\n", err)
311323 }
324 if v.onConfigChange != nil {
325 v.onConfigChange(event)
326 }
327 } else if filepath.Clean(event.Name) == configFile &&
328 event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
329 eventsWG.Done()
330 return
312331 }
313 case err := <-watcher.Errors:
314 log.Println("error:", err)
332
333 case err, ok := <-watcher.Errors:
334 if ok { // 'Errors' channel is not closed
335 log.Printf("watcher error: %v\n", err)
336 }
337 eventsWG.Done()
338 return
315339 }
316340 }
317341 }()
318
319342 watcher.Add(configDir)
320 <-done
343 initWG.Done() // done initalizing the watch in this go routine, so the parent routine can move on...
344 eventsWG.Wait() // now, wait for event loop to end in this go-routine...
321345 }()
346 initWG.Wait() // make sure that the go routine above fully ended before returning
322347 }
323348
324349 // SetConfigFile explicitly defines the path, name and extension of the config file.
348373 return strings.ToUpper(in)
349374 }
350375
376 // AllowEmptyEnv tells Viper to consider set,
377 // but empty environment variables as valid values instead of falling back.
378 // For backward compatibility reasons this is false by default.
379 func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) }
380 func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) {
381 v.allowEmptyEnv = allowEmptyEnv
382 }
383
351384 // TODO: should getEnv logic be moved into find(). Can generalize the use of
352385 // rewriting keys many things, Ex: Get('someKey') -> some_key
353386 // (camel case to snake case for JSON keys perhaps)
355388 // getEnv is a wrapper around os.Getenv which replaces characters in the original
356389 // key. This allows env vars which have different keys than the config object
357390 // keys.
358 func (v *Viper) getEnv(key string) string {
391 func (v *Viper) getEnv(key string) (string, bool) {
359392 if v.envKeyReplacer != nil {
360393 key = v.envKeyReplacer.Replace(key)
361394 }
362 return os.Getenv(key)
395
396 val, ok := os.LookupEnv(key)
397
398 return val, ok && (v.allowEmptyEnv || val != "")
363399 }
364400
365401 // ConfigFileUsed returns the file used to populate the config registry.
586622 // "foo.bar.baz" in a lower-priority map
587623 func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
588624 var parentKey string
589 var val string
590625 for i := 1; i < len(path); i++ {
591626 parentKey = strings.Join(path[0:i], v.keyDelim)
592 if val = v.getEnv(v.mergeWithEnvPrefix(parentKey)); val != "" {
627 if _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok {
593628 return parentKey
594629 }
595630 }
9681003 if v.automaticEnvApplied {
9691004 // even if it hasn't been registered, if automaticEnv is used,
9701005 // check any Get request
971 if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" {
1006 if val, ok := v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); ok {
9721007 return val
9731008 }
9741009 if nested && v.isPathShadowedInAutoEnv(path) != "" {
9771012 }
9781013 envkey, exists := v.env[lcaseKey]
9791014 if exists {
980 if val = v.getEnv(envkey); val != "" {
1015 if val, ok := v.getEnv(envkey); ok {
9811016 return val
9821017 }
9831018 }
12231258 // MergeConfig merges a new configuration with an existing config.
12241259 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
12251260 func (v *Viper) MergeConfig(in io.Reader) error {
1226 if v.config == nil {
1227 v.config = make(map[string]interface{})
1228 }
12291261 cfg := make(map[string]interface{})
12301262 if err := v.unmarshalReader(in, cfg); err != nil {
12311263 return err
12321264 }
1265 return v.MergeConfigMap(cfg)
1266 }
1267
1268 // MergeConfigMap merges the configuration from the map given with an existing config.
1269 // Note that the map given may be modified.
1270 func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) }
1271 func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
1272 if v.config == nil {
1273 v.config = make(map[string]interface{})
1274 }
1275 insensitiviseMap(cfg)
12331276 mergeMaps(cfg, v.config, nil)
12341277 return nil
12351278 }
1111 "io"
1212 "io/ioutil"
1313 "os"
14 "os/exec"
1415 "path"
1516 "reflect"
17 "runtime"
1618 "sort"
1719 "strings"
20 "sync"
1821 "testing"
1922 "time"
2023
24 "github.com/fsnotify/fsnotify"
2125 "github.com/mitchellh/mapstructure"
2226 "github.com/spf13/afero"
2327 "github.com/spf13/cast"
2428
2529 "github.com/spf13/pflag"
2630 "github.com/stretchr/testify/assert"
31 "github.com/stretchr/testify/require"
2732 )
2833
2934 var yamlExample = []byte(`Hacker: true
380385
381386 assert.Equal(t, "crunk", Get("name"))
382387
388 }
389
390 func TestEmptyEnv(t *testing.T) {
391 initJSON()
392
393 BindEnv("type") // Empty environment variable
394 BindEnv("name") // Bound, but not set environment variable
395
396 os.Clearenv()
397
398 os.Setenv("TYPE", "")
399
400 assert.Equal(t, "donut", Get("type"))
401 assert.Equal(t, "Cake", Get("name"))
402 }
403
404 func TestEmptyEnv_Allowed(t *testing.T) {
405 initJSON()
406
407 AllowEmptyEnv(true)
408
409 BindEnv("type") // Empty environment variable
410 BindEnv("name") // Bound, but not set environment variable
411
412 os.Clearenv()
413
414 os.Setenv("TYPE", "")
415
416 assert.Equal(t, "", Get("type"))
417 assert.Equal(t, "Cake", Get("name"))
383418 }
384419
385420 func TestEnvPrefix(t *testing.T) {
577612 }
578613
579614 func TestBindPFlagsStringSlice(t *testing.T) {
580 for _, testValue := range []struct {
615 tests := []struct {
581616 Expected []string
582617 Value string
583618 }{
584 {[]string{}, ""},
619 {nil, ""},
585620 {[]string{"jeden"}, "jeden"},
586621 {[]string{"dwa", "trzy"}, "dwa,trzy"},
587 {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} {
622 {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
623 }
624
625 v := New() // create independent Viper object
626 defaultVal := []string{"default"}
627 v.SetDefault("stringslice", defaultVal)
628
629 for _, testValue := range tests {
630 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
631 flagSet.StringSlice("stringslice", testValue.Expected, "test")
588632
589633 for _, changed := range []bool{true, false} {
590 v := New() // create independent Viper object
591 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
592 flagSet.StringSlice("stringslice", testValue.Expected, "test")
593 flagSet.Visit(func(f *pflag.Flag) {
594 if len(testValue.Value) > 0 {
595 f.Value.Set(testValue.Value)
596 f.Changed = changed
597 }
634 flagSet.VisitAll(func(f *pflag.Flag) {
635 f.Value.Set(testValue.Value)
636 f.Changed = changed
598637 })
599638
600639 err := v.BindPFlags(flagSet)
609648 if err := v.Unmarshal(val); err != nil {
610649 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
611650 }
612 assert.Equal(t, testValue.Expected, val.StringSlice)
651 if changed {
652 assert.Equal(t, testValue.Expected, val.StringSlice)
653 } else {
654 assert.Equal(t, defaultVal, val.StringSlice)
655 }
613656 }
614657 }
615658 }
11861229 }
11871230 }
11881231
1232 func TestMergeConfigMap(t *testing.T) {
1233 v := New()
1234 v.SetConfigType("yml")
1235 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
1236 t.Fatal(err)
1237 }
1238
1239 assert := func(i int) {
1240 large := v.GetInt("hello.lagrenum")
1241 pop := v.GetInt("hello.pop")
1242 if large != 765432101234567 {
1243 t.Fatal("Got large num:", large)
1244 }
1245
1246 if pop != i {
1247 t.Fatal("Got pop:", pop)
1248 }
1249 }
1250
1251 assert(37890)
1252
1253 update := map[string]interface{}{
1254 "Hello": map[string]interface{}{
1255 "Pop": 1234,
1256 },
1257 "World": map[interface{}]interface{}{
1258 "Rock": 345,
1259 },
1260 }
1261
1262 if err := v.MergeConfigMap(update); err != nil {
1263 t.Fatal(err)
1264 }
1265
1266 if rock := v.GetInt("world.rock"); rock != 345 {
1267 t.Fatal("Got rock:", rock)
1268 }
1269
1270 assert(1234)
1271
1272 }
1273
11891274 func TestUnmarshalingWithAliases(t *testing.T) {
11901275 v := New()
11911276 v.SetDefault("ID", 1)
14051490
14061491 }
14071492
1493 func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
1494 watchDir, err := ioutil.TempDir("", "")
1495 require.Nil(t, err)
1496 configFile := path.Join(watchDir, "config.yaml")
1497 err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
1498 require.Nil(t, err)
1499 cleanup := func() {
1500 os.RemoveAll(watchDir)
1501 }
1502 v := New()
1503 v.SetConfigFile(configFile)
1504 err = v.ReadInConfig()
1505 require.Nil(t, err)
1506 require.Equal(t, "bar", v.Get("foo"))
1507 return v, configFile, cleanup
1508 }
1509
1510 func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
1511 watchDir, err := ioutil.TempDir("", "")
1512 require.Nil(t, err)
1513 dataDir1 := path.Join(watchDir, "data1")
1514 err = os.Mkdir(dataDir1, 0777)
1515 require.Nil(t, err)
1516 realConfigFile := path.Join(dataDir1, "config.yaml")
1517 t.Logf("Real config file location: %s\n", realConfigFile)
1518 err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
1519 require.Nil(t, err)
1520 cleanup := func() {
1521 os.RemoveAll(watchDir)
1522 }
1523 // now, symlink the tm `data1` dir to `data` in the baseDir
1524 os.Symlink(dataDir1, path.Join(watchDir, "data"))
1525 // and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
1526 configFile := path.Join(watchDir, "config.yaml")
1527 os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
1528 t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
1529 // init Viper
1530 v := New()
1531 v.SetConfigFile(configFile)
1532 err = v.ReadInConfig()
1533 require.Nil(t, err)
1534 require.Equal(t, "bar", v.Get("foo"))
1535 return v, watchDir, configFile, cleanup
1536 }
1537
1538 func TestWatchFile(t *testing.T) {
1539 if runtime.GOOS == "linux" {
1540 // TODO(bep) FIX ME
1541 t.Skip("Skip test on Linux ...")
1542 }
1543
1544 t.Run("file content changed", func(t *testing.T) {
1545 // given a `config.yaml` file being watched
1546 v, configFile, cleanup := newViperWithConfigFile(t)
1547 defer cleanup()
1548 _, err := os.Stat(configFile)
1549 require.NoError(t, err)
1550 t.Logf("test config file: %s\n", configFile)
1551 wg := sync.WaitGroup{}
1552 wg.Add(1)
1553 v.OnConfigChange(func(in fsnotify.Event) {
1554 t.Logf("config file changed")
1555 wg.Done()
1556 })
1557 v.WatchConfig()
1558 // when overwriting the file and waiting for the custom change notification handler to be triggered
1559 err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
1560 wg.Wait()
1561 // then the config value should have changed
1562 require.Nil(t, err)
1563 assert.Equal(t, "baz", v.Get("foo"))
1564 })
1565
1566 t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
1567 // skip if not executed on Linux
1568 if runtime.GOOS != "linux" {
1569 t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
1570 }
1571 v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
1572 // defer cleanup()
1573 wg := sync.WaitGroup{}
1574 v.WatchConfig()
1575 v.OnConfigChange(func(in fsnotify.Event) {
1576 t.Logf("config file changed")
1577 wg.Done()
1578 })
1579 wg.Add(1)
1580 // when link to another `config.yaml` file
1581 dataDir2 := path.Join(watchDir, "data2")
1582 err := os.Mkdir(dataDir2, 0777)
1583 require.Nil(t, err)
1584 configFile2 := path.Join(dataDir2, "config.yaml")
1585 err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
1586 require.Nil(t, err)
1587 // change the symlink using the `ln -sfn` command
1588 err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
1589 require.Nil(t, err)
1590 wg.Wait()
1591 // then
1592 require.Nil(t, err)
1593 assert.Equal(t, "baz", v.Get("foo"))
1594 })
1595
1596 }
1597
14081598 func BenchmarkGetBool(b *testing.B) {
14091599 key := "BenchmarkGetBool"
14101600 v = New()