34 "strings" |
33 "strings" |
35 "sync" |
34 "sync" |
36 "time" |
35 "time" |
37 |
36 |
38 "github.com/fsnotify/fsnotify" |
37 "github.com/fsnotify/fsnotify" |
39 "github.com/hashicorp/hcl" |
|
40 "github.com/hashicorp/hcl/hcl/printer" |
|
41 "github.com/magiconair/properties" |
|
42 "github.com/mitchellh/mapstructure" |
38 "github.com/mitchellh/mapstructure" |
43 "github.com/pelletier/go-toml" |
|
44 "github.com/spf13/afero" |
39 "github.com/spf13/afero" |
45 "github.com/spf13/cast" |
40 "github.com/spf13/cast" |
46 jww "github.com/spf13/jwalterweatherman" |
|
47 "github.com/spf13/pflag" |
41 "github.com/spf13/pflag" |
48 "github.com/subosito/gotenv" |
42 |
49 "gopkg.in/ini.v1" |
43 "github.com/spf13/viper/internal/encoding" |
50 "gopkg.in/yaml.v2" |
44 "github.com/spf13/viper/internal/encoding/dotenv" |
|
45 "github.com/spf13/viper/internal/encoding/hcl" |
|
46 "github.com/spf13/viper/internal/encoding/ini" |
|
47 "github.com/spf13/viper/internal/encoding/javaproperties" |
|
48 "github.com/spf13/viper/internal/encoding/json" |
|
49 "github.com/spf13/viper/internal/encoding/toml" |
|
50 "github.com/spf13/viper/internal/encoding/yaml" |
51 ) |
51 ) |
52 |
52 |
53 // ConfigMarshalError happens when failing to marshal the configuration. |
53 // ConfigMarshalError happens when failing to marshal the configuration. |
54 type ConfigMarshalError struct { |
54 type ConfigMarshalError struct { |
55 err error |
55 err error |
213 pflags map[string]FlagValue |
213 pflags map[string]FlagValue |
214 env map[string][]string |
214 env map[string][]string |
215 aliases map[string]string |
215 aliases map[string]string |
216 typeByDefValue bool |
216 typeByDefValue bool |
217 |
217 |
218 // Store read properties on the object so that we can write back in order with comments. |
|
219 // This will only be used if the configuration read is a properties file. |
|
220 properties *properties.Properties |
|
221 |
|
222 onConfigChange func(fsnotify.Event) |
218 onConfigChange func(fsnotify.Event) |
|
219 |
|
220 logger Logger |
|
221 |
|
222 // TODO: should probably be protected with a mutex |
|
223 encoderRegistry *encoding.EncoderRegistry |
|
224 decoderRegistry *encoding.DecoderRegistry |
223 } |
225 } |
224 |
226 |
225 // New returns an initialized Viper instance. |
227 // New returns an initialized Viper instance. |
226 func New() *Viper { |
228 func New() *Viper { |
227 v := new(Viper) |
229 v := new(Viper) |
228 v.keyDelim = "." |
230 v.keyDelim = "." |
229 v.configName = "config" |
231 v.configName = "config" |
230 v.configPermissions = os.FileMode(0644) |
232 v.configPermissions = os.FileMode(0o644) |
231 v.fs = afero.NewOsFs() |
233 v.fs = afero.NewOsFs() |
232 v.config = make(map[string]interface{}) |
234 v.config = make(map[string]interface{}) |
233 v.override = make(map[string]interface{}) |
235 v.override = make(map[string]interface{}) |
234 v.defaults = make(map[string]interface{}) |
236 v.defaults = make(map[string]interface{}) |
235 v.kvstore = make(map[string]interface{}) |
237 v.kvstore = make(map[string]interface{}) |
236 v.pflags = make(map[string]FlagValue) |
238 v.pflags = make(map[string]FlagValue) |
237 v.env = make(map[string][]string) |
239 v.env = make(map[string][]string) |
238 v.aliases = make(map[string]string) |
240 v.aliases = make(map[string]string) |
239 v.typeByDefValue = false |
241 v.typeByDefValue = false |
|
242 v.logger = jwwLogger{} |
|
243 |
|
244 v.resetEncoding() |
240 |
245 |
241 return v |
246 return v |
242 } |
247 } |
243 |
248 |
244 // Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney. |
249 // Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney. |
282 |
287 |
283 for _, opt := range opts { |
288 for _, opt := range opts { |
284 opt.apply(v) |
289 opt.apply(v) |
285 } |
290 } |
286 |
291 |
|
292 v.resetEncoding() |
|
293 |
287 return v |
294 return v |
288 } |
295 } |
289 |
296 |
290 // Reset is intended for testing, will reset all to default settings. |
297 // Reset is intended for testing, will reset all to default settings. |
291 // In the public interface for the viper package so applications |
298 // In the public interface for the viper package so applications |
292 // can use it in their testing as well. |
299 // can use it in their testing as well. |
293 func Reset() { |
300 func Reset() { |
294 v = New() |
301 v = New() |
295 SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} |
302 SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} |
296 SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} |
303 SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} |
|
304 } |
|
305 |
|
306 // TODO: make this lazy initialization instead |
|
307 func (v *Viper) resetEncoding() { |
|
308 encoderRegistry := encoding.NewEncoderRegistry() |
|
309 decoderRegistry := encoding.NewDecoderRegistry() |
|
310 |
|
311 { |
|
312 codec := yaml.Codec{} |
|
313 |
|
314 encoderRegistry.RegisterEncoder("yaml", codec) |
|
315 decoderRegistry.RegisterDecoder("yaml", codec) |
|
316 |
|
317 encoderRegistry.RegisterEncoder("yml", codec) |
|
318 decoderRegistry.RegisterDecoder("yml", codec) |
|
319 } |
|
320 |
|
321 { |
|
322 codec := json.Codec{} |
|
323 |
|
324 encoderRegistry.RegisterEncoder("json", codec) |
|
325 decoderRegistry.RegisterDecoder("json", codec) |
|
326 } |
|
327 |
|
328 { |
|
329 codec := toml.Codec{} |
|
330 |
|
331 encoderRegistry.RegisterEncoder("toml", codec) |
|
332 decoderRegistry.RegisterDecoder("toml", codec) |
|
333 } |
|
334 |
|
335 { |
|
336 codec := hcl.Codec{} |
|
337 |
|
338 encoderRegistry.RegisterEncoder("hcl", codec) |
|
339 decoderRegistry.RegisterDecoder("hcl", codec) |
|
340 |
|
341 encoderRegistry.RegisterEncoder("tfvars", codec) |
|
342 decoderRegistry.RegisterDecoder("tfvars", codec) |
|
343 } |
|
344 |
|
345 { |
|
346 codec := ini.Codec{ |
|
347 KeyDelimiter: v.keyDelim, |
|
348 LoadOptions: v.iniLoadOptions, |
|
349 } |
|
350 |
|
351 encoderRegistry.RegisterEncoder("ini", codec) |
|
352 decoderRegistry.RegisterDecoder("ini", codec) |
|
353 } |
|
354 |
|
355 { |
|
356 codec := &javaproperties.Codec{ |
|
357 KeyDelimiter: v.keyDelim, |
|
358 } |
|
359 |
|
360 encoderRegistry.RegisterEncoder("properties", codec) |
|
361 decoderRegistry.RegisterDecoder("properties", codec) |
|
362 |
|
363 encoderRegistry.RegisterEncoder("props", codec) |
|
364 decoderRegistry.RegisterDecoder("props", codec) |
|
365 |
|
366 encoderRegistry.RegisterEncoder("prop", codec) |
|
367 decoderRegistry.RegisterDecoder("prop", codec) |
|
368 } |
|
369 |
|
370 { |
|
371 codec := &dotenv.Codec{} |
|
372 |
|
373 encoderRegistry.RegisterEncoder("dotenv", codec) |
|
374 decoderRegistry.RegisterDecoder("dotenv", codec) |
|
375 |
|
376 encoderRegistry.RegisterEncoder("env", codec) |
|
377 decoderRegistry.RegisterDecoder("env", codec) |
|
378 } |
|
379 |
|
380 v.encoderRegistry = encoderRegistry |
|
381 v.decoderRegistry = decoderRegistry |
297 } |
382 } |
298 |
383 |
299 type defaultRemoteProvider struct { |
384 type defaultRemoteProvider struct { |
300 provider string |
385 provider string |
301 endpoint string |
386 endpoint string |
329 Path() string |
414 Path() string |
330 SecretKeyring() string |
415 SecretKeyring() string |
331 } |
416 } |
332 |
417 |
333 // SupportedExts are universally supported extensions. |
418 // SupportedExts are universally supported extensions. |
334 var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} |
419 var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} |
335 |
420 |
336 // SupportedRemoteProviders are universally supported remote providers. |
421 // SupportedRemoteProviders are universally supported remote providers. |
337 var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} |
422 var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} |
338 |
423 |
339 func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } |
424 func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } |
475 // Can be called multiple times to define multiple search paths. |
560 // Can be called multiple times to define multiple search paths. |
476 func AddConfigPath(in string) { v.AddConfigPath(in) } |
561 func AddConfigPath(in string) { v.AddConfigPath(in) } |
477 |
562 |
478 func (v *Viper) AddConfigPath(in string) { |
563 func (v *Viper) AddConfigPath(in string) { |
479 if in != "" { |
564 if in != "" { |
480 absin := absPathify(in) |
565 absin := absPathify(v.logger, in) |
481 jww.INFO.Println("adding", absin, "to paths to search") |
566 |
|
567 v.logger.Info("adding path to search paths", "path", absin) |
482 if !stringInSlice(absin, v.configPaths) { |
568 if !stringInSlice(absin, v.configPaths) { |
483 v.configPaths = append(v.configPaths, absin) |
569 v.configPaths = append(v.configPaths, absin) |
484 } |
570 } |
485 } |
571 } |
486 } |
572 } |
500 func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { |
586 func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { |
501 if !stringInSlice(provider, SupportedRemoteProviders) { |
587 if !stringInSlice(provider, SupportedRemoteProviders) { |
502 return UnsupportedRemoteProviderError(provider) |
588 return UnsupportedRemoteProviderError(provider) |
503 } |
589 } |
504 if provider != "" && endpoint != "" { |
590 if provider != "" && endpoint != "" { |
505 jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) |
591 v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) |
|
592 |
506 rp := &defaultRemoteProvider{ |
593 rp := &defaultRemoteProvider{ |
507 endpoint: endpoint, |
594 endpoint: endpoint, |
508 provider: provider, |
595 provider: provider, |
509 path: path, |
596 path: path, |
510 } |
597 } |
532 func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { |
619 func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { |
533 if !stringInSlice(provider, SupportedRemoteProviders) { |
620 if !stringInSlice(provider, SupportedRemoteProviders) { |
534 return UnsupportedRemoteProviderError(provider) |
621 return UnsupportedRemoteProviderError(provider) |
535 } |
622 } |
536 if provider != "" && endpoint != "" { |
623 if provider != "" && endpoint != "" { |
537 jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint) |
624 v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) |
|
625 |
538 rp := &defaultRemoteProvider{ |
626 rp := &defaultRemoteProvider{ |
539 endpoint: endpoint, |
627 endpoint: endpoint, |
540 provider: provider, |
628 provider: provider, |
541 path: path, |
629 path: path, |
542 secretKeyring: secretkeyring, |
630 secretKeyring: secretkeyring, |
1107 } |
1195 } |
1108 |
1196 |
1109 return nil |
1197 return nil |
1110 } |
1198 } |
1111 |
1199 |
|
1200 // MustBindEnv wraps BindEnv in a panic. |
|
1201 // If there is an error binding an environment variable, MustBindEnv will |
|
1202 // panic. |
|
1203 func MustBindEnv(input ...string) { v.MustBindEnv(input...) } |
|
1204 |
|
1205 func (v *Viper) MustBindEnv(input ...string) { |
|
1206 if err := v.BindEnv(input...); err != nil { |
|
1207 panic(fmt.Sprintf("error while binding environment variable: %v", err)) |
|
1208 } |
|
1209 } |
|
1210 |
1112 // Given a key, find the value. |
1211 // Given a key, find the value. |
1113 // |
1212 // |
1114 // Viper will check to see if an alias exists first. |
1213 // Viper will check to see if an alias exists first. |
1115 // Viper will then check in the following order: |
1214 // Viper will then check in the following order: |
1116 // flag, env, config file, key/value store. |
1215 // flag, env, config file, key/value store. |
1348 v.override[key] = val |
1447 v.override[key] = val |
1349 } |
1448 } |
1350 v.aliases[alias] = key |
1449 v.aliases[alias] = key |
1351 } |
1450 } |
1352 } else { |
1451 } else { |
1353 jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key)) |
1452 v.logger.Warn("creating circular reference alias", "alias", alias, "key", key, "real_key", v.realKey(key)) |
1354 } |
1453 } |
1355 } |
1454 } |
1356 |
1455 |
1357 func (v *Viper) realKey(key string) string { |
1456 func (v *Viper) realKey(key string) string { |
1358 newkey, exists := v.aliases[key] |
1457 newkey, exists := v.aliases[key] |
1359 if exists { |
1458 if exists { |
1360 jww.DEBUG.Println("Alias", key, "to", newkey) |
1459 v.logger.Debug("key is an alias", "alias", key, "to", newkey) |
|
1460 |
1361 return v.realKey(newkey) |
1461 return v.realKey(newkey) |
1362 } |
1462 } |
1363 return key |
1463 return key |
1364 } |
1464 } |
1365 |
1465 |
1366 // InConfig checks to see if the given key (or an alias) is in the config file. |
1466 // InConfig checks to see if the given key (or an alias) is in the config file. |
1367 func InConfig(key string) bool { return v.InConfig(key) } |
1467 func InConfig(key string) bool { return v.InConfig(key) } |
1368 |
1468 |
1369 func (v *Viper) InConfig(key string) bool { |
1469 func (v *Viper) InConfig(key string) bool { |
|
1470 lcaseKey := strings.ToLower(key) |
|
1471 |
1370 // if the requested key is an alias, then return the proper key |
1472 // if the requested key is an alias, then return the proper key |
1371 key = v.realKey(key) |
1473 lcaseKey = v.realKey(lcaseKey) |
1372 |
1474 path := strings.Split(lcaseKey, v.keyDelim) |
1373 _, exists := v.config[key] |
1475 |
1374 return exists |
1476 return v.searchIndexableWithPathPrefixes(v.config, path) != nil |
1375 } |
1477 } |
1376 |
1478 |
1377 // SetDefault sets the default value for this key. |
1479 // SetDefault sets the default value for this key. |
1378 // SetDefault is case-insensitive for a key. |
1480 // SetDefault is case-insensitive for a key. |
1379 // Default only used when no value is provided by the user via flag, config or ENV. |
1481 // Default only used when no value is provided by the user via flag, config or ENV. |
1414 // ReadInConfig will discover and load the configuration file from disk |
1516 // ReadInConfig will discover and load the configuration file from disk |
1415 // and key/value stores, searching in one of the defined paths. |
1517 // and key/value stores, searching in one of the defined paths. |
1416 func ReadInConfig() error { return v.ReadInConfig() } |
1518 func ReadInConfig() error { return v.ReadInConfig() } |
1417 |
1519 |
1418 func (v *Viper) ReadInConfig() error { |
1520 func (v *Viper) ReadInConfig() error { |
1419 jww.INFO.Println("Attempting to read in config file") |
1521 v.logger.Info("attempting to read in config file") |
1420 filename, err := v.getConfigFile() |
1522 filename, err := v.getConfigFile() |
1421 if err != nil { |
1523 if err != nil { |
1422 return err |
1524 return err |
1423 } |
1525 } |
1424 |
1526 |
1425 if !stringInSlice(v.getConfigType(), SupportedExts) { |
1527 if !stringInSlice(v.getConfigType(), SupportedExts) { |
1426 return UnsupportedConfigError(v.getConfigType()) |
1528 return UnsupportedConfigError(v.getConfigType()) |
1427 } |
1529 } |
1428 |
1530 |
1429 jww.DEBUG.Println("Reading file: ", filename) |
1531 v.logger.Debug("reading file", "file", filename) |
1430 file, err := afero.ReadFile(v.fs, filename) |
1532 file, err := afero.ReadFile(v.fs, filename) |
1431 if err != nil { |
1533 if err != nil { |
1432 return err |
1534 return err |
1433 } |
1535 } |
1434 |
1536 |
1536 } |
1638 } |
1537 return v.writeConfig(filename, false) |
1639 return v.writeConfig(filename, false) |
1538 } |
1640 } |
1539 |
1641 |
1540 func (v *Viper) writeConfig(filename string, force bool) error { |
1642 func (v *Viper) writeConfig(filename string, force bool) error { |
1541 jww.INFO.Println("Attempting to write configuration to file.") |
1643 v.logger.Info("attempting to write configuration to file") |
|
1644 |
1542 var configType string |
1645 var configType string |
1543 |
1646 |
1544 ext := filepath.Ext(filename) |
1647 ext := filepath.Ext(filename) |
1545 if ext != "" { |
1648 if ext != "" && ext != filepath.Base(filename) { |
1546 configType = ext[1:] |
1649 configType = ext[1:] |
1547 } else { |
1650 } else { |
1548 configType = v.configType |
1651 configType = v.configType |
1549 } |
1652 } |
1550 if configType == "" { |
1653 if configType == "" { |
1582 |
1685 |
1583 func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { |
1686 func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { |
1584 buf := new(bytes.Buffer) |
1687 buf := new(bytes.Buffer) |
1585 buf.ReadFrom(in) |
1688 buf.ReadFrom(in) |
1586 |
1689 |
1587 switch strings.ToLower(v.getConfigType()) { |
1690 switch format := strings.ToLower(v.getConfigType()); format { |
1588 case "yaml", "yml": |
1691 case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env": |
1589 if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil { |
1692 err := v.decoderRegistry.Decode(format, buf.Bytes(), c) |
1590 return ConfigParseError{err} |
|
1591 } |
|
1592 |
|
1593 case "json": |
|
1594 if err := json.Unmarshal(buf.Bytes(), &c); err != nil { |
|
1595 return ConfigParseError{err} |
|
1596 } |
|
1597 |
|
1598 case "hcl": |
|
1599 obj, err := hcl.Parse(buf.String()) |
|
1600 if err != nil { |
1693 if err != nil { |
1601 return ConfigParseError{err} |
1694 return ConfigParseError{err} |
1602 } |
|
1603 if err = hcl.DecodeObject(&c, obj); err != nil { |
|
1604 return ConfigParseError{err} |
|
1605 } |
|
1606 |
|
1607 case "toml": |
|
1608 tree, err := toml.LoadReader(buf) |
|
1609 if err != nil { |
|
1610 return ConfigParseError{err} |
|
1611 } |
|
1612 tmap := tree.ToMap() |
|
1613 for k, v := range tmap { |
|
1614 c[k] = v |
|
1615 } |
|
1616 |
|
1617 case "dotenv", "env": |
|
1618 env, err := gotenv.StrictParse(buf) |
|
1619 if err != nil { |
|
1620 return ConfigParseError{err} |
|
1621 } |
|
1622 for k, v := range env { |
|
1623 c[k] = v |
|
1624 } |
|
1625 |
|
1626 case "properties", "props", "prop": |
|
1627 v.properties = properties.NewProperties() |
|
1628 var err error |
|
1629 if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil { |
|
1630 return ConfigParseError{err} |
|
1631 } |
|
1632 for _, key := range v.properties.Keys() { |
|
1633 value, _ := v.properties.Get(key) |
|
1634 // recursively build nested maps |
|
1635 path := strings.Split(key, ".") |
|
1636 lastKey := strings.ToLower(path[len(path)-1]) |
|
1637 deepestMap := deepSearch(c, path[0:len(path)-1]) |
|
1638 // set innermost value |
|
1639 deepestMap[lastKey] = value |
|
1640 } |
|
1641 |
|
1642 case "ini": |
|
1643 cfg := ini.Empty(v.iniLoadOptions) |
|
1644 err := cfg.Append(buf.Bytes()) |
|
1645 if err != nil { |
|
1646 return ConfigParseError{err} |
|
1647 } |
|
1648 sections := cfg.Sections() |
|
1649 for i := 0; i < len(sections); i++ { |
|
1650 section := sections[i] |
|
1651 keys := section.Keys() |
|
1652 for j := 0; j < len(keys); j++ { |
|
1653 key := keys[j] |
|
1654 value := cfg.Section(section.Name()).Key(key.Name()).String() |
|
1655 c[section.Name()+"."+key.Name()] = value |
|
1656 } |
|
1657 } |
1695 } |
1658 } |
1696 } |
1659 |
1697 |
1660 insensitiviseMap(c) |
1698 insensitiviseMap(c) |
1661 return nil |
1699 return nil |
1663 |
1701 |
1664 // Marshal a map into Writer. |
1702 // Marshal a map into Writer. |
1665 func (v *Viper) marshalWriter(f afero.File, configType string) error { |
1703 func (v *Viper) marshalWriter(f afero.File, configType string) error { |
1666 c := v.AllSettings() |
1704 c := v.AllSettings() |
1667 switch configType { |
1705 switch configType { |
1668 case "json": |
1706 case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "prop", "props", "properties", "dotenv", "env": |
1669 b, err := json.MarshalIndent(c, "", " ") |
1707 b, err := v.encoderRegistry.Encode(configType, c) |
1670 if err != nil { |
1708 if err != nil { |
1671 return ConfigMarshalError{err} |
1709 return ConfigMarshalError{err} |
1672 } |
1710 } |
|
1711 |
1673 _, err = f.WriteString(string(b)) |
1712 _, err = f.WriteString(string(b)) |
1674 if err != nil { |
1713 if err != nil { |
1675 return ConfigMarshalError{err} |
1714 return ConfigMarshalError{err} |
1676 } |
1715 } |
1677 |
|
1678 case "hcl": |
|
1679 b, err := json.Marshal(c) |
|
1680 if err != nil { |
|
1681 return ConfigMarshalError{err} |
|
1682 } |
|
1683 ast, err := hcl.Parse(string(b)) |
|
1684 if err != nil { |
|
1685 return ConfigMarshalError{err} |
|
1686 } |
|
1687 err = printer.Fprint(f, ast.Node) |
|
1688 if err != nil { |
|
1689 return ConfigMarshalError{err} |
|
1690 } |
|
1691 |
|
1692 case "prop", "props", "properties": |
|
1693 if v.properties == nil { |
|
1694 v.properties = properties.NewProperties() |
|
1695 } |
|
1696 p := v.properties |
|
1697 for _, key := range v.AllKeys() { |
|
1698 _, _, err := p.Set(key, v.GetString(key)) |
|
1699 if err != nil { |
|
1700 return ConfigMarshalError{err} |
|
1701 } |
|
1702 } |
|
1703 _, err := p.WriteComment(f, "#", properties.UTF8) |
|
1704 if err != nil { |
|
1705 return ConfigMarshalError{err} |
|
1706 } |
|
1707 |
|
1708 case "dotenv", "env": |
|
1709 lines := []string{} |
|
1710 for _, key := range v.AllKeys() { |
|
1711 envName := strings.ToUpper(strings.Replace(key, ".", "_", -1)) |
|
1712 val := v.Get(key) |
|
1713 lines = append(lines, fmt.Sprintf("%v=%v", envName, val)) |
|
1714 } |
|
1715 s := strings.Join(lines, "\n") |
|
1716 if _, err := f.WriteString(s); err != nil { |
|
1717 return ConfigMarshalError{err} |
|
1718 } |
|
1719 |
|
1720 case "toml": |
|
1721 t, err := toml.TreeFromMap(c) |
|
1722 if err != nil { |
|
1723 return ConfigMarshalError{err} |
|
1724 } |
|
1725 s := t.String() |
|
1726 if _, err := f.WriteString(s); err != nil { |
|
1727 return ConfigMarshalError{err} |
|
1728 } |
|
1729 |
|
1730 case "yaml", "yml": |
|
1731 b, err := yaml.Marshal(c) |
|
1732 if err != nil { |
|
1733 return ConfigMarshalError{err} |
|
1734 } |
|
1735 if _, err = f.WriteString(string(b)); err != nil { |
|
1736 return ConfigMarshalError{err} |
|
1737 } |
|
1738 |
|
1739 case "ini": |
|
1740 keys := v.AllKeys() |
|
1741 cfg := ini.Empty() |
|
1742 ini.PrettyFormat = false |
|
1743 for i := 0; i < len(keys); i++ { |
|
1744 key := keys[i] |
|
1745 lastSep := strings.LastIndex(key, ".") |
|
1746 sectionName := key[:(lastSep)] |
|
1747 keyName := key[(lastSep + 1):] |
|
1748 if sectionName == "default" { |
|
1749 sectionName = "" |
|
1750 } |
|
1751 cfg.Section(sectionName).Key(keyName).SetValue(v.GetString(key)) |
|
1752 } |
|
1753 cfg.WriteTo(f) |
|
1754 } |
1716 } |
1755 return nil |
1717 return nil |
1756 } |
1718 } |
1757 |
1719 |
1758 func keyExists(k string, m map[string]interface{}) string { |
1720 func keyExists(k string, m map[string]interface{}) string { |
1803 // insistence on parsing nested structures as `map[interface{}]interface{}` |
1766 // insistence on parsing nested structures as `map[interface{}]interface{}` |
1804 // instead of using a `string` as the key for nest structures beyond one level |
1767 // instead of using a `string` as the key for nest structures beyond one level |
1805 // deep. Both map types are supported as there is a go-yaml fork that uses |
1768 // deep. Both map types are supported as there is a go-yaml fork that uses |
1806 // `map[string]interface{}` instead. |
1769 // `map[string]interface{}` instead. |
1807 func mergeMaps( |
1770 func mergeMaps( |
1808 src, tgt map[string]interface{}, itgt map[interface{}]interface{}) { |
1771 src, tgt map[string]interface{}, itgt map[interface{}]interface{}, |
|
1772 ) { |
1809 for sk, sv := range src { |
1773 for sk, sv := range src { |
1810 tk := keyExists(sk, tgt) |
1774 tk := keyExists(sk, tgt) |
1811 if tk == "" { |
1775 if tk == "" { |
1812 jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv) |
1776 v.logger.Trace("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv) |
1813 tgt[sk] = sv |
1777 tgt[sk] = sv |
1814 if itgt != nil { |
1778 if itgt != nil { |
1815 itgt[sk] = sv |
1779 itgt[sk] = sv |
1816 } |
1780 } |
1817 continue |
1781 continue |
1818 } |
1782 } |
1819 |
1783 |
1820 tv, ok := tgt[tk] |
1784 tv, ok := tgt[tk] |
1821 if !ok { |
1785 if !ok { |
1822 jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv) |
1786 v.logger.Trace("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv) |
1823 tgt[sk] = sv |
1787 tgt[sk] = sv |
1824 if itgt != nil { |
1788 if itgt != nil { |
1825 itgt[sk] = sv |
1789 itgt[sk] = sv |
1826 } |
1790 } |
1827 continue |
1791 continue |
1828 } |
1792 } |
1829 |
1793 |
1830 svType := reflect.TypeOf(sv) |
1794 svType := reflect.TypeOf(sv) |
1831 tvType := reflect.TypeOf(tv) |
1795 tvType := reflect.TypeOf(tv) |
1832 if tvType != nil && svType != tvType { // Allow for the target to be nil |
1796 |
1833 jww.ERROR.Printf( |
1797 v.logger.Trace( |
1834 "svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v", |
1798 "processing", |
1835 sk, svType, tvType, sv, tv) |
1799 "key", sk, |
1836 continue |
1800 "st", svType, |
1837 } |
1801 "tt", tvType, |
1838 |
1802 "sv", sv, |
1839 jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v", |
1803 "tv", tv, |
1840 sk, svType, tvType, sv, tv) |
1804 ) |
1841 |
1805 |
1842 switch ttv := tv.(type) { |
1806 switch ttv := tv.(type) { |
1843 case map[interface{}]interface{}: |
1807 case map[interface{}]interface{}: |
1844 jww.TRACE.Printf("merging maps (must convert)") |
1808 v.logger.Trace("merging maps (must convert)") |
1845 tsv := sv.(map[interface{}]interface{}) |
1809 tsv, ok := sv.(map[interface{}]interface{}) |
|
1810 if !ok { |
|
1811 v.logger.Error( |
|
1812 "Could not cast sv to map[interface{}]interface{}", |
|
1813 "key", sk, |
|
1814 "st", svType, |
|
1815 "tt", tvType, |
|
1816 "sv", sv, |
|
1817 "tv", tv, |
|
1818 ) |
|
1819 continue |
|
1820 } |
|
1821 |
1846 ssv := castToMapStringInterface(tsv) |
1822 ssv := castToMapStringInterface(tsv) |
1847 stv := castToMapStringInterface(ttv) |
1823 stv := castToMapStringInterface(ttv) |
1848 mergeMaps(ssv, stv, ttv) |
1824 mergeMaps(ssv, stv, ttv) |
1849 case map[string]interface{}: |
1825 case map[string]interface{}: |
1850 jww.TRACE.Printf("merging maps") |
1826 v.logger.Trace("merging maps") |
1851 mergeMaps(sv.(map[string]interface{}), ttv, nil) |
1827 tsv, ok := sv.(map[string]interface{}) |
|
1828 if !ok { |
|
1829 v.logger.Error( |
|
1830 "Could not cast sv to map[string]interface{}", |
|
1831 "key", sk, |
|
1832 "st", svType, |
|
1833 "tt", tvType, |
|
1834 "sv", sv, |
|
1835 "tv", tv, |
|
1836 ) |
|
1837 continue |
|
1838 } |
|
1839 mergeMaps(tsv, ttv, nil) |
1852 default: |
1840 default: |
1853 jww.TRACE.Printf("setting value") |
1841 v.logger.Trace("setting value") |
1854 tgt[tk] = sv |
1842 tgt[tk] = sv |
1855 if itgt != nil { |
1843 if itgt != nil { |
1856 itgt[tk] = sv |
1844 itgt[tk] = sv |
1857 } |
1845 } |
1858 } |
1846 } |
2119 v.configFile = cf |
2107 v.configFile = cf |
2120 } |
2108 } |
2121 return v.configFile, nil |
2109 return v.configFile, nil |
2122 } |
2110 } |
2123 |
2111 |
2124 func (v *Viper) searchInPath(in string) (filename string) { |
|
2125 jww.DEBUG.Println("Searching for config in ", in) |
|
2126 for _, ext := range SupportedExts { |
|
2127 jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) |
|
2128 if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { |
|
2129 jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) |
|
2130 return filepath.Join(in, v.configName+"."+ext) |
|
2131 } |
|
2132 } |
|
2133 |
|
2134 if v.configType != "" { |
|
2135 if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b { |
|
2136 return filepath.Join(in, v.configName) |
|
2137 } |
|
2138 } |
|
2139 |
|
2140 return "" |
|
2141 } |
|
2142 |
|
2143 // Search all configPaths for any config file. |
|
2144 // Returns the first path that exists (and is a config file). |
|
2145 func (v *Viper) findConfigFile() (string, error) { |
|
2146 jww.INFO.Println("Searching for config in ", v.configPaths) |
|
2147 |
|
2148 for _, cp := range v.configPaths { |
|
2149 file := v.searchInPath(cp) |
|
2150 if file != "" { |
|
2151 return file, nil |
|
2152 } |
|
2153 } |
|
2154 return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} |
|
2155 } |
|
2156 |
|
2157 // Debug prints all configuration registries for debugging |
2112 // Debug prints all configuration registries for debugging |
2158 // purposes. |
2113 // purposes. |
2159 func Debug() { v.Debug() } |
2114 func Debug() { v.Debug() } |
2160 |
2115 |
2161 func (v *Viper) Debug() { |
2116 func (v *Viper) Debug() { |