mirror of
https://github.com/spf13/viper.git
synced 2024-04-21 12:31:38 +00:00
Compare commits
@@ -11,7 +11,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check minimum labels
|
||||
uses: mheap/github-action-required-labels@v1
|
||||
uses: mheap/github-action-required-labels@v2
|
||||
with:
|
||||
mode: minimum
|
||||
count: 1
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
go: ['1.15', '1.16', '1.17', '1.18']
|
||||
go: ['1.15', '1.16', '1.17', '1.18', '1.19']
|
||||
tags: ['', 'viper_yaml2', 'viper_toml1']
|
||||
env:
|
||||
GOFLAGS: -mod=readonly
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.19
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
@@ -56,4 +56,4 @@ jobs:
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.45.2
|
||||
version: v1.49
|
||||
|
||||
@@ -16,7 +16,7 @@ endif
|
||||
|
||||
# Dependency versions
|
||||
GOTESTSUM_VERSION = 1.8.0
|
||||
GOLANGCI_VERSION = 1.45.2
|
||||
GOLANGCI_VERSION = 1.49.0
|
||||
|
||||
# Add the ability to override some variables
|
||||
# Use with care
|
||||
@@ -48,7 +48,7 @@ bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION}
|
||||
@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
|
||||
bin/golangci-lint-${GOLANGCI_VERSION}:
|
||||
@mkdir -p bin
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
|
||||
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
|
||||
@mv bin/golangci-lint "$@"
|
||||
|
||||
.PHONY: lint
|
||||
|
||||
@@ -119,7 +119,7 @@ viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search
|
||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||
err := viper.ReadInConfig() // Find and read the config file
|
||||
if err != nil { // Handle errors reading the config file
|
||||
panic(fmt.Errorf("Fatal error config file: %w \n", err))
|
||||
panic(fmt.Errorf("fatal error config file: %w", err))
|
||||
}
|
||||
```
|
||||
|
||||
@@ -447,6 +447,13 @@ viper.SetConfigType("json") // because there is no file extension in a stream of
|
||||
err := viper.ReadRemoteConfig()
|
||||
```
|
||||
|
||||
#### etcd3
|
||||
```go
|
||||
viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json")
|
||||
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
|
||||
err := viper.ReadRemoteConfig()
|
||||
```
|
||||
|
||||
#### Consul
|
||||
You need to set a key to Consul key/value storage with JSON value containing your desired config.
|
||||
For example, create a Consul key/value store key `MY_CONSUL_KEY` with value:
|
||||
@@ -594,7 +601,7 @@ configuration level.
|
||||
|
||||
Viper can access array indices by using numbers in the path. For example:
|
||||
|
||||
```json
|
||||
```jsonc
|
||||
{
|
||||
"host": {
|
||||
"address": "localhost",
|
||||
@@ -622,7 +629,7 @@ GetInt("host.ports.1") // returns 6029
|
||||
Lastly, if there exists a key that matches the delimited key path, its value
|
||||
will be returned instead. E.g.
|
||||
|
||||
```json
|
||||
```jsonc
|
||||
{
|
||||
"datastore.metric.host": "0.0.0.0",
|
||||
"host": {
|
||||
|
||||
@@ -8,17 +8,17 @@ require (
|
||||
github.com/magiconair/properties v1.8.6
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pelletier/go-toml/v2 v2.0.1
|
||||
github.com/pelletier/go-toml/v2 v2.0.5
|
||||
github.com/sagikazarmark/crypt v0.6.0
|
||||
github.com/spf13/afero v1.8.2
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/subosito/gotenv v1.3.0
|
||||
gopkg.in/ini.v1 v1.66.4
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/subosito/gotenv v1.4.1
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -335,8 +335,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -386,18 +386,21 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
|
||||
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
|
||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -890,8 +893,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
|
||||
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -903,8 +906,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -20,7 +20,6 @@ const encoded = `key=value
|
||||
|
||||
[map]
|
||||
key=value
|
||||
|
||||
`
|
||||
|
||||
// decoded form of the data
|
||||
|
||||
@@ -31,6 +31,7 @@ list = [
|
||||
// encoded form of the data
|
||||
const encoded = `key = 'value'
|
||||
list = ['item1', 'item2', 'item3']
|
||||
|
||||
[map]
|
||||
key = 'value'
|
||||
|
||||
@@ -38,8 +39,6 @@ key = 'value'
|
||||
[nested_map.map]
|
||||
key = 'value'
|
||||
list = ['item1', 'item2', 'item3']
|
||||
|
||||
|
||||
`
|
||||
|
||||
// Viper's internal representation
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
)
|
||||
|
||||
// Logger is a unified interface for various logging use cases and practices, including:
|
||||
// - leveled logging
|
||||
// - structured logging
|
||||
// - leveled logging
|
||||
// - structured logging
|
||||
type Logger interface {
|
||||
// Trace logs a Trace event.
|
||||
//
|
||||
|
||||
@@ -64,18 +64,25 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
||||
return nm
|
||||
}
|
||||
|
||||
func insensitiviseVal(val interface{}) interface{} {
|
||||
switch val.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
// nested map: cast and recursively insensitivise
|
||||
val = cast.ToStringMap(val)
|
||||
insensitiviseMap(val.(map[string]interface{}))
|
||||
case map[string]interface{}:
|
||||
// nested map: recursively insensitivise
|
||||
insensitiviseMap(val.(map[string]interface{}))
|
||||
case []interface{}:
|
||||
// nested array: recursively insensitivise
|
||||
insensitiveArray(val.([]interface{}))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func insensitiviseMap(m map[string]interface{}) {
|
||||
for key, val := range m {
|
||||
switch val.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
// nested map: cast and recursively insensitivise
|
||||
val = cast.ToStringMap(val)
|
||||
insensitiviseMap(val.(map[string]interface{}))
|
||||
case map[string]interface{}:
|
||||
// nested map: recursively insensitivise
|
||||
insensitiviseMap(val.(map[string]interface{}))
|
||||
}
|
||||
|
||||
val = insensitiviseVal(val)
|
||||
lower := strings.ToLower(key)
|
||||
if key != lower {
|
||||
// remove old key (not lower-cased)
|
||||
@@ -86,6 +93,12 @@ func insensitiviseMap(m map[string]interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func insensitiveArray(a []interface{}) {
|
||||
for i, val := range a {
|
||||
a[i] = insensitiviseVal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func absPathify(logger Logger, inPath string) string {
|
||||
logger.Info("trying to resolve absolute path", "path", inPath)
|
||||
|
||||
|
||||
@@ -132,10 +132,10 @@ type DecoderConfigOption func(*mapstructure.DecoderConfig)
|
||||
// DecodeHook returns a DecoderConfigOption which overrides the default
|
||||
// DecoderConfig.DecodeHook value, the default is:
|
||||
//
|
||||
// mapstructure.ComposeDecodeHookFunc(
|
||||
// mapstructure.StringToTimeDurationHookFunc(),
|
||||
// mapstructure.StringToSliceHookFunc(","),
|
||||
// )
|
||||
// mapstructure.ComposeDecodeHookFunc(
|
||||
// mapstructure.StringToTimeDurationHookFunc(),
|
||||
// mapstructure.StringToSliceHookFunc(","),
|
||||
// )
|
||||
func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
|
||||
return func(c *mapstructure.DecoderConfig) {
|
||||
c.DecodeHook = hook
|
||||
@@ -156,18 +156,18 @@ func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
|
||||
//
|
||||
// For example, if values from the following sources were loaded:
|
||||
//
|
||||
// Defaults : {
|
||||
// "secret": "",
|
||||
// "user": "default",
|
||||
// "endpoint": "https://localhost"
|
||||
// }
|
||||
// Config : {
|
||||
// "user": "root"
|
||||
// "secret": "defaultsecret"
|
||||
// }
|
||||
// Env : {
|
||||
// "secret": "somesecretkey"
|
||||
// }
|
||||
// Defaults : {
|
||||
// "secret": "",
|
||||
// "user": "default",
|
||||
// "endpoint": "https://localhost"
|
||||
// }
|
||||
// Config : {
|
||||
// "user": "root"
|
||||
// "secret": "defaultsecret"
|
||||
// }
|
||||
// Env : {
|
||||
// "secret": "somesecretkey"
|
||||
// }
|
||||
//
|
||||
// The resulting config will have the following values:
|
||||
//
|
||||
@@ -300,7 +300,7 @@ func NewWithOptions(opts ...Option) *Viper {
|
||||
func Reset() {
|
||||
v = New()
|
||||
SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
|
||||
SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
|
||||
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
||||
}
|
||||
|
||||
// TODO: make this lazy initialization instead
|
||||
@@ -419,7 +419,7 @@ type RemoteProvider interface {
|
||||
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
|
||||
|
||||
// SupportedRemoteProviders are universally supported remote providers.
|
||||
var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
|
||||
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
||||
|
||||
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
||||
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
|
||||
@@ -573,7 +573,7 @@ func (v *Viper) AddConfigPath(in string) {
|
||||
|
||||
// AddRemoteProvider adds a remote configuration source.
|
||||
// Remote Providers are searched in the order they are added.
|
||||
// provider is a string value: "etcd", "consul" or "firestore" are currently supported.
|
||||
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
|
||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
||||
// path is the path in the k/v store to retrieve configuration
|
||||
// To retrieve a config file called myapp.json from /configs/myapp.json
|
||||
@@ -604,7 +604,7 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
|
||||
|
||||
// AddSecureRemoteProvider adds a remote configuration source.
|
||||
// Secure Remote Providers are searched in the order they are added.
|
||||
// provider is a string value: "etcd", "consul" or "firestore" are currently supported.
|
||||
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
|
||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
||||
// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
|
||||
// path is the path in the k/v store to retrieve configuration
|
||||
@@ -785,7 +785,8 @@ func (v *Viper) searchMapWithPathPrefixes(
|
||||
// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
|
||||
// on its path in the map.
|
||||
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
//
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
|
||||
var parentVal interface{}
|
||||
for i := 1; i < len(path); i++ {
|
||||
@@ -810,7 +811,8 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
|
||||
// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
|
||||
// in a sub-path of the map.
|
||||
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
//
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
|
||||
// unify input map
|
||||
var m map[string]interface{}
|
||||
@@ -835,7 +837,8 @@ func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
|
||||
// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
|
||||
// in the environment, when automatic env is on.
|
||||
// e.g., if "foo.bar" has a value in the environment, it “shadows”
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
//
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
||||
var parentKey string
|
||||
for i := 1; i < len(path); i++ {
|
||||
@@ -856,11 +859,11 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
||||
// would return a string slice for the key if the key's type is inferred by
|
||||
// the default value and the Get function would return:
|
||||
//
|
||||
// []string {"a", "b", "c"}
|
||||
// []string {"a", "b", "c"}
|
||||
//
|
||||
// Otherwise the Get function would return:
|
||||
//
|
||||
// "a b c"
|
||||
// "a b c"
|
||||
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
|
||||
|
||||
func (v *Viper) SetTypeByDefaultValue(enable bool) {
|
||||
@@ -988,6 +991,13 @@ func (v *Viper) GetUint(key string) uint {
|
||||
return cast.ToUint(v.Get(key))
|
||||
}
|
||||
|
||||
// GetUint16 returns the value associated with the key as an unsigned integer.
|
||||
func GetUint16(key string) uint16 { return v.GetUint16(key) }
|
||||
|
||||
func (v *Viper) GetUint16(key string) uint16 {
|
||||
return cast.ToUint16(v.Get(key))
|
||||
}
|
||||
|
||||
// GetUint32 returns the value associated with the key as an unsigned integer.
|
||||
func GetUint32(key string) uint32 { return v.GetUint32(key) }
|
||||
|
||||
@@ -1137,9 +1147,8 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
|
||||
// BindPFlag binds a specific key to a pflag (as used by cobra).
|
||||
// Example (where serverCmd is a Cobra instance):
|
||||
//
|
||||
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
|
||||
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
|
||||
//
|
||||
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
|
||||
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
|
||||
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
|
||||
|
||||
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
|
||||
@@ -1870,6 +1879,10 @@ func (v *Viper) getKeyValueConfig() error {
|
||||
return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'")
|
||||
}
|
||||
|
||||
if len(v.remoteProviders) == 0 {
|
||||
return RemoteConfigError("No Remote Providers")
|
||||
}
|
||||
|
||||
for _, rp := range v.remoteProviders {
|
||||
val, err := v.getRemoteConfig(rp)
|
||||
if err != nil {
|
||||
@@ -1896,6 +1909,10 @@ func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}
|
||||
|
||||
// Retrieve the first found remote configuration.
|
||||
func (v *Viper) watchKeyValueConfigOnChannel() error {
|
||||
if len(v.remoteProviders) == 0 {
|
||||
return RemoteConfigError("No Remote Providers")
|
||||
}
|
||||
|
||||
for _, rp := range v.remoteProviders {
|
||||
respc, _ := RemoteConfig.WatchChannel(rp)
|
||||
// Todo: Add quit channel
|
||||
@@ -1913,9 +1930,15 @@ func (v *Viper) watchKeyValueConfigOnChannel() error {
|
||||
|
||||
// Retrieve the first found remote configuration.
|
||||
func (v *Viper) watchKeyValueConfig() error {
|
||||
if len(v.remoteProviders) == 0 {
|
||||
return RemoteConfigError("No Remote Providers")
|
||||
}
|
||||
|
||||
for _, rp := range v.remoteProviders {
|
||||
val, err := v.watchRemoteConfig(rp)
|
||||
if err != nil {
|
||||
v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error())
|
||||
|
||||
continue
|
||||
}
|
||||
v.kvstore = val
|
||||
@@ -1958,9 +1981,10 @@ func (v *Viper) AllKeys() []string {
|
||||
|
||||
// flattenAndMergeMap recursively flattens the given map into a map[string]bool
|
||||
// of key paths (used as a set, easier to manipulate than a []string):
|
||||
// - each path is merged into a single key string, delimited with v.keyDelim
|
||||
// - if a path is shadowed by an earlier value in the initial shadow map,
|
||||
// it is skipped.
|
||||
// - each path is merged into a single key string, delimited with v.keyDelim
|
||||
// - if a path is shadowed by an earlier value in the initial shadow map,
|
||||
// it is skipped.
|
||||
//
|
||||
// The resulting set of paths is merged to the given shadow set at the same time.
|
||||
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
|
||||
if shadow != nil && prefix != "" && shadow[prefix] {
|
||||
@@ -2111,14 +2135,17 @@ func (v *Viper) getConfigFile() (string, error) {
|
||||
|
||||
// Debug prints all configuration registries for debugging
|
||||
// purposes.
|
||||
func Debug() { v.Debug() }
|
||||
func Debug() { v.Debug() }
|
||||
func DebugTo(w io.Writer) { v.DebugTo(w) }
|
||||
|
||||
func (v *Viper) Debug() {
|
||||
fmt.Printf("Aliases:\n%#v\n", v.aliases)
|
||||
fmt.Printf("Override:\n%#v\n", v.override)
|
||||
fmt.Printf("PFlags:\n%#v\n", v.pflags)
|
||||
fmt.Printf("Env:\n%#v\n", v.env)
|
||||
fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore)
|
||||
fmt.Printf("Config:\n%#v\n", v.config)
|
||||
fmt.Printf("Defaults:\n%#v\n", v.defaults)
|
||||
func (v *Viper) Debug() { v.DebugTo(os.Stdout) }
|
||||
|
||||
func (v *Viper) DebugTo(w io.Writer) {
|
||||
fmt.Fprintf(w, "Aliases:\n%#v\n", v.aliases)
|
||||
fmt.Fprintf(w, "Override:\n%#v\n", v.override)
|
||||
fmt.Fprintf(w, "PFlags:\n%#v\n", v.pflags)
|
||||
fmt.Fprintf(w, "Env:\n%#v\n", v.env)
|
||||
fmt.Fprintf(w, "Key/Value Store:\n%#v\n", v.kvstore)
|
||||
fmt.Fprintf(w, "Config:\n%#v\n", v.config)
|
||||
fmt.Fprintf(w, "Defaults:\n%#v\n", v.defaults)
|
||||
}
|
||||
|
||||
+17
-6
@@ -9,7 +9,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"io/ioutil" //nolint:staticcheck
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -998,7 +998,7 @@ func TestBindPFlags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: dupl
|
||||
//nolint:dupl
|
||||
func TestBindPFlagsStringSlice(t *testing.T) {
|
||||
tests := []struct {
|
||||
Expected []string
|
||||
@@ -1046,7 +1046,7 @@ func TestBindPFlagsStringSlice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: dupl
|
||||
//nolint:dupl
|
||||
func TestBindPFlagsStringArray(t *testing.T) {
|
||||
tests := []struct {
|
||||
Expected []string
|
||||
@@ -1094,7 +1094,7 @@ func TestBindPFlagsStringArray(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: dupl
|
||||
//nolint:dupl
|
||||
func TestBindPFlagsIntSlice(t *testing.T) {
|
||||
tests := []struct {
|
||||
Expected []int
|
||||
@@ -1953,6 +1953,10 @@ func TestMergeConfig(t *testing.T) {
|
||||
t.Fatalf("uint pop != 37890, = %d", pop)
|
||||
}
|
||||
|
||||
if pop := v.GetUint16("hello.pop"); pop != uint16(37890) {
|
||||
t.Fatalf("uint pop != 37890, = %d", pop)
|
||||
}
|
||||
|
||||
if pop := v.GetUint32("hello.pop"); pop != 37890 {
|
||||
t.Fatalf("uint32 pop != 37890, = %d", pop)
|
||||
}
|
||||
@@ -2516,7 +2520,10 @@ func TestKeyDelimiter(t *testing.T) {
|
||||
}
|
||||
|
||||
var yamlDeepNestedSlices = []byte(`TV:
|
||||
- title: "The expanse"
|
||||
- title: "The Expanse"
|
||||
title_i18n:
|
||||
USA: "The Expanse"
|
||||
Japan: "エクスパンス -巨獣めざめる-"
|
||||
seasons:
|
||||
- first_released: "December 14, 2015"
|
||||
episodes:
|
||||
@@ -2546,11 +2553,15 @@ func TestSliceIndexAccess(t *testing.T) {
|
||||
err := v.unmarshalReader(r, v.config)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "The expanse", v.GetString("tv.0.title"))
|
||||
assert.Equal(t, "The Expanse", v.GetString("tv.0.title"))
|
||||
assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
|
||||
assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
|
||||
assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
|
||||
|
||||
// Test nested keys with capital letters
|
||||
assert.Equal(t, "The Expanse", v.GetString("tv.0.title_i18n.USA"))
|
||||
assert.Equal(t, "エクスパンス -巨獣めざめる-", v.GetString("tv.0.title_i18n.Japan"))
|
||||
|
||||
// Test for index out of bounds
|
||||
assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user