diff options
Diffstat (limited to 'vendor/github.com/spf13/viper')
30 files changed, 0 insertions, 4841 deletions
diff --git a/vendor/github.com/spf13/viper/.editorconfig b/vendor/github.com/spf13/viper/.editorconfig deleted file mode 100644 index faef0c91e..000000000 --- a/vendor/github.com/spf13/viper/.editorconfig +++ /dev/null @@ -1,21 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true - -[*.go] -indent_style = tab - -[{Makefile,*.mk}] -indent_style = tab - -[*.nix] -indent_size = 2 - -[.golangci.yaml] -indent_size = 2 diff --git a/vendor/github.com/spf13/viper/.gitignore b/vendor/github.com/spf13/viper/.gitignore deleted file mode 100644 index f1bbd4280..000000000 --- a/vendor/github.com/spf13/viper/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/.devenv/ -/.direnv/ -/.idea/ -/.pre-commit-config.yaml -/bin/ -/build/ -/var/ -/vendor/ diff --git a/vendor/github.com/spf13/viper/.golangci.yaml b/vendor/github.com/spf13/viper/.golangci.yaml deleted file mode 100644 index bed0b83ec..000000000 --- a/vendor/github.com/spf13/viper/.golangci.yaml +++ /dev/null @@ -1,118 +0,0 @@ -version: "2" - -run: - timeout: 5m - -linters: - enable: - - bodyclose - - dogsled - - dupl - - durationcheck - - exhaustive - - gocritic - - godot - - gomoddirectives - - goprintffuncname - - govet - - importas - - ineffassign - - makezero - - misspell - - nakedret - - nilerr - - noctx - - nolintlint - - prealloc - - predeclared - - revive - - rowserrcheck - - sqlclosecheck - - staticcheck - - tparallel - - unconvert - - unparam - - unused - - wastedassign - - whitespace - - # fixme - # - cyclop - # - errcheck - # - errorlint - # - exhaustivestruct - # - forbidigo - # - forcetypeassert - # - gochecknoglobals - # - gochecknoinits - # - gocognit - # - goconst - # - gocyclo - # - gosec - # - gosimple - # - ifshort - # - lll - # - nlreturn - # - paralleltest - # - scopelint - # - thelper - # - wrapcheck - - # unused - # - depguard - # - goheader - # - gomodguard - - # don't enable: - # - asciicheck - # - funlen - # - godox - # - goerr113 - # - gomnd - # - interfacer - # - maligned - # - nestif - # - testpackage - # - wsl - - exclusions: - rules: - - linters: - - errcheck - - noctx - path: _test.go - presets: - - comments - - std-error-handling - - settings: - misspell: - locale: US - nolintlint: - allow-unused: false # report any unused nolint directives - require-specific: false # don't require nolint directives to be specific about which linter is being skipped - gocritic: - # Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage. - enabled-tags: - - diagnostic - - experimental - - opinionated - - style - disabled-checks: - - importShadow - - unnamedResult - -formatters: - enable: - - gci - - gofmt - - gofumpt - - goimports - # - golines - - settings: - gci: - sections: - - standard - - default - - localmodule diff --git a/vendor/github.com/spf13/viper/.yamlignore b/vendor/github.com/spf13/viper/.yamlignore deleted file mode 100644 index c04c4dead..000000000 --- a/vendor/github.com/spf13/viper/.yamlignore +++ /dev/null @@ -1,2 +0,0 @@ -# TODO: FIXME -/.github/ diff --git a/vendor/github.com/spf13/viper/.yamllint.yaml b/vendor/github.com/spf13/viper/.yamllint.yaml deleted file mode 100644 index bac19ce18..000000000 --- a/vendor/github.com/spf13/viper/.yamllint.yaml +++ /dev/null @@ -1,6 +0,0 @@ -ignore-from-file: [.gitignore, .yamlignore] - -extends: default - -rules: - line-length: disable diff --git a/vendor/github.com/spf13/viper/LICENSE b/vendor/github.com/spf13/viper/LICENSE deleted file mode 100644 index 4527efb9c..000000000 --- a/vendor/github.com/spf13/viper/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Steve Francia - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.
\ No newline at end of file diff --git a/vendor/github.com/spf13/viper/Makefile b/vendor/github.com/spf13/viper/Makefile deleted file mode 100644 index a77b9c81c..000000000 --- a/vendor/github.com/spf13/viper/Makefile +++ /dev/null @@ -1,87 +0,0 @@ -# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html - -OS = $(shell uname | tr A-Z a-z) -export PATH := $(abspath bin/):${PATH} - -# Build variables -BUILD_DIR ?= build -export CGO_ENABLED ?= 0 -export GOOS = $(shell go env GOOS) -ifeq (${VERBOSE}, 1) -ifeq ($(filter -v,${GOARGS}),) - GOARGS += -v -endif -TEST_FORMAT = short-verbose -endif - -# Dependency versions -GOTESTSUM_VERSION = 1.9.0 -GOLANGCI_VERSION = 1.53.3 - -# Add the ability to override some variables -# Use with care --include override.mk - -.PHONY: clear -clear: ## Clear the working area and the project - rm -rf bin/ - -.PHONY: check -check: test lint ## Run tests and linters - - -TEST_PKGS ?= ./... -.PHONY: test -test: TEST_FORMAT ?= short -test: SHELL = /bin/bash -test: export CGO_ENABLED=1 -test: bin/gotestsum ## Run tests - @mkdir -p ${BUILD_DIR} - bin/gotestsum --no-summary=skipped --junitfile ${BUILD_DIR}/coverage.xml --format ${TEST_FORMAT} -- -race -coverprofile=${BUILD_DIR}/coverage.txt -covermode=atomic $(filter-out -v,${GOARGS}) $(if ${TEST_PKGS},${TEST_PKGS},./...) - -.PHONY: lint -lint: lint-go lint-yaml -lint: ## Run linters - -.PHONY: lint-go -lint-go: - golangci-lint run $(if ${CI},--out-format github-actions,) - -.PHONY: lint-yaml -lint-yaml: - yamllint $(if ${CI},-f github,) --no-warnings . - -.PHONY: fmt -fmt: ## Format code - golangci-lint run --fix - -deps: bin/golangci-lint bin/gotestsum yamllint -deps: ## Install dependencies - -bin/gotestsum: - @mkdir -p bin - curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum && chmod +x ./bin/gotestsum - -bin/golangci-lint: - @mkdir -p bin - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- v${GOLANGCI_VERSION} - -.PHONY: yamllint -yamllint: - pip3 install --user yamllint - -# Add custom targets here --include custom.mk - -.PHONY: list -list: ## List all make targets - @${MAKE} -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | sort - -.PHONY: help -.DEFAULT_GOAL := help -help: - @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -# Variable outputting/exporting rules -var-%: ; @echo $($*) -varexport-%: ; @echo $*=$($*) diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md deleted file mode 100644 index 7a4c0fc30..000000000 --- a/vendor/github.com/spf13/viper/README.md +++ /dev/null @@ -1,931 +0,0 @@ -> ## Viper v2 feedback -> Viper is heading towards v2 and we would love to hear what _**you**_ would like to see in it. Share your thoughts here: https://forms.gle/R6faU74qPRPAzchZ9 -> -> **Thank you!** - - - - -[](https://github.com/avelino/awesome-go#configuration) -[](https://repl.it/@sagikazarmark/Viper-example#main.go) - -[](https://github.com/spf13/viper/actions?query=workflow%3ACI) -[](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[](https://goreportcard.com/report/github.com/spf13/viper) - -[](https://pkg.go.dev/mod/github.com/spf13/viper) - -**Go configuration with fangs!** - -Many Go projects are built using Viper including: - -* [Hugo](http://gohugo.io) -* [EMC RexRay](http://rexray.readthedocs.org/en/stable/) -* [Imgur’s Incus](https://github.com/Imgur/incus) -* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) -* [Docker Notary](https://github.com/docker/Notary) -* [BloomApi](https://www.bloomapi.com/) -* [doctl](https://github.com/digitalocean/doctl) -* [Clairctl](https://github.com/jgsqware/clairctl) -* [Mercure](https://mercure.rocks) -* [Meshery](https://github.com/meshery/meshery) -* [Bearer](https://github.com/bearer/bearer) -* [Coder](https://github.com/coder/coder) -* [Vitess](https://vitess.io/) - - -## Install - -```shell -go get github.com/spf13/viper -``` - -**Note:** Viper uses [Go Modules](https://go.dev/wiki/Modules) to manage dependencies. - - -## What is Viper? - -Viper is a complete configuration solution for Go applications including [12-Factor apps](https://12factor.net/#the_twelve_factors). -It is designed to work within an application, and can handle all types of configuration needs -and formats. It supports: - -* setting defaults -* reading from JSON, TOML, YAML, HCL, envfile and Java properties config files -* live watching and re-reading of config files (optional) -* reading from environment variables -* reading from remote config systems (etcd or Consul), and watching changes -* reading from command line flags -* reading from buffer -* setting explicit values - -Viper can be thought of as a registry for all of your applications configuration needs. - - -## Why Viper? - -When building a modern application, you don’t want to worry about -configuration file formats; you want to focus on building awesome software. -Viper is here to help with that. - -Viper does the following for you: - -1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, INI, envfile or Java properties formats. -2. Provide a mechanism to set default values for your different configuration options. -3. Provide a mechanism to set override values for options specified through command line flags. -4. Provide an alias system to easily rename parameters without breaking existing code. -5. Make it easy to tell the difference between when a user has provided a command line or config file which is the same as the default. - -Viper uses the following precedence order. Each item takes precedence over the item below it: - - * explicit call to `Set` - * flag - * env - * config - * key/value store - * default - -**Important:** Viper configuration keys are case insensitive. -There are ongoing discussions about making that optional. - - -## Putting Values into Viper - -### Establishing Defaults - -A good configuration system will support default values. A default value is not -required for a key, but it’s useful in the event that a key hasn't been set via -config file, environment variable, remote configuration or flag. - -Examples: - -```go -viper.SetDefault("ContentDir", "content") -viper.SetDefault("LayoutDir", "layouts") -viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"}) -``` - -### Reading Config Files - -Viper requires minimal configuration so it knows where to look for config files. -Viper supports JSON, TOML, YAML, HCL, INI, envfile and Java Properties files. Viper can search multiple paths, but -currently a single Viper instance only supports a single configuration file. -Viper does not default to any configuration search paths leaving defaults decision -to an application. - -Here is an example of how to use Viper to search for and read a configuration file. -None of the specific paths are required, but at least one path should be provided -where a configuration file is expected. - -```go -viper.SetConfigName("config") // name of config file (without extension) -viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name -viper.AddConfigPath("/etc/appname/") // path to look for the config file in -viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths -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", err)) -} -``` - -You can handle the specific case where no config file is found like this: - -```go -if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - // Config file not found; ignore error if desired - } else { - // Config file was found but another error was produced - } -} - -// Config file found and successfully parsed -``` - -*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmatically. For those configuration files that lie in the home of the user without any extension like `.bashrc` - -### Writing Config Files - -Reading from config files is useful, but at times you want to store all modifications made at run time. -For that, a bunch of commands are available, each with its own purpose: - -* WriteConfig - writes the current viper configuration to the predefined path, if exists. Errors if no predefined path. Will overwrite the current config file, if it exists. -* SafeWriteConfig - writes the current viper configuration to the predefined path. Errors if no predefined path. Will not overwrite the current config file, if it exists. -* WriteConfigAs - writes the current viper configuration to the given filepath. Will overwrite the given file, if it exists. -* SafeWriteConfigAs - writes the current viper configuration to the given filepath. Will not overwrite the given file, if it exists. - -As a rule of the thumb, everything marked with safe won't overwrite any file, but just create if not existent, whilst the default behavior is to create or truncate. - -A small examples section: - -```go -viper.WriteConfig() // writes current config to predefined path set by 'viper.AddConfigPath()' and 'viper.SetConfigName' -viper.SafeWriteConfig() -viper.WriteConfigAs("/path/to/my/.config") -viper.SafeWriteConfigAs("/path/to/my/.config") // will error since it has already been written -viper.SafeWriteConfigAs("/path/to/my/.other_config") -``` - -### Watching and re-reading config files - -Viper supports the ability to have your application live read a config file while running. - -Gone are the days of needing to restart a server to have a config take effect, -viper powered applications can read an update to a config file while running and -not miss a beat. - -Simply tell the viper instance to watchConfig. -Optionally you can provide a function for Viper to run each time a change occurs. - -**Make sure you add all of the configPaths prior to calling `WatchConfig()`** - -```go -viper.OnConfigChange(func(e fsnotify.Event) { - fmt.Println("Config file changed:", e.Name) -}) -viper.WatchConfig() -``` - -### Reading Config from io.Reader - -Viper predefines many configuration sources such as files, environment -variables, flags, and remote K/V store, but you are not bound to them. You can -also implement your own required configuration source and feed it to viper. - -```go -viper.SetConfigType("yaml") // or viper.SetConfigType("YAML") - -// any approach to require this configuration into your program. -var yamlExample = []byte(` -Hacker: true -name: steve -hobbies: -- skateboarding -- snowboarding -- go -clothing: - jacket: leather - trousers: denim -age: 35 -eyes : brown -beard: true -`) - -viper.ReadConfig(bytes.NewBuffer(yamlExample)) - -viper.Get("name") // this would be "steve" -``` - -### Setting Overrides - -These could be from a command line flag, or from your own application logic. - -```go -viper.Set("Verbose", true) -viper.Set("LogFile", LogFile) -viper.Set("host.port", 5899) // set subset -``` - -### Registering and Using Aliases - -Aliases permit a single value to be referenced by multiple keys - -```go -viper.RegisterAlias("loud", "Verbose") - -viper.Set("verbose", true) // same result as next line -viper.Set("loud", true) // same result as prior line - -viper.GetBool("loud") // true -viper.GetBool("verbose") // true -``` - -### Working with Environment Variables - -Viper has full support for environment variables. This enables 12 factor -applications out of the box. There are five methods that exist to aid working -with ENV: - - * `AutomaticEnv()` - * `BindEnv(string...) : error` - * `SetEnvPrefix(string)` - * `SetEnvKeyReplacer(string...) *strings.Replacer` - * `AllowEmptyEnv(bool)` - -_When working with ENV variables, it’s important to recognize that Viper -treats ENV variables as case sensitive._ - -Viper provides a mechanism to try to ensure that ENV variables are unique. By -using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from -the environment variables. Both `BindEnv` and `AutomaticEnv` will use this -prefix. - -`BindEnv` takes one or more parameters. The first parameter is the key name, the -rest are the name of the environment variables to bind to this key. If more than -one are provided, they will take precedence in the specified order. The name of -the environment variable is case sensitive. If the ENV variable name is not provided, then -Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter), -it **does not** automatically add the prefix. For example if the second parameter is "id", -Viper will look for the ENV variable "ID". - -One important thing to recognize when working with ENV variables is that the -value will be read each time it is accessed. Viper does not fix the value when -the `BindEnv` is called. - -`AutomaticEnv` is a powerful helper especially when combined with -`SetEnvPrefix`. When called, Viper will check for an environment variable any -time a `viper.Get` request is made. It will apply the following rules. It will -check for an environment variable with a name matching the key uppercased and -prefixed with the `EnvPrefix` if set. - -`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env -keys to an extent. This is useful if you want to use `-` or something in your -`Get()` calls, but want your environmental variables to use `_` delimiters. An -example of using it can be found in `viper_test.go`. - -Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function. -Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic. - -By default empty environment variables are considered unset and will fall back to -the next configuration source. To treat empty environment variables as set, use -the `AllowEmptyEnv` method. - -#### Env example - -```go -SetEnvPrefix("spf") // will be uppercased automatically -BindEnv("id") - -os.Setenv("SPF_ID", "13") // typically done outside of the app - -id := Get("id") // 13 -``` - -### Working with Flags - -Viper has the ability to bind to flags. Specifically, Viper supports `Pflags` -as used in the [Cobra](https://github.com/spf13/cobra) library. - -Like `BindEnv`, the value is not set when the binding method is called, but when -it is accessed. This means you can bind as early as you want, even in an -`init()` function. - -For individual flags, the `BindPFlag()` method provides this functionality. - -Example: - -```go -serverCmd.Flags().Int("port", 1138, "Port to run Application server on") -viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) -``` - -You can also bind an existing set of pflags (pflag.FlagSet): - -Example: - -```go -pflag.Int("flagname", 1234, "help message for flagname") - -pflag.Parse() -viper.BindPFlags(pflag.CommandLine) - -i := viper.GetInt("flagname") // retrieve values from viper instead of pflag -``` - -The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude -the use of other packages that use the [flag](https://golang.org/pkg/flag/) -package from the standard library. The pflag package can handle the flags -defined for the flag package by importing these flags. This is accomplished -by a calling a convenience function provided by the pflag package called -AddGoFlagSet(). - -Example: - -```go -package main - -import ( - "flag" - "github.com/spf13/pflag" -) - -func main() { - - // using standard library "flag" package - flag.Int("flagname", 1234, "help message for flagname") - - pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - pflag.Parse() - viper.BindPFlags(pflag.CommandLine) - - i := viper.GetInt("flagname") // retrieve value from viper - - // ... -} -``` - -#### Flag interfaces - -Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`. - -`FlagValue` represents a single flag. This is a very simple example on how to implement this interface: - -```go -type myFlag struct {} -func (f myFlag) HasChanged() bool { return false } -func (f myFlag) Name() string { return "my-flag-name" } -func (f myFlag) ValueString() string { return "my-flag-value" } -func (f myFlag) ValueType() string { return "string" } -``` - -Once your flag implements this interface, you can simply tell Viper to bind it: - -```go -viper.BindFlagValue("my-flag-name", myFlag{}) -``` - -`FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface: - -```go -type myFlagSet struct { - flags []myFlag -} - -func (f myFlagSet) VisitAll(fn func(FlagValue)) { - for _, flag := range flags { - fn(flag) - } -} -``` - -Once your flag set implements this interface, you can simply tell Viper to bind it: - -```go -fSet := myFlagSet{ - flags: []myFlag{myFlag{}, myFlag{}}, -} -viper.BindFlagValues("my-flags", fSet) -``` - -### Remote Key/Value Store Support - -To enable remote support in Viper, do a blank import of the `viper/remote` -package: - -`import _ "github.com/spf13/viper/remote"` - -Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path -in a Key/Value store such as etcd or Consul. These values take precedence over -default values, but are overridden by configuration values retrieved from disk, -flags, or environment variables. - -Viper supports multiple hosts. To use, pass a list of endpoints separated by `;`. For example `http://127.0.0.1:4001;http://127.0.0.1:4002`. - -Viper uses [crypt](https://github.com/sagikazarmark/crypt) to retrieve -configuration from the K/V store, which means that you can store your -configuration values encrypted and have them automatically decrypted if you have -the correct gpg keyring. Encryption is optional. - -You can use remote configuration in conjunction with local configuration, or -independently of it. - -`crypt` has a command-line helper that you can use to put configurations in your -K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001. - -```bash -$ go get github.com/sagikazarmark/crypt/bin/crypt -$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json -``` - -Confirm that your value was set: - -```bash -$ crypt get -plaintext /config/hugo.json -``` - -See the `crypt` documentation for examples of how to set encrypted values, or -how to use Consul. - -### Remote Key/Value Store Example - Unencrypted - -#### etcd -```go -viper.AddRemoteProvider("etcd", "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() -``` - -#### 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: - -```json -{ - "port": 8080, - "hostname": "myhostname.com" -} -``` - -```go -viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY") -viper.SetConfigType("json") // Need to explicitly set this to json -err := viper.ReadRemoteConfig() - -fmt.Println(viper.Get("port")) // 8080 -fmt.Println(viper.Get("hostname")) // myhostname.com -``` - -#### Firestore - -```go -viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document") -viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml" -err := viper.ReadRemoteConfig() -``` - -Of course, you're allowed to use `SecureRemoteProvider` also - - -#### NATS - -```go -viper.AddRemoteProvider("nats", "nats://127.0.0.1:4222", "myapp.config") -viper.SetConfigType("json") -err := viper.ReadRemoteConfig() -``` - -### Remote Key/Value Store Example - Encrypted - -```go -viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") -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() -``` - -### Watching Changes in etcd - Unencrypted - -```go -// alternatively, you can create a new viper instance. -var runtime_viper = viper.New() - -runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") -runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" - -// read from remote config the first time. -err := runtime_viper.ReadRemoteConfig() - -// unmarshal config -runtime_viper.Unmarshal(&runtime_conf) - -// open a goroutine to watch remote changes forever -go func(){ - for { - time.Sleep(time.Second * 5) // delay after each request - - // currently, only tested with etcd support - err := runtime_viper.WatchRemoteConfig() - if err != nil { - log.Errorf("unable to read remote config: %v", err) - continue - } - - // unmarshal new config into our runtime config struct. you can also use channel - // to implement a signal to notify the system of the changes - runtime_viper.Unmarshal(&runtime_conf) - } -}() -``` - -## Getting Values From Viper - -In Viper, there are a few ways to get a value depending on the value’s type. -The following functions and methods exist: - - * `Get(key string) : any` - * `GetBool(key string) : bool` - * `GetFloat64(key string) : float64` - * `GetInt(key string) : int` - * `GetIntSlice(key string) : []int` - * `GetString(key string) : string` - * `GetStringMap(key string) : map[string]any` - * `GetStringMapString(key string) : map[string]string` - * `GetStringSlice(key string) : []string` - * `GetTime(key string) : time.Time` - * `GetDuration(key string) : time.Duration` - * `IsSet(key string) : bool` - * `AllSettings() : map[string]any` - -One important thing to recognize is that each Get function will return a zero -value if it’s not found. To check if a given key exists, the `IsSet()` method -has been provided. - -The zero value will also be returned if the value is set, but fails to parse -as the requested type. - -Example: -```go -viper.GetString("logfile") // case-insensitive Setting & Getting -if viper.GetBool("verbose") { - fmt.Println("verbose enabled") -} -``` -### Accessing nested keys - -The accessor methods also accept formatted paths to deeply nested keys. For -example, if the following JSON file is loaded: - -```json -{ - "host": { - "address": "localhost", - "port": 5799 - }, - "datastore": { - "metric": { - "host": "127.0.0.1", - "port": 3099 - }, - "warehouse": { - "host": "198.0.0.1", - "port": 2112 - } - } -} - -``` - -Viper can access a nested field by passing a `.` delimited path of keys: - -```go -GetString("datastore.metric.host") // (returns "127.0.0.1") -``` - -This obeys the precedence rules established above; the search for the path -will cascade through the remaining configuration registries until found. - -For example, given this configuration file, both `datastore.metric.host` and -`datastore.metric.port` are already defined (and may be overridden). If in addition -`datastore.metric.protocol` was defined in the defaults, Viper would also find it. - -However, if `datastore.metric` was overridden (by a flag, an environment variable, -the `Set()` method, …) with an immediate value, then all sub-keys of -`datastore.metric` become undefined, they are “shadowed” by the higher-priority -configuration level. - -Viper can access array indices by using numbers in the path. For example: - -```jsonc -{ - "host": { - "address": "localhost", - "ports": [ - 5799, - 6029 - ] - }, - "datastore": { - "metric": { - "host": "127.0.0.1", - "port": 3099 - }, - "warehouse": { - "host": "198.0.0.1", - "port": 2112 - } - } -} - -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. - -```jsonc -{ - "datastore.metric.host": "0.0.0.0", - "host": { - "address": "localhost", - "port": 5799 - }, - "datastore": { - "metric": { - "host": "127.0.0.1", - "port": 3099 - }, - "warehouse": { - "host": "198.0.0.1", - "port": 2112 - } - } -} - -GetString("datastore.metric.host") // returns "0.0.0.0" -``` - -### Extracting a sub-tree - -When developing reusable modules, it's often useful to extract a subset of the configuration -and pass it to a module. This way the module can be instantiated more than once, with different configurations. - -For example, an application might use multiple different cache stores for different purposes: - -```yaml -cache: - cache1: - max-items: 100 - item-size: 64 - cache2: - max-items: 200 - item-size: 80 -``` - -We could pass the cache name to a module (eg. `NewCache("cache1")`), -but it would require weird concatenation for accessing config keys and would be less separated from the global config. - -So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration: - -```go -cache1Config := viper.Sub("cache.cache1") -if cache1Config == nil { // Sub returns nil if the key cannot be found - panic("cache configuration not found") -} - -cache1 := NewCache(cache1Config) -``` - -**Note:** Always check the return value of `Sub`. It returns `nil` if a key cannot be found. - -Internally, the `NewCache` function can address `max-items` and `item-size` keys directly: - -```go -func NewCache(v *Viper) *Cache { - return &Cache{ - MaxItems: v.GetInt("max-items"), - ItemSize: v.GetInt("item-size"), - } -} -``` - -The resulting code is easy to test, since it's decoupled from the main config structure, -and easier to reuse (for the same reason). - - -### Unmarshaling - -You also have the option of Unmarshaling all or a specific value to a struct, map, -etc. - -There are two methods to do this: - - * `Unmarshal(rawVal any) : error` - * `UnmarshalKey(key string, rawVal any) : error` - -Example: - -```go -type config struct { - Port int - Name string - PathMap string `mapstructure:"path_map"` -} - -var C config - -err := viper.Unmarshal(&C) -if err != nil { - t.Fatalf("unable to decode into struct, %v", err) -} -``` - -If you want to unmarshal configuration where the keys themselves contain dot (the default key delimiter), -you have to change the delimiter: - -```go -v := viper.NewWithOptions(viper.KeyDelimiter("::")) - -v.SetDefault("chart::values", map[string]any{ - "ingress": map[string]any{ - "annotations": map[string]any{ - "traefik.frontend.rule.type": "PathPrefix", - "traefik.ingress.kubernetes.io/ssl-redirect": "true", - }, - }, -}) - -type config struct { - Chart struct{ - Values map[string]any - } -} - -var C config - -v.Unmarshal(&C) -``` - -Viper also supports unmarshaling into embedded structs: - -```go -/* -Example config: - -module: - enabled: true - token: 89h3f98hbwf987h3f98wenf89ehf -*/ -type config struct { - Module struct { - Enabled bool - - moduleConfig `mapstructure:",squash"` - } -} - -// moduleConfig could be in a module specific package -type moduleConfig struct { - Token string -} - -var C config - -err := viper.Unmarshal(&C) -if err != nil { - t.Fatalf("unable to decode into struct, %v", err) -} -``` - -Viper uses [github.com/go-viper/mapstructure](https://github.com/go-viper/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. - -### Decoding custom formats - -A frequently requested feature for Viper is adding more value formats and decoders. -For example, parsing character (dot, comma, semicolon, etc) separated strings into slices. - -This is already available in Viper using mapstructure decode hooks. - -Read more about the details in [this blog post](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/). - -### Marshalling to string - -You may need to marshal all the settings held in viper into a string rather than write them to a file. -You can use your favorite format's marshaller with the config returned by `AllSettings()`. - -```go -import ( - yaml "go.yaml.in/yaml/v3" - // ... -) - -func yamlStringSettings() string { - c := viper.AllSettings() - bs, err := yaml.Marshal(c) - if err != nil { - log.Fatalf("unable to marshal config to YAML: %v", err) - } - return string(bs) -} -``` - -## Viper or Vipers? - -Viper comes with a global instance (singleton) out of the box. - -Although it makes setting up configuration easy, -using it is generally discouraged as it makes testing harder and can lead to unexpected behavior. - -The best practice is to initialize a Viper instance and pass that around when necessary. - -The global instance _MAY_ be deprecated in the future. -See [#1855](https://github.com/spf13/viper/issues/1855) for more details. - -### Working with multiple vipers - -You can also create many different vipers for use in your application. Each will -have its own unique set of configurations and values. Each can read from a -different config file, key value store, etc. All of the functions that viper -package supports are mirrored as methods on a viper. - -Example: - -```go -x := viper.New() -y := viper.New() - -x.SetDefault("ContentDir", "content") -y.SetDefault("ContentDir", "foobar") - -//... -``` - -When working with multiple vipers, it is up to the user to keep track of the -different vipers. - - -## Q & A - -### Why is it called “Viper”? - -A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe)) -to [Cobra](https://github.com/spf13/cobra). While both can operate completely -independently, together they make a powerful pair to handle much of your -application foundation needs. - -### Why is it called “Cobra”? - -Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)? - -### Does Viper support case sensitive keys? - -**tl;dr:** No. - -Viper merges configuration from various sources, many of which are either case insensitive or uses different casing than the rest of the sources (eg. env vars). -In order to provide the best experience when using multiple sources, the decision has been made to make all keys case insensitive. - -There has been several attempts to implement case sensitivity, but unfortunately it's not that trivial. We might take a stab at implementing it in [Viper v2](https://github.com/spf13/viper/issues/772), but despite the initial noise, it does not seem to be requested that much. - -You can vote for case sensitivity by filling out this feedback form: https://forms.gle/R6faU74qPRPAzchZ9 - -### Is it safe to concurrently read and write to a viper? - -No, you will need to synchronize access to the viper yourself (for example by using the `sync` package). Concurrent reads and writes can cause a panic. - -## Troubleshooting - -See [TROUBLESHOOTING.md](TROUBLESHOOTING.md). - -## Development - -**For an optimal developer experience, it is recommended to install [Nix](https://nixos.org/download.html) and [direnv](https://direnv.net/docs/installation.html).** - -_Alternatively, install [Go](https://go.dev/dl/) on your computer then run `make deps` to install the rest of the dependencies._ - -Run the test suite: - -```shell -make test -``` - -Run linters: - -```shell -make lint # pass -j option to run them in parallel -``` - -Some linter violations can automatically be fixed: - -```shell -make fmt -``` - -## License - -The project is licensed under the [MIT License](LICENSE). diff --git a/vendor/github.com/spf13/viper/TROUBLESHOOTING.md b/vendor/github.com/spf13/viper/TROUBLESHOOTING.md deleted file mode 100644 index b68993d41..000000000 --- a/vendor/github.com/spf13/viper/TROUBLESHOOTING.md +++ /dev/null @@ -1,32 +0,0 @@ -# Troubleshooting - -## Unmarshaling doesn't work - -The most common reason for this issue is improper use of struct tags (eg. `yaml` or `json`). Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. Please refer to the library's documentation for using other struct tags. - -## Cannot find package - -Viper installation seems to fail a lot lately with the following (or a similar) error: - -``` -cannot find package "github.com/hashicorp/hcl/tree/hcl1" in any of: -/usr/local/Cellar/go/1.15.7_1/libexec/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOROOT) -/Users/user/go/src/github.com/hashicorp/hcl/tree/hcl1 (from $GOPATH) -``` - -As the error message suggests, Go tries to look up dependencies in `GOPATH` mode (as it's commonly called) from the `GOPATH`. -Viper opted to use [Go Modules](https://go.dev/wiki/Modules) to manage its dependencies. While in many cases the two methods are interchangeable, once a dependency releases new (major) versions, `GOPATH` mode is no longer able to decide which version to use, so it'll either use one that's already present or pick a version (usually the `master` branch). - -The solution is easy: switch to using Go Modules. -Please refer to the [wiki](https://go.dev/wiki/Modules) on how to do that. - -**tl;dr* `export GO111MODULE=on` - -## Unquoted 'y' and 'n' characters get replaced with _true_ and _false_ when reading a YAML file - -This is a YAML 1.1 feature according to [go-yaml/yaml#740](https://github.com/go-yaml/yaml/issues/740). - -Potential solutions are: - -1. Quoting values resolved as boolean -1. Upgrading to YAML v3 (for the time being this is possible by passing the `viper_yaml3` tag to your build) diff --git a/vendor/github.com/spf13/viper/UPGRADE.md b/vendor/github.com/spf13/viper/UPGRADE.md deleted file mode 100644 index a33c965a4..000000000 --- a/vendor/github.com/spf13/viper/UPGRADE.md +++ /dev/null @@ -1,147 +0,0 @@ -# Update Log - -**This document details any major updates required to use new features or improvements in Viper.** - -## v1.20.x - -### New file searching API - -Viper now includes a new file searching API that allows users to customize how Viper looks for config files. - -Viper accepts a custom [`Finder`](https://pkg.go.dev/github.com/spf13/viper#Finder) interface implementation: - -```go -// Finder looks for files and directories in an [afero.Fs] filesystem. -type Finder interface { - Find(fsys afero.Fs) ([]string, error) -} -``` - -It is supposed to return a list of paths to config files. - -The default implementation uses [github.com/sagikazarmark/locafero](https://github.com/sagikazarmark/locafero) under the hood. - -You can supply your own implementation using `WithFinder`: - -```go -v := viper.NewWithOptions( - viper.WithFinder(&MyFinder{}), -) -``` - -For more information, check out the [Finder examples](https://pkg.go.dev/github.com/spf13/viper#Finder) -and the [documentation](https://pkg.go.dev/github.com/sagikazarmark/locafero) for the locafero package. - -### New encoding API - -Viper now allows customizing the encoding layer by providing an API for encoding and decoding configuration data: - -```go -// Encoder encodes Viper's internal data structures into a byte representation. -// It's primarily used for encoding a map[string]any into a file format. -type Encoder interface { - Encode(v map[string]any) ([]byte, error) -} - -// Decoder decodes the contents of a byte slice into Viper's internal data structures. -// It's primarily used for decoding contents of a file into a map[string]any. -type Decoder interface { - Decode(b []byte, v map[string]any) error -} - -// Codec combines [Encoder] and [Decoder] interfaces. -type Codec interface { - Encoder - Decoder -} -``` - -By default, Viper includes the following codecs: - -- JSON -- TOML -- YAML -- Dotenv - -The rest of the codecs are moved to [github.com/go-viper/encoding](https://github.com/go-viper/encoding) - -Customizing the encoding layer is possible by providing a custom registry of codecs: - -- [Encoder](https://pkg.go.dev/github.com/spf13/viper#Encoder) -> [EncoderRegistry](https://pkg.go.dev/github.com/spf13/viper#EncoderRegistry) -- [Decoder](https://pkg.go.dev/github.com/spf13/viper#Decoder) -> [DecoderRegistry](https://pkg.go.dev/github.com/spf13/viper#DecoderRegistry) -- [Codec](https://pkg.go.dev/github.com/spf13/viper#Codec) -> [CodecRegistry](https://pkg.go.dev/github.com/spf13/viper#CodecRegistry) - -You can supply the registry of codecs to Viper using the appropriate `With*Registry` function: - -```go -codecRegistry := viper.NewCodecRegistry() - -codecRegistry.RegisterCodec("myformat", &MyCodec{}) - -v := viper.NewWithOptions( - viper.WithCodecRegistry(codecRegistry), -) -``` - -### BREAKING: "github.com/mitchellh/mapstructure" depedency replaced - -The original [mapstructure](https://github.com/mitchellh/mapstructure) has been [archived](https://github.com/mitchellh/mapstructure/issues/349) and was replaced with a [fork](https://github.com/go-viper/mapstructure) maintained by Viper ([#1723](https://github.com/spf13/viper/pull/1723)). - -As a result, the package import path needs to be changed in cases where `mapstructure` is directly referenced in your code. - -For example, when providing a custom decoder config: - -```go -err := viper.Unmarshal(&appConfig, func(config *mapstructure.DecoderConfig) { - config.TagName = "yaml" -}) -``` - -The change is fairly straightforward, just replace all occurrences of the import path `github.com/mitchellh/mapstructure` with `github.com/go-viper/mapstructure/v2`: - -```diff -- import "github.com/mitchellh/mapstructure" -+ import "github.com/go-viper/mapstructure/v2" -``` - -### BREAKING: HCL, Java properties, INI removed from core - -In order to reduce third-party dependencies, Viper dropped support for the following formats from the core: - -- HCL -- Java properties -- INI - -You can still use these formats though by importing them from [github.com/go-viper/encoding](https://github.com/go-viper/encoding): - -```go -import ( - "github.com/go-viper/encoding/hcl" - "github.com/go-viper/encoding/javaproperties" - "github.com/go-viper/encoding/ini" -) - -codecRegistry := viper.NewCodecRegistry() - -{ - codec := hcl.Codec{} - - codecRegistry.RegisterCodec("hcl", codec) - codecRegistry.RegisterCodec("tfvars", codec) - -} - -{ - codec := &javaproperties.Codec{} - - codecRegistry.RegisterCodec("properties", codec) - codecRegistry.RegisterCodec("props", codec) - codecRegistry.RegisterCodec("prop", codec) -} - -codecRegistry.RegisterCodec("ini", ini.Codec{}) - -v := viper.NewWithOptions( - viper.WithCodecRegistry(codecRegistry), -) -``` diff --git a/vendor/github.com/spf13/viper/encoding.go b/vendor/github.com/spf13/viper/encoding.go deleted file mode 100644 index a7da55860..000000000 --- a/vendor/github.com/spf13/viper/encoding.go +++ /dev/null @@ -1,181 +0,0 @@ -package viper - -import ( - "errors" - "strings" - "sync" - - "github.com/spf13/viper/internal/encoding/dotenv" - "github.com/spf13/viper/internal/encoding/json" - "github.com/spf13/viper/internal/encoding/toml" - "github.com/spf13/viper/internal/encoding/yaml" -) - -// Encoder encodes Viper's internal data structures into a byte representation. -// It's primarily used for encoding a map[string]any into a file format. -type Encoder interface { - Encode(v map[string]any) ([]byte, error) -} - -// Decoder decodes the contents of a byte slice into Viper's internal data structures. -// It's primarily used for decoding contents of a file into a map[string]any. -type Decoder interface { - Decode(b []byte, v map[string]any) error -} - -// Codec combines [Encoder] and [Decoder] interfaces. -type Codec interface { - Encoder - Decoder -} - -// TODO: consider adding specific errors for not found scenarios - -// EncoderRegistry returns an [Encoder] for a given format. -// -// Format is case-insensitive. -// -// [EncoderRegistry] returns an error if no [Encoder] is registered for the format. -type EncoderRegistry interface { - Encoder(format string) (Encoder, error) -} - -// DecoderRegistry returns an [Decoder] for a given format. -// -// Format is case-insensitive. -// -// [DecoderRegistry] returns an error if no [Decoder] is registered for the format. -type DecoderRegistry interface { - Decoder(format string) (Decoder, error) -} - -// [CodecRegistry] combines [EncoderRegistry] and [DecoderRegistry] interfaces. -type CodecRegistry interface { - EncoderRegistry - DecoderRegistry -} - -// WithEncoderRegistry sets a custom [EncoderRegistry]. -func WithEncoderRegistry(r EncoderRegistry) Option { - return optionFunc(func(v *Viper) { - if r == nil { - return - } - - v.encoderRegistry = r - }) -} - -// WithDecoderRegistry sets a custom [DecoderRegistry]. -func WithDecoderRegistry(r DecoderRegistry) Option { - return optionFunc(func(v *Viper) { - if r == nil { - return - } - - v.decoderRegistry = r - }) -} - -// WithCodecRegistry sets a custom [EncoderRegistry] and [DecoderRegistry]. -func WithCodecRegistry(r CodecRegistry) Option { - return optionFunc(func(v *Viper) { - if r == nil { - return - } - - v.encoderRegistry = r - v.decoderRegistry = r - }) -} - -// DefaultCodecRegistry is a simple implementation of [CodecRegistry] that allows registering custom [Codec]s. -type DefaultCodecRegistry struct { - codecs map[string]Codec - - mu sync.RWMutex - once sync.Once -} - -// NewCodecRegistry returns a new [CodecRegistry], ready to accept custom [Codec]s. -func NewCodecRegistry() *DefaultCodecRegistry { - r := &DefaultCodecRegistry{} - - r.init() - - return r -} - -func (r *DefaultCodecRegistry) init() { - r.once.Do(func() { - r.codecs = map[string]Codec{} - }) -} - -// RegisterCodec registers a custom [Codec]. -// -// Format is case-insensitive. -func (r *DefaultCodecRegistry) RegisterCodec(format string, codec Codec) error { - r.init() - - r.mu.Lock() - defer r.mu.Unlock() - - r.codecs[strings.ToLower(format)] = codec - - return nil -} - -// Encoder implements the [EncoderRegistry] interface. -// -// Format is case-insensitive. -func (r *DefaultCodecRegistry) Encoder(format string) (Encoder, error) { - encoder, ok := r.codec(format) - if !ok { - return nil, errors.New("encoder not found for this format") - } - - return encoder, nil -} - -// Decoder implements the [DecoderRegistry] interface. -// -// Format is case-insensitive. -func (r *DefaultCodecRegistry) Decoder(format string) (Decoder, error) { - decoder, ok := r.codec(format) - if !ok { - return nil, errors.New("decoder not found for this format") - } - - return decoder, nil -} - -func (r *DefaultCodecRegistry) codec(format string) (Codec, bool) { - r.mu.Lock() - defer r.mu.Unlock() - - format = strings.ToLower(format) - - if r.codecs != nil { - codec, ok := r.codecs[format] - if ok { - return codec, true - } - } - - switch format { - case "yaml", "yml": - return yaml.Codec{}, true - - case "json": - return json.Codec{}, true - - case "toml": - return toml.Codec{}, true - - case "dotenv", "env": - return &dotenv.Codec{}, true - } - - return nil, false -} diff --git a/vendor/github.com/spf13/viper/experimental.go b/vendor/github.com/spf13/viper/experimental.go deleted file mode 100644 index 6e19e8a10..000000000 --- a/vendor/github.com/spf13/viper/experimental.go +++ /dev/null @@ -1,8 +0,0 @@ -package viper - -// ExperimentalBindStruct tells Viper to use the new bind struct feature. -func ExperimentalBindStruct() Option { - return optionFunc(func(v *Viper) { - v.experimentalBindStruct = true - }) -} diff --git a/vendor/github.com/spf13/viper/file.go b/vendor/github.com/spf13/viper/file.go deleted file mode 100644 index 50a40581d..000000000 --- a/vendor/github.com/spf13/viper/file.go +++ /dev/null @@ -1,104 +0,0 @@ -package viper - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/sagikazarmark/locafero" - "github.com/spf13/afero" -) - -// ExperimentalFinder tells Viper to use the new Finder interface for finding configuration files. -func ExperimentalFinder() Option { - return optionFunc(func(v *Viper) { - v.experimentalFinder = true - }) -} - -// Search for a config file. -func (v *Viper) findConfigFile() (string, error) { - finder := v.finder - - if finder == nil && v.experimentalFinder { - var names []string - - if v.configType != "" { - names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...) - } else { - names = locafero.NameWithExtensions(v.configName, SupportedExts...) - } - - finder = locafero.Finder{ - Paths: v.configPaths, - Names: names, - Type: locafero.FileTypeFile, - } - } - - if finder != nil { - return v.findConfigFileWithFinder(finder) - } - - return v.findConfigFileOld() -} - -func (v *Viper) findConfigFileWithFinder(finder Finder) (string, error) { - results, err := finder.Find(v.fs) - if err != nil { - return "", err - } - - if len(results) == 0 { - return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} - } - - // We call clean on the final result to ensure that the path is in its canonical form. - // This is mostly for consistent path handling and to make sure tests pass. - return results[0], nil -} - -// Search all configPaths for any config file. -// Returns the first path that exists (and is a config file). -func (v *Viper) findConfigFileOld() (string, error) { - v.logger.Info("searching for config in paths", "paths", v.configPaths) - - for _, cp := range v.configPaths { - file := v.searchInPath(cp) - if file != "" { - return file, nil - } - } - return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} -} - -func (v *Viper) searchInPath(in string) (filename string) { - v.logger.Debug("searching for config in path", "path", in) - for _, ext := range SupportedExts { - v.logger.Debug("checking if file exists", "file", filepath.Join(in, v.configName+"."+ext)) - if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { - v.logger.Debug("found file", "file", filepath.Join(in, v.configName+"."+ext)) - return filepath.Join(in, v.configName+"."+ext) - } - } - - if v.configType != "" { - if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b { - return filepath.Join(in, v.configName) - } - } - - return "" -} - -// exists checks if file exists. -func exists(fs afero.Fs, path string) (bool, error) { - stat, err := fs.Stat(path) - if err == nil { - return !stat.IsDir(), nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} diff --git a/vendor/github.com/spf13/viper/finder.go b/vendor/github.com/spf13/viper/finder.go deleted file mode 100644 index 9b203ea69..000000000 --- a/vendor/github.com/spf13/viper/finder.go +++ /dev/null @@ -1,55 +0,0 @@ -package viper - -import ( - "errors" - - "github.com/spf13/afero" -) - -// WithFinder sets a custom [Finder]. -func WithFinder(f Finder) Option { - return optionFunc(func(v *Viper) { - if f == nil { - return - } - - v.finder = f - }) -} - -// Finder looks for files and directories in an [afero.Fs] filesystem. -type Finder interface { - Find(fsys afero.Fs) ([]string, error) -} - -// Finders combines multiple finders into one. -func Finders(finders ...Finder) Finder { - return &combinedFinder{finders: finders} -} - -// combinedFinder is a Finder that combines multiple finders. -type combinedFinder struct { - finders []Finder -} - -// Find implements the [Finder] interface. -func (c *combinedFinder) Find(fsys afero.Fs) ([]string, error) { - var results []string - var errs []error - - for _, finder := range c.finders { - if finder == nil { - continue - } - - r, err := finder.Find(fsys) - if err != nil { - errs = append(errs, err) - continue - } - - results = append(results, r...) - } - - return results, errors.Join(errs...) -} diff --git a/vendor/github.com/spf13/viper/flags.go b/vendor/github.com/spf13/viper/flags.go deleted file mode 100644 index de033ed58..000000000 --- a/vendor/github.com/spf13/viper/flags.go +++ /dev/null @@ -1,57 +0,0 @@ -package viper - -import "github.com/spf13/pflag" - -// FlagValueSet is an interface that users can implement -// to bind a set of flags to viper. -type FlagValueSet interface { - VisitAll(fn func(FlagValue)) -} - -// FlagValue is an interface that users can implement -// to bind different flags to viper. -type FlagValue interface { - HasChanged() bool - Name() string - ValueString() string - ValueType() string -} - -// pflagValueSet is a wrapper around *pflag.ValueSet -// that implements FlagValueSet. -type pflagValueSet struct { - flags *pflag.FlagSet -} - -// VisitAll iterates over all *pflag.Flag inside the *pflag.FlagSet. -func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) { - p.flags.VisitAll(func(flag *pflag.Flag) { - fn(pflagValue{flag}) - }) -} - -// pflagValue is a wrapper around *pflag.flag -// that implements FlagValue. -type pflagValue struct { - flag *pflag.Flag -} - -// HasChanged returns whether the flag has changes or not. -func (p pflagValue) HasChanged() bool { - return p.flag.Changed -} - -// Name returns the name of the flag. -func (p pflagValue) Name() string { - return p.flag.Name -} - -// ValueString returns the value of the flag as a string. -func (p pflagValue) ValueString() string { - return p.flag.Value.String() -} - -// ValueType returns the type of the flag as a string. -func (p pflagValue) ValueType() string { - return p.flag.Value.Type() -} diff --git a/vendor/github.com/spf13/viper/flake.lock b/vendor/github.com/spf13/viper/flake.lock deleted file mode 100644 index 0b8cfb5a8..000000000 --- a/vendor/github.com/spf13/viper/flake.lock +++ /dev/null @@ -1,255 +0,0 @@ -{ - "nodes": { - "cachix": { - "inputs": { - "devenv": [ - "devenv" - ], - "flake-compat": [ - "devenv" - ], - "git-hooks": [ - "devenv", - "git-hooks" - ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1748883665, - "narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=", - "owner": "cachix", - "repo": "cachix", - "rev": "f707778d902af4d62d8dd92c269f8e70de09acbe", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "latest", - "repo": "cachix", - "type": "github" - } - }, - "devenv": { - "inputs": { - "cachix": "cachix", - "flake-compat": "flake-compat", - "git-hooks": "git-hooks", - "nix": "nix", - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1755257397, - "narHash": "sha256-VU+OHexL2y6y7yrpEc6bZvYYwoQg6aZK1b4YxT0yZCk=", - "owner": "cachix", - "repo": "devenv", - "rev": "6f9c3d4722aa253631644329f7bda60b1d3d1b97", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": [ - "devenv", - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-parts_2": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1754487366, - "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "gitignore": "gitignore", - "nixpkgs": [ - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1750779888, - "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "devenv", - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "nix": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "flake-parts": "flake-parts", - "git-hooks-nix": [ - "devenv", - "git-hooks" - ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "nixpkgs-23-11": [ - "devenv" - ], - "nixpkgs-regression": [ - "devenv" - ] - }, - "locked": { - "lastModified": 1755029779, - "narHash": "sha256-3+GHIYGg4U9XKUN4rg473frIVNn8YD06bjwxKS1IPrU=", - "owner": "cachix", - "repo": "nix", - "rev": "b0972b0eee6726081d10b1199f54de6d2917f861", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "devenv-2.30", - "repo": "nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1750441195, - "narHash": "sha256-yke+pm+MdgRb6c0dPt8MgDhv7fcBbdjmv1ZceNTyzKg=", - "owner": "cachix", - "repo": "devenv-nixpkgs", - "rev": "0ceffe312871b443929ff3006960d29b120dc627", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "rolling", - "repo": "devenv-nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1753579242, - "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1755268003, - "narHash": "sha256-nNaeJjo861wFR0tjHDyCnHs1rbRtrMgxAKMoig9Sj/w=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "32f313e49e42f715491e1ea7b306a87c16fe0388", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "devenv": "devenv", - "flake-parts": "flake-parts_2", - "nixpkgs": "nixpkgs_2" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/vendor/github.com/spf13/viper/flake.nix b/vendor/github.com/spf13/viper/flake.nix deleted file mode 100644 index a16b2e3a7..000000000 --- a/vendor/github.com/spf13/viper/flake.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ - description = "Viper"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - devenv.url = "github:cachix/devenv"; - }; - - outputs = - inputs@{ flake-parts, ... }: - flake-parts.lib.mkFlake { inherit inputs; } { - imports = [ - inputs.devenv.flakeModule - ]; - - systems = [ - "x86_64-linux" - "x86_64-darwin" - "aarch64-darwin" - ]; - - perSystem = - { pkgs, ... }: - { - devenv.shells = { - default = { - languages = { - go.enable = true; - }; - - git-hooks.hooks = { - nixpkgs-fmt.enable = true; - yamllint.enable = true; - }; - - packages = with pkgs; [ - gnumake - - golangci-lint - yamllint - ]; - - scripts = { - versions.exec = '' - go version - golangci-lint version - ''; - }; - - enterShell = '' - versions - ''; - - # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 - containers = pkgs.lib.mkForce { }; - }; - }; - }; - }; -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/dotenv/codec.go b/vendor/github.com/spf13/viper/internal/encoding/dotenv/codec.go deleted file mode 100644 index 3ebc76f02..000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/dotenv/codec.go +++ /dev/null @@ -1,61 +0,0 @@ -package dotenv - -import ( - "bytes" - "fmt" - "sort" - "strings" - - "github.com/subosito/gotenv" -) - -const keyDelimiter = "_" - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for encoding data containing environment variables -// (commonly called as dotenv format). -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - flattened := map[string]any{} - - flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter) - - keys := make([]string, 0, len(flattened)) - - for key := range flattened { - keys = append(keys, key) - } - - sort.Strings(keys) - - var buf bytes.Buffer - - for _, key := range keys { - _, err := buf.WriteString(fmt.Sprintf("%v=%v\n", strings.ToUpper(key), flattened[key])) - if err != nil { - return nil, err - } - } - - return buf.Bytes(), nil -} - -func (Codec) Decode(b []byte, v map[string]any) error { - var buf bytes.Buffer - - _, err := buf.Write(b) - if err != nil { - return err - } - - env, err := gotenv.StrictParse(&buf) - if err != nil { - return err - } - - for key, value := range env { - v[key] = value - } - - return nil -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/dotenv/map_utils.go b/vendor/github.com/spf13/viper/internal/encoding/dotenv/map_utils.go deleted file mode 100644 index 8bfe0a9de..000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/dotenv/map_utils.go +++ /dev/null @@ -1,41 +0,0 @@ -package dotenv - -import ( - "strings" - - "github.com/spf13/cast" -) - -// flattenAndMergeMap recursively flattens the given map into a new map -// Code is based on the function with the same name in the main package. -// TODO: move it to a common place. -func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any { - if shadow != nil && prefix != "" && shadow[prefix] != nil { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]any) - } - - var m2 map[string]any - if prefix != "" { - prefix += delimiter - } - for k, val := range m { - fullKey := prefix + k - switch val := val.(type) { - case map[string]any: - m2 = val - case map[any]any: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = val - continue - } - // recursively merge to shadow map - shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter) - } - return shadow -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/json/codec.go b/vendor/github.com/spf13/viper/internal/encoding/json/codec.go deleted file mode 100644 index da7546b5a..000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/json/codec.go +++ /dev/null @@ -1,17 +0,0 @@ -package json - -import ( - "encoding/json" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding. -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - // TODO: expose prefix and indent in the Codec as setting? - return json.MarshalIndent(v, "", " ") -} - -func (Codec) Decode(b []byte, v map[string]any) error { - return json.Unmarshal(b, &v) -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/toml/codec.go b/vendor/github.com/spf13/viper/internal/encoding/toml/codec.go deleted file mode 100644 index c70aa8d28..000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/toml/codec.go +++ /dev/null @@ -1,16 +0,0 @@ -package toml - -import ( - "github.com/pelletier/go-toml/v2" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding. -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - return toml.Marshal(v) -} - -func (Codec) Decode(b []byte, v map[string]any) error { - return toml.Unmarshal(b, &v) -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go b/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go deleted file mode 100644 index a7a839fd9..000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go +++ /dev/null @@ -1,14 +0,0 @@ -package yaml - -import "go.yaml.in/yaml/v3" - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding. -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - return yaml.Marshal(v) -} - -func (Codec) Decode(b []byte, v map[string]any) error { - return yaml.Unmarshal(b, &v) -} diff --git a/vendor/github.com/spf13/viper/internal/features/bind_struct.go b/vendor/github.com/spf13/viper/internal/features/bind_struct.go deleted file mode 100644 index 89302c216..000000000 --- a/vendor/github.com/spf13/viper/internal/features/bind_struct.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build viper_bind_struct - -package features - -const BindStruct = true diff --git a/vendor/github.com/spf13/viper/internal/features/bind_struct_default.go b/vendor/github.com/spf13/viper/internal/features/bind_struct_default.go deleted file mode 100644 index edfaf73b6..000000000 --- a/vendor/github.com/spf13/viper/internal/features/bind_struct_default.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build !viper_bind_struct - -package features - -const BindStruct = false diff --git a/vendor/github.com/spf13/viper/internal/features/finder.go b/vendor/github.com/spf13/viper/internal/features/finder.go deleted file mode 100644 index 983ea3a9d..000000000 --- a/vendor/github.com/spf13/viper/internal/features/finder.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build viper_finder - -package features - -const Finder = true diff --git a/vendor/github.com/spf13/viper/internal/features/finder_default.go b/vendor/github.com/spf13/viper/internal/features/finder_default.go deleted file mode 100644 index 89bcb06ee..000000000 --- a/vendor/github.com/spf13/viper/internal/features/finder_default.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build !viper_finder - -package features - -const Finder = false diff --git a/vendor/github.com/spf13/viper/logger.go b/vendor/github.com/spf13/viper/logger.go deleted file mode 100644 index 828042f29..000000000 --- a/vendor/github.com/spf13/viper/logger.go +++ /dev/null @@ -1,31 +0,0 @@ -package viper - -import ( - "context" - "log/slog" -) - -// WithLogger sets a custom logger. -func WithLogger(l *slog.Logger) Option { - return optionFunc(func(v *Viper) { - v.logger = l - }) -} - -type discardHandler struct{} - -func (n *discardHandler) Enabled(_ context.Context, _ slog.Level) bool { - return false -} - -func (n *discardHandler) Handle(_ context.Context, _ slog.Record) error { - return nil -} - -func (n *discardHandler) WithAttrs(_ []slog.Attr) slog.Handler { - return n -} - -func (n *discardHandler) WithGroup(_ string) slog.Handler { - return n -} diff --git a/vendor/github.com/spf13/viper/remote.go b/vendor/github.com/spf13/viper/remote.go deleted file mode 100644 index 46f26721d..000000000 --- a/vendor/github.com/spf13/viper/remote.go +++ /dev/null @@ -1,259 +0,0 @@ -package viper - -import ( - "bytes" - "fmt" - "io" - "reflect" - "slices" -) - -// SupportedRemoteProviders are universally supported remote providers. -var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"} - -func resetRemote() { - SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"} -} - -type remoteConfigFactory interface { - Get(rp RemoteProvider) (io.Reader, error) - Watch(rp RemoteProvider) (io.Reader, error) - WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) -} - -type RemoteResponse struct { - Value []byte - Error error -} - -// RemoteConfig is optional, see the remote package. -var RemoteConfig remoteConfigFactory - -// UnsupportedRemoteProviderError denotes encountering an unsupported remote -// provider. Currently only etcd and Consul are supported. -type UnsupportedRemoteProviderError string - -// Error returns the formatted remote provider error. -func (str UnsupportedRemoteProviderError) Error() string { - return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str)) -} - -// RemoteConfigError denotes encountering an error while trying to -// pull the configuration from the remote provider. -type RemoteConfigError string - -// Error returns the formatted remote provider error. -func (rce RemoteConfigError) Error() string { - return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) -} - -type defaultRemoteProvider struct { - provider string - endpoint string - path string - secretKeyring string -} - -func (rp defaultRemoteProvider) Provider() string { - return rp.provider -} - -func (rp defaultRemoteProvider) Endpoint() string { - return rp.endpoint -} - -func (rp defaultRemoteProvider) Path() string { - return rp.path -} - -func (rp defaultRemoteProvider) SecretKeyring() string { - return rp.secretKeyring -} - -// RemoteProvider stores the configuration necessary -// to connect to a remote key/value store. -// Optional secretKeyring to unencrypt encrypted values -// can be provided. -type RemoteProvider interface { - Provider() string - Endpoint() string - Path() string - SecretKeyring() string -} - -// AddRemoteProvider adds a remote configuration source. -// Remote Providers are searched in the order they are added. -// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported. -// endpoint is the url. etcd requires http://ip:port, consul requires ip:port, nats requires nats://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 -// you should set path to /configs and set config name (SetConfigName()) to -// "myapp". -func AddRemoteProvider(provider, endpoint, path string) error { - return v.AddRemoteProvider(provider, endpoint, path) -} - -func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { - if !slices.Contains(SupportedRemoteProviders, provider) { - return UnsupportedRemoteProviderError(provider) - } - if provider != "" && endpoint != "" { - v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) - - rp := &defaultRemoteProvider{ - endpoint: endpoint, - provider: provider, - path: path, - } - if !v.providerPathExists(rp) { - v.remoteProviders = append(v.remoteProviders, rp) - } - } - return nil -} - -// AddSecureRemoteProvider adds a remote configuration source. -// Secure Remote Providers are searched in the order they are added. -// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" 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 -// To retrieve a config file called myapp.json from /configs/myapp.json -// you should set path to /configs and set config name (SetConfigName()) to -// "myapp". -// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt. -func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { - return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) -} - -func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { - if !slices.Contains(SupportedRemoteProviders, provider) { - return UnsupportedRemoteProviderError(provider) - } - if provider != "" && endpoint != "" { - v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) - - rp := &defaultRemoteProvider{ - endpoint: endpoint, - provider: provider, - path: path, - secretKeyring: secretkeyring, - } - if !v.providerPathExists(rp) { - v.remoteProviders = append(v.remoteProviders, rp) - } - } - return nil -} - -func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool { - for _, y := range v.remoteProviders { - if reflect.DeepEqual(y, p) { - return true - } - } - return false -} - -// ReadRemoteConfig attempts to get configuration from a remote source -// and read it in the remote configuration registry. -func ReadRemoteConfig() error { return v.ReadRemoteConfig() } - -func (v *Viper) ReadRemoteConfig() error { - return v.getKeyValueConfig() -} - -func WatchRemoteConfig() error { return v.WatchRemoteConfig() } -func (v *Viper) WatchRemoteConfig() error { - return v.watchKeyValueConfig() -} - -func (v *Viper) WatchRemoteConfigOnChannel() error { - return v.watchKeyValueConfigOnChannel() -} - -// Retrieve the first found remote configuration. -func (v *Viper) getKeyValueConfig() error { - if RemoteConfig == nil { - 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 { - v.logger.Error(fmt.Errorf("get remote config: %w", err).Error()) - - continue - } - - v.kvstore = val - - return nil - } - return RemoteConfigError("No Files Found") -} - -func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) { - reader, err := RemoteConfig.Get(provider) - if err != nil { - return nil, err - } - err = v.unmarshalReader(reader, v.kvstore) - return v.kvstore, err -} - -// 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 - go func(rc <-chan *RemoteResponse) { - for { - b := <-rc - reader := bytes.NewReader(b.Value) - err := v.unmarshalReader(reader, v.kvstore) - if err != nil { - v.logger.Error(fmt.Errorf("failed to unmarshal remote config: %w", err).Error()) - } - } - }(respc) - return nil - } - return RemoteConfigError("No Files Found") -} - -// 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 - return nil - } - return RemoteConfigError("No Files Found") -} - -func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) { - reader, err := RemoteConfig.Watch(provider) - if err != nil { - return nil, err - } - err = v.unmarshalReader(reader, v.kvstore) - return v.kvstore, err -} diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go deleted file mode 100644 index d08ed4621..000000000 --- a/vendor/github.com/spf13/viper/util.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright © 2014 Steve Francia <spf@spf13.com>. -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// Viper is a application configuration system. -// It believes that applications can be configured a variety of ways -// via flags, ENVIRONMENT variables, configuration files retrieved -// from the file system, or a remote key/value store. - -package viper - -import ( - "fmt" - "log/slog" - "os" - "path/filepath" - "runtime" - "strings" - "unicode" - - "github.com/spf13/cast" -) - -// ConfigParseError denotes failing to parse configuration file. -type ConfigParseError struct { - err error -} - -// Error returns the formatted configuration error. -func (pe ConfigParseError) Error() string { - return fmt.Sprintf("While parsing config: %s", pe.err.Error()) -} - -// Unwrap returns the wrapped error. -func (pe ConfigParseError) Unwrap() error { - return pe.err -} - -// toCaseInsensitiveValue checks if the value is a map; -// if so, create a copy and lower-case the keys recursively. -func toCaseInsensitiveValue(value any) any { - switch v := value.(type) { - case map[any]any: - value = copyAndInsensitiviseMap(cast.ToStringMap(v)) - case map[string]any: - value = copyAndInsensitiviseMap(v) - } - - return value -} - -// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of -// any map it makes case insensitive. -func copyAndInsensitiviseMap(m map[string]any) map[string]any { - nm := make(map[string]any) - - for key, val := range m { - lkey := strings.ToLower(key) - switch v := val.(type) { - case map[any]any: - nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v)) - case map[string]any: - nm[lkey] = copyAndInsensitiviseMap(v) - default: - nm[lkey] = v - } - } - - return nm -} - -func insensitiviseVal(val any) any { - switch v := val.(type) { - case map[any]any: - // nested map: cast and recursively insensitivise - val = cast.ToStringMap(val) - insensitiviseMap(val.(map[string]any)) - case map[string]any: - // nested map: recursively insensitivise - insensitiviseMap(v) - case []any: - // nested array: recursively insensitivise - insensitiveArray(v) - } - return val -} - -func insensitiviseMap(m map[string]any) { - for key, val := range m { - val = insensitiviseVal(val) - lower := strings.ToLower(key) - if key != lower { - // remove old key (not lower-cased) - delete(m, key) - } - // update map - m[lower] = val - } -} - -func insensitiveArray(a []any) { - for i, val := range a { - a[i] = insensitiviseVal(val) - } -} - -func absPathify(logger *slog.Logger, inPath string) string { - logger.Info("trying to resolve absolute path", "path", inPath) - - if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) { - inPath = userHomeDir() + inPath[5:] - } - - inPath = os.ExpandEnv(inPath) - - if filepath.IsAbs(inPath) { - return filepath.Clean(inPath) - } - - p, err := filepath.Abs(inPath) - if err == nil { - return filepath.Clean(p) - } - - logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error()) - - return "" -} - -func userHomeDir() string { - if runtime.GOOS == "windows" { - home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") - if home == "" { - home = os.Getenv("USERPROFILE") - } - return home - } - return os.Getenv("HOME") -} - -func safeMul(a, b uint) uint { - c := a * b - if a > 1 && b > 1 && c/b != a { - return 0 - } - return c -} - -// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes. -func parseSizeInBytes(sizeStr string) uint { - sizeStr = strings.TrimSpace(sizeStr) - lastChar := len(sizeStr) - 1 - multiplier := uint(1) - - if lastChar > 0 { - if sizeStr[lastChar] == 'b' || sizeStr[lastChar] == 'B' { - if lastChar > 1 { - switch unicode.ToLower(rune(sizeStr[lastChar-1])) { - case 'k': - multiplier = 1 << 10 - sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) - case 'm': - multiplier = 1 << 20 - sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) - case 'g': - multiplier = 1 << 30 - sizeStr = strings.TrimSpace(sizeStr[:lastChar-1]) - default: - multiplier = 1 - sizeStr = strings.TrimSpace(sizeStr[:lastChar]) - } - } - } - } - - size := max(cast.ToInt(sizeStr), 0) - - return safeMul(uint(size), multiplier) -} - -// deepSearch scans deep maps, following the key indexes listed in the -// sequence "path". -// The last value is expected to be another map, and is returned. -// -// In case intermediate keys do not exist, or map to a non-map value, -// a new map is created and inserted, and the search continues from there: -// the initial map "m" may be modified! -func deepSearch(m map[string]any, path []string) map[string]any { - for _, k := range path { - m2, ok := m[k] - if !ok { - // intermediate key does not exist - // => create it and continue from there - m3 := make(map[string]any) - m[k] = m3 - m = m3 - continue - } - m3, ok := m2.(map[string]any) - if !ok { - // intermediate key is a value - // => replace with a new map - m3 = make(map[string]any) - m[k] = m3 - } - // continue search from here - m = m3 - } - return m -} diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go deleted file mode 100644 index 34a94798b..000000000 --- a/vendor/github.com/spf13/viper/viper.go +++ /dev/null @@ -1,2077 +0,0 @@ -// Copyright © 2014 Steve Francia <spf@spf13.com>. -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// Viper is an application configuration system. -// It believes that applications can be configured a variety of ways -// via flags, ENVIRONMENT variables, configuration files retrieved -// from the file system, or a remote key/value store. - -// Each item takes precedence over the item below it: - -// overrides -// flag -// env -// config -// key/value store -// default - -package viper - -import ( - "bytes" - "encoding/csv" - "errors" - "fmt" - "io" - "log/slog" - "os" - "path/filepath" - "reflect" - "slices" - "strconv" - "strings" - "sync" - "time" - - "github.com/fsnotify/fsnotify" - "github.com/go-viper/mapstructure/v2" - "github.com/spf13/afero" - "github.com/spf13/cast" - "github.com/spf13/pflag" - - "github.com/spf13/viper/internal/features" -) - -// ConfigMarshalError happens when failing to marshal the configuration. -type ConfigMarshalError struct { - err error -} - -// Error returns the formatted configuration error. -func (e ConfigMarshalError) Error() string { - return fmt.Sprintf("While marshaling config: %s", e.err.Error()) -} - -var v *Viper - -func init() { - v = New() -} - -// UnsupportedConfigError denotes encountering an unsupported -// configuration filetype. -type UnsupportedConfigError string - -// Error returns the formatted configuration error. -func (str UnsupportedConfigError) Error() string { - return fmt.Sprintf("Unsupported Config Type %q", string(str)) -} - -// ConfigFileNotFoundError denotes failing to find configuration file. -type ConfigFileNotFoundError struct { - name, locations string -} - -// Error returns the formatted configuration error. -func (fnfe ConfigFileNotFoundError) Error() string { - return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) -} - -// ConfigFileAlreadyExistsError denotes failure to write new configuration file. -type ConfigFileAlreadyExistsError string - -// Error returns the formatted error when configuration already exists. -func (faee ConfigFileAlreadyExistsError) Error() string { - return fmt.Sprintf("Config File %q Already Exists", string(faee)) -} - -// A DecoderConfigOption can be passed to viper.Unmarshal to configure -// mapstructure.DecoderConfig options. -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(","), -// ) -func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption { - return func(c *mapstructure.DecoderConfig) { - c.DecodeHook = hook - } -} - -// Viper is a prioritized configuration registry. It -// maintains a set of configuration sources, fetches -// values to populate those, and provides them according -// to the source's priority. -// The priority of the sources is the following: -// 1. overrides -// 2. flags -// 3. env. variables -// 4. config file -// 5. key/value store -// 6. defaults -// -// 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" -// } -// -// The resulting config will have the following values: -// -// { -// "secret": "somesecretkey", -// "user": "root", -// "endpoint": "https://localhost" -// } -// -// Note: Vipers are not safe for concurrent Get() and Set() operations. -type Viper struct { - // Delimiter that separates a list of keys - // used to access a nested value in one go - keyDelim string - - // A set of paths to look for the config file in - configPaths []string - - // The filesystem to read config from. - fs afero.Fs - - finder Finder - - // A set of remote providers to search for the configuration - remoteProviders []*defaultRemoteProvider - - // Name of file to look for inside the path - configName string - configFile string - configType string - configPermissions os.FileMode - envPrefix string - - automaticEnvApplied bool - envKeyReplacer StringReplacer - allowEmptyEnv bool - - parents []string - config map[string]any - override map[string]any - defaults map[string]any - kvstore map[string]any - pflags map[string]FlagValue - env map[string][]string - aliases map[string]string - typeByDefValue bool - - onConfigChange func(fsnotify.Event) - - logger *slog.Logger - - encoderRegistry EncoderRegistry - decoderRegistry DecoderRegistry - - decodeHook mapstructure.DecodeHookFunc - - experimentalFinder bool - experimentalBindStruct bool -} - -// New returns an initialized Viper instance. -func New() *Viper { - v := new(Viper) - v.keyDelim = "." - v.configName = "config" - v.configPermissions = os.FileMode(0o644) - v.fs = afero.NewOsFs() - v.config = make(map[string]any) - v.parents = []string{} - v.override = make(map[string]any) - v.defaults = make(map[string]any) - v.kvstore = make(map[string]any) - v.pflags = make(map[string]FlagValue) - v.env = make(map[string][]string) - v.aliases = make(map[string]string) - v.typeByDefValue = false - v.logger = slog.New(&discardHandler{}) - - codecRegistry := NewCodecRegistry() - - v.encoderRegistry = codecRegistry - v.decoderRegistry = codecRegistry - - v.experimentalFinder = features.Finder - v.experimentalBindStruct = features.BindStruct - - return v -} - -// Option configures Viper using the functional options paradigm popularized by Rob Pike and Dave Cheney. -// If you're unfamiliar with this style, -// see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and -// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis. -type Option interface { - apply(v *Viper) -} - -type optionFunc func(v *Viper) - -func (fn optionFunc) apply(v *Viper) { - fn(v) -} - -// KeyDelimiter sets the delimiter used for determining key parts. -// By default it's value is ".". -func KeyDelimiter(d string) Option { - return optionFunc(func(v *Viper) { - v.keyDelim = d - }) -} - -// StringReplacer applies a set of replacements to a string. -type StringReplacer interface { - // Replace returns a copy of s with all replacements performed. - Replace(s string) string -} - -// EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys. -func EnvKeyReplacer(r StringReplacer) Option { - return optionFunc(func(v *Viper) { - if r == nil { - return - } - - v.envKeyReplacer = r - }) -} - -// WithDecodeHook sets a default decode hook for mapstructure. -func WithDecodeHook(h mapstructure.DecodeHookFunc) Option { - return optionFunc(func(v *Viper) { - if h == nil { - return - } - - v.decodeHook = h - }) -} - -// NewWithOptions creates a new Viper instance. -func NewWithOptions(opts ...Option) *Viper { - v := New() - - for _, opt := range opts { - opt.apply(v) - } - - return v -} - -// SetOptions sets the options on the global Viper instance. -// -// Be careful when using this function: subsequent calls may override options you set. -// It's always better to use a local Viper instance. -func SetOptions(opts ...Option) { - for _, opt := range opts { - opt.apply(v) - } -} - -// Reset is intended for testing, will reset all to default settings. -// In the public interface for the viper package so applications -// can use it in their testing as well. -func Reset() { - v = New() - SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} - - resetRemote() -} - -// SupportedExts are universally supported extensions. -var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} - -// OnConfigChange sets the event handler that is called when a config file changes. -func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } - -// OnConfigChange sets the event handler that is called when a config file changes. -func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { - v.onConfigChange = run -} - -// WatchConfig starts watching a config file for changes. -func WatchConfig() { v.WatchConfig() } - -// WatchConfig starts watching a config file for changes. -func (v *Viper) WatchConfig() { - initWG := sync.WaitGroup{} - initWG.Add(1) - go func() { - watcher, err := fsnotify.NewWatcher() - if err != nil { - v.logger.Error(fmt.Sprintf("failed to create watcher: %s", err)) - os.Exit(1) - } - defer watcher.Close() - // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way - filename, err := v.getConfigFile() - if err != nil { - v.logger.Error(fmt.Sprintf("get config file: %s", err)) - initWG.Done() - return - } - - configFile := filepath.Clean(filename) - configDir, _ := filepath.Split(configFile) - realConfigFile, _ := filepath.EvalSymlinks(filename) - - eventsWG := sync.WaitGroup{} - eventsWG.Add(1) - go func() { - for { - select { - case event, ok := <-watcher.Events: - if !ok { // 'Events' channel is closed - eventsWG.Done() - return - } - currentConfigFile, _ := filepath.EvalSymlinks(filename) - // we only care about the config file with the following cases: - // 1 - if the config file was modified or created - // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) - if (filepath.Clean(event.Name) == configFile && - (event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) || - (currentConfigFile != "" && currentConfigFile != realConfigFile) { - realConfigFile = currentConfigFile - err := v.ReadInConfig() - if err != nil { - v.logger.Error(fmt.Sprintf("read config file: %s", err)) - } - if v.onConfigChange != nil { - v.onConfigChange(event) - } - } else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) { - eventsWG.Done() - return - } - - case err, ok := <-watcher.Errors: - if ok { // 'Errors' channel is not closed - v.logger.Error(fmt.Sprintf("watcher error: %s", err)) - } - eventsWG.Done() - return - } - } - }() - err = watcher.Add(configDir) - if err != nil { - v.logger.Error(fmt.Sprintf("failed to add watcher: %s", err)) - initWG.Done() - return - } - initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on... - eventsWG.Wait() // now, wait for event loop to end in this go-routine... - }() - initWG.Wait() // make sure that the go routine above fully ended before returning -} - -// SetConfigFile explicitly defines the path, name and extension of the config file. -// Viper will use this and not check any of the config paths. -func SetConfigFile(in string) { v.SetConfigFile(in) } - -func (v *Viper) SetConfigFile(in string) { - if in != "" { - v.configFile = in - } -} - -// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use. -// E.g. if your prefix is "spf", the env registry will look for env -// variables that start with "SPF_". -func SetEnvPrefix(in string) { v.SetEnvPrefix(in) } - -func (v *Viper) SetEnvPrefix(in string) { - if in != "" { - v.envPrefix = in - } -} - -func GetEnvPrefix() string { return v.GetEnvPrefix() } - -func (v *Viper) GetEnvPrefix() string { - return v.envPrefix -} - -func (v *Viper) mergeWithEnvPrefix(in string) string { - if v.envPrefix != "" { - return strings.ToUpper(v.envPrefix + "_" + in) - } - - return strings.ToUpper(in) -} - -// AllowEmptyEnv tells Viper to consider set, -// but empty environment variables as valid values instead of falling back. -// For backward compatibility reasons this is false by default. -func AllowEmptyEnv(allowEmptyEnv bool) { v.AllowEmptyEnv(allowEmptyEnv) } - -func (v *Viper) AllowEmptyEnv(allowEmptyEnv bool) { - v.allowEmptyEnv = allowEmptyEnv -} - -// TODO: should getEnv logic be moved into find(). Can generalize the use of -// rewriting keys many things, Ex: Get('someKey') -> some_key -// (camel case to snake case for JSON keys perhaps) - -// getEnv is a wrapper around os.Getenv which replaces characters in the original -// key. This allows env vars which have different keys than the config object -// keys. -func (v *Viper) getEnv(key string) (string, bool) { - if v.envKeyReplacer != nil { - key = v.envKeyReplacer.Replace(key) - } - - val, ok := os.LookupEnv(key) - - return val, ok && (v.allowEmptyEnv || val != "") -} - -// ConfigFileUsed returns the file used to populate the config registry. -func ConfigFileUsed() string { return v.ConfigFileUsed() } -func (v *Viper) ConfigFileUsed() string { return v.configFile } - -// AddConfigPath adds a path for Viper to search for the config file in. -// Can be called multiple times to define multiple search paths. -func AddConfigPath(in string) { v.AddConfigPath(in) } - -func (v *Viper) AddConfigPath(in string) { - if v.finder != nil { - v.logger.Warn("ineffective call to function: custom finder takes precedence", slog.String("function", "AddConfigPath")) - } - - if in != "" { - absin := absPathify(v.logger, in) - - v.logger.Info("adding path to search paths", "path", absin) - if !slices.Contains(v.configPaths, absin) { - v.configPaths = append(v.configPaths, absin) - } - } -} - -// searchMap recursively searches for a value for path in source map. -// Returns nil if not found. -// Note: This assumes that the path entries and map keys are lower cased. -func (v *Viper) searchMap(source map[string]any, path []string) any { - if len(path) == 0 { - return source - } - - next, ok := source[path[0]] - if ok { - // Fast path - if len(path) == 1 { - return next - } - - // Nested case - switch next := next.(type) { - case map[any]any: - return v.searchMap(cast.ToStringMap(next), path[1:]) - case map[string]any: - // Type assertion is safe here since it is only reached - // if the type of `next` is the same as the type being asserted - return v.searchMap(next, path[1:]) - default: - // got a value but nested key expected, return "nil" for not found - return nil - } - } - return nil -} - -// searchIndexableWithPathPrefixes recursively searches for a value for path in source map/slice. -// -// While searchMap() considers each path element as a single map key or slice index, this -// function searches for, and prioritizes, merged path elements. -// e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar" -// is also defined, this latter value is returned for path ["foo", "bar"]. -// -// This should be useful only at config level (other maps may not contain dots -// in their keys). -// -// Note: This assumes that the path entries and map keys are lower cased. -func (v *Viper) searchIndexableWithPathPrefixes(source any, path []string) any { - if len(path) == 0 { - return source - } - - // search for path prefixes, starting from the longest one - for i := len(path); i > 0; i-- { - prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim)) - - var val any - switch sourceIndexable := source.(type) { - case []any: - val = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path) - case map[string]any: - val = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path) - } - if val != nil { - return val - } - } - - // not found - return nil -} - -// searchSliceWithPathPrefixes searches for a value for path in sourceSlice -// -// This function is part of the searchIndexableWithPathPrefixes recurring search and -// should not be called directly from functions other than searchIndexableWithPathPrefixes. -func (v *Viper) searchSliceWithPathPrefixes( - sourceSlice []any, - prefixKey string, - pathIndex int, - path []string, -) any { - // if the prefixKey is not a number or it is out of bounds of the slice - index, err := strconv.Atoi(prefixKey) - if err != nil || len(sourceSlice) <= index { - return nil - } - - next := sourceSlice[index] - - // Fast path - if pathIndex == len(path) { - return next - } - - switch n := next.(type) { - case map[any]any: - return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:]) - case map[string]any, []any: - return v.searchIndexableWithPathPrefixes(n, path[pathIndex:]) - default: - // got a value but nested key expected, do nothing and look for next prefix - } - - // not found - return nil -} - -// searchMapWithPathPrefixes searches for a value for path in sourceMap -// -// This function is part of the searchIndexableWithPathPrefixes recurring search and -// should not be called directly from functions other than searchIndexableWithPathPrefixes. -func (v *Viper) searchMapWithPathPrefixes( - sourceMap map[string]any, - prefixKey string, - pathIndex int, - path []string, -) any { - next, ok := sourceMap[prefixKey] - if !ok { - return nil - } - - // Fast path - if pathIndex == len(path) { - return next - } - - // Nested case - switch n := next.(type) { - case map[any]any: - return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:]) - case map[string]any, []any: - return v.searchIndexableWithPathPrefixes(n, path[pathIndex:]) - default: - // got a value but nested key expected, do nothing and look for next prefix - } - - // not found - return nil -} - -// 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 -func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]any) string { - var parentVal any - for i := 1; i < len(path); i++ { - parentVal = v.searchMap(m, path[0:i]) - if parentVal == nil { - // not found, no need to add more path elements - return "" - } - switch parentVal.(type) { - case map[any]any: - continue - case map[string]any: - continue - default: - // parentVal is a regular value which shadows "path" - return strings.Join(path[0:i], v.keyDelim) - } - } - return "" -} - -// 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 -func (v *Viper) isPathShadowedInFlatMap(path []string, mi any) string { - // unify input map - var m map[string]interface{} - switch miv := mi.(type) { - case map[string]string: - m = castMapStringToMapInterface(miv) - case map[string]FlagValue: - m = castMapFlagToMapInterface(miv) - default: - return "" - } - - // scan paths - var parentKey string - for i := 1; i < len(path); i++ { - parentKey = strings.Join(path[0:i], v.keyDelim) - if _, ok := m[parentKey]; ok { - return parentKey - } - } - return "" -} - -// 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 -func (v *Viper) isPathShadowedInAutoEnv(path []string) string { - var parentKey string - for i := 1; i < len(path); i++ { - parentKey = strings.Join(path[0:i], v.keyDelim) - if _, ok := v.getEnv(v.mergeWithEnvPrefix(parentKey)); ok { - return parentKey - } - } - return "" -} - -// SetTypeByDefaultValue enables or disables the inference of a key value's -// type when the Get function is used based upon a key's default value as -// opposed to the value returned based on the normal fetch logic. -// -// For example, if a key has a default value of []string{} and the same key -// is set via an environment variable to "a b c", a call to the Get function -// 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"} -// -// Otherwise the Get function would return: -// -// "a b c" -func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) } - -func (v *Viper) SetTypeByDefaultValue(enable bool) { - v.typeByDefValue = enable -} - -// GetViper gets the global Viper instance. -func GetViper() *Viper { - return v -} - -// Get can retrieve any value given the key to use. -// Get is case-insensitive for a key. -// Get has the behavior of returning the value associated with the first -// place from where it is set. Viper will check in the following order: -// override, flag, env, config file, key/value store, default -// -// Get returns an interface. For a specific value use one of the Get____ methods. -func Get(key string) any { return v.Get(key) } - -func (v *Viper) Get(key string) any { - lcaseKey := strings.ToLower(key) - val := v.find(lcaseKey, true) - if val == nil { - return nil - } - - if v.typeByDefValue { - // TODO(bep) this branch isn't covered by a single test. - valType := val - path := strings.Split(lcaseKey, v.keyDelim) - defVal := v.searchMap(v.defaults, path) - if defVal != nil { - valType = defVal - } - - switch valType.(type) { - case bool: - return cast.ToBool(val) - case string: - return cast.ToString(val) - case int32, int16, int8, int: - return cast.ToInt(val) - case uint: - return cast.ToUint(val) - case uint32: - return cast.ToUint32(val) - case uint64: - return cast.ToUint64(val) - case int64: - return cast.ToInt64(val) - case float64, float32: - return cast.ToFloat64(val) - case time.Time: - return cast.ToTime(val) - case time.Duration: - return cast.ToDuration(val) - case []string: - return cast.ToStringSlice(val) - case []int: - return cast.ToIntSlice(val) - case []time.Duration: - return cast.ToDurationSlice(val) - } - } - - return val -} - -// Sub returns new Viper instance representing a sub tree of this instance. -// Sub is case-insensitive for a key. -func Sub(key string) *Viper { return v.Sub(key) } - -func (v *Viper) Sub(key string) *Viper { - subv := New() - data := v.Get(key) - if data == nil { - return nil - } - - if reflect.TypeOf(data).Kind() == reflect.Map { - subv.parents = append([]string(nil), v.parents...) - subv.parents = append(subv.parents, strings.ToLower(key)) - subv.automaticEnvApplied = v.automaticEnvApplied - subv.envPrefix = v.envPrefix - subv.envKeyReplacer = v.envKeyReplacer - subv.keyDelim = v.keyDelim - subv.config = cast.ToStringMap(data) - return subv - } - return nil -} - -// GetString returns the value associated with the key as a string. -func GetString(key string) string { return v.GetString(key) } - -func (v *Viper) GetString(key string) string { - return cast.ToString(v.Get(key)) -} - -// GetBool returns the value associated with the key as a boolean. -func GetBool(key string) bool { return v.GetBool(key) } - -func (v *Viper) GetBool(key string) bool { - return cast.ToBool(v.Get(key)) -} - -// GetInt returns the value associated with the key as an integer. -func GetInt(key string) int { return v.GetInt(key) } - -func (v *Viper) GetInt(key string) int { - return cast.ToInt(v.Get(key)) -} - -// GetInt32 returns the value associated with the key as an integer. -func GetInt32(key string) int32 { return v.GetInt32(key) } - -func (v *Viper) GetInt32(key string) int32 { - return cast.ToInt32(v.Get(key)) -} - -// GetInt64 returns the value associated with the key as an integer. -func GetInt64(key string) int64 { return v.GetInt64(key) } - -func (v *Viper) GetInt64(key string) int64 { - return cast.ToInt64(v.Get(key)) -} - -// GetUint8 returns the value associated with the key as an unsigned integer. -func GetUint8(key string) uint8 { return v.GetUint8(key) } - -func (v *Viper) GetUint8(key string) uint8 { - return cast.ToUint8(v.Get(key)) -} - -// GetUint returns the value associated with the key as an unsigned integer. -func GetUint(key string) uint { return v.GetUint(key) } - -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) } - -func (v *Viper) GetUint32(key string) uint32 { - return cast.ToUint32(v.Get(key)) -} - -// GetUint64 returns the value associated with the key as an unsigned integer. -func GetUint64(key string) uint64 { return v.GetUint64(key) } - -func (v *Viper) GetUint64(key string) uint64 { - return cast.ToUint64(v.Get(key)) -} - -// GetFloat64 returns the value associated with the key as a float64. -func GetFloat64(key string) float64 { return v.GetFloat64(key) } - -func (v *Viper) GetFloat64(key string) float64 { - return cast.ToFloat64(v.Get(key)) -} - -// GetTime returns the value associated with the key as time. -func GetTime(key string) time.Time { return v.GetTime(key) } - -func (v *Viper) GetTime(key string) time.Time { - return cast.ToTime(v.Get(key)) -} - -// GetDuration returns the value associated with the key as a duration. -func GetDuration(key string) time.Duration { return v.GetDuration(key) } - -func (v *Viper) GetDuration(key string) time.Duration { - return cast.ToDuration(v.Get(key)) -} - -// GetIntSlice returns the value associated with the key as a slice of int values. -func GetIntSlice(key string) []int { return v.GetIntSlice(key) } - -func (v *Viper) GetIntSlice(key string) []int { - return cast.ToIntSlice(v.Get(key)) -} - -// GetStringSlice returns the value associated with the key as a slice of strings. -func GetStringSlice(key string) []string { return v.GetStringSlice(key) } - -func (v *Viper) GetStringSlice(key string) []string { - return cast.ToStringSlice(v.Get(key)) -} - -// GetStringMap returns the value associated with the key as a map of interfaces. -func GetStringMap(key string) map[string]any { return v.GetStringMap(key) } - -func (v *Viper) GetStringMap(key string) map[string]any { - return cast.ToStringMap(v.Get(key)) -} - -// GetStringMapString returns the value associated with the key as a map of strings. -func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) } - -func (v *Viper) GetStringMapString(key string) map[string]string { - return cast.ToStringMapString(v.Get(key)) -} - -// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. -func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) } - -func (v *Viper) GetStringMapStringSlice(key string) map[string][]string { - return cast.ToStringMapStringSlice(v.Get(key)) -} - -// GetSizeInBytes returns the size of the value associated with the given key -// in bytes. -func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) } - -func (v *Viper) GetSizeInBytes(key string) uint { - sizeStr := cast.ToString(v.Get(key)) - return parseSizeInBytes(sizeStr) -} - -// UnmarshalKey takes a single key and unmarshals it into a Struct. -func UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error { - return v.UnmarshalKey(key, rawVal, opts...) -} - -func (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error { - return decode(v.Get(key), v.defaultDecoderConfig(rawVal, opts...)) -} - -// Unmarshal unmarshals the config into a Struct. Make sure that the tags -// on the fields of the structure are properly set. -func Unmarshal(rawVal any, opts ...DecoderConfigOption) error { - return v.Unmarshal(rawVal, opts...) -} - -func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error { - keys := v.AllKeys() - - if v.experimentalBindStruct { - // TODO: make this optional? - structKeys, err := v.decodeStructKeys(rawVal, opts...) - if err != nil { - return err - } - - keys = append(keys, structKeys...) - } - - // TODO: struct keys should be enough? - return decode(v.getSettings(keys), v.defaultDecoderConfig(rawVal, opts...)) -} - -func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) { - var structKeyMap map[string]any - - err := decode(input, v.defaultDecoderConfig(&structKeyMap, opts...)) - if err != nil { - return nil, err - } - - flattenedStructKeyMap := v.flattenAndMergeMap(map[string]bool{}, structKeyMap, "") - - r := make([]string, 0, len(flattenedStructKeyMap)) - for v := range flattenedStructKeyMap { - r = append(r, v) - } - - return r, nil -} - -// defaultDecoderConfig returns default mapstructure.DecoderConfig with support -// of time.Duration values & string slices. -func (v *Viper) defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { - decodeHook := v.decodeHook - if decodeHook == nil { - decodeHook = mapstructure.ComposeDecodeHookFunc( - mapstructure.StringToTimeDurationHookFunc(), - // mapstructure.StringToSliceHookFunc(","), - stringToWeakSliceHookFunc(","), - ) - } - - c := &mapstructure.DecoderConfig{ - Metadata: nil, - WeaklyTypedInput: true, - DecodeHook: decodeHook, - } - - for _, opt := range opts { - opt(c) - } - - // Do not allow overwriting the output - c.Result = output - - return c -} - -// As of mapstructure v2.0.0 StringToSliceHookFunc checks if the return type is a string slice. -// This function removes that check. -// TODO: implement a function that checks if the value can be converted to the return type and use it instead. -func stringToWeakSliceHookFunc(sep string) mapstructure.DecodeHookFunc { - return func( - f reflect.Type, - t reflect.Type, - data interface{}, - ) (interface{}, error) { - if f.Kind() != reflect.String || t.Kind() != reflect.Slice { - return data, nil - } - - raw := data.(string) - if raw == "" { - return []string{}, nil - } - - return strings.Split(raw, sep), nil - } -} - -// decode is a wrapper around mapstructure.Decode that mimics the WeakDecode functionality. -func decode(input any, config *mapstructure.DecoderConfig) error { - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return err - } - return decoder.Decode(input) -} - -// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent -// in the destination struct. -func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error { - return v.UnmarshalExact(rawVal, opts...) -} - -func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error { - config := v.defaultDecoderConfig(rawVal, opts...) - config.ErrorUnused = true - - keys := v.AllKeys() - - if v.experimentalBindStruct { - // TODO: make this optional? - structKeys, err := v.decodeStructKeys(rawVal, opts...) - if err != nil { - return err - } - - keys = append(keys, structKeys...) - } - - // TODO: struct keys should be enough? - return decode(v.getSettings(keys), config) -} - -// BindPFlags binds a full flag set to the configuration, using each flag's long -// name as the config key. -func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) } - -func (v *Viper) BindPFlags(flags *pflag.FlagSet) error { - return v.BindFlagValues(pflagValueSet{flags}) -} - -// 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")) -func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) } - -func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error { - if flag == nil { - return fmt.Errorf("flag for %q is nil", key) - } - return v.BindFlagValue(key, pflagValue{flag}) -} - -// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long -// name as the config key. -func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) } - -func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { - flags.VisitAll(func(flag FlagValue) { - if err = v.BindFlagValue(flag.Name(), flag); err != nil { - return - } - }) - return nil -} - -// BindFlagValue binds a specific key to a FlagValue. -func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) } - -func (v *Viper) BindFlagValue(key string, flag FlagValue) error { - if flag == nil { - return fmt.Errorf("flag for %q is nil", key) - } - v.pflags[strings.ToLower(key)] = flag - return nil -} - -// BindEnv binds a Viper key to a ENV variable. -// ENV variables are case sensitive. -// If only a key is provided, it will use the env key matching the key, uppercased. -// If more arguments are provided, they will represent the env variable names that -// should bind to this key and will be taken in the specified order. -// EnvPrefix will be used when set when env name is not provided. -func BindEnv(input ...string) error { return v.BindEnv(input...) } - -func (v *Viper) BindEnv(input ...string) error { - if len(input) == 0 { - return fmt.Errorf("missing key to bind to") - } - - key := strings.ToLower(input[0]) - - if len(input) == 1 { - v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key)) - } else { - v.env[key] = append(v.env[key], input[1:]...) - } - - return nil -} - -// MustBindEnv wraps BindEnv in a panic. -// If there is an error binding an environment variable, MustBindEnv will -// panic. -func MustBindEnv(input ...string) { v.MustBindEnv(input...) } - -func (v *Viper) MustBindEnv(input ...string) { - if err := v.BindEnv(input...); err != nil { - panic(fmt.Sprintf("error while binding environment variable: %v", err)) - } -} - -// Given a key, find the value. -// -// Viper will check to see if an alias exists first. -// Viper will then check in the following order: -// flag, env, config file, key/value store. -// Lastly, if no value was found and flagDefault is true, and if the key -// corresponds to a flag, the flag's default value is returned. -// -// Note: this assumes a lower-cased key given. -func (v *Viper) find(lcaseKey string, flagDefault bool) any { - var ( - val any - exists bool - path = strings.Split(lcaseKey, v.keyDelim) - nested = len(path) > 1 - ) - - // compute the path through the nested maps to the nested value - if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" { - return nil - } - - // if the requested key is an alias, then return the proper key - lcaseKey = v.realKey(lcaseKey) - path = strings.Split(lcaseKey, v.keyDelim) - nested = len(path) > 1 - - // Set() override first - val = v.searchMap(v.override, path) - if val != nil { - return val - } - if nested && v.isPathShadowedInDeepMap(path, v.override) != "" { - return nil - } - - // PFlag override next - flag, exists := v.pflags[lcaseKey] - if exists && flag.HasChanged() { - switch flag.ValueType() { - case "int", "int8", "int16", "int32", "int64": - return cast.ToInt(flag.ValueString()) - case "bool": - return cast.ToBool(flag.ValueString()) - case "stringSlice", "stringArray": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return res - case "boolSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToBoolSlice(res) - case "intSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToIntSlice(res) - case "uintSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToUintSlice(res) - case "float64Slice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToFloat64Slice(res) - case "durationSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - slice := strings.Split(s, ",") - return cast.ToDurationSlice(slice) - case "stringToString": - return stringToStringConv(flag.ValueString()) - case "stringToInt": - return stringToIntConv(flag.ValueString()) - default: - return flag.ValueString() - } - } - if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" { - return nil - } - - // Env override next - if v.automaticEnvApplied { - envKey := strings.Join(append(v.parents, lcaseKey), ".") - // even if it hasn't been registered, if automaticEnv is used, - // check any Get request - if val, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok { - return val - } - if nested && v.isPathShadowedInAutoEnv(path) != "" { - return nil - } - } - envkeys, exists := v.env[lcaseKey] - if exists { - for _, envkey := range envkeys { - if val, ok := v.getEnv(envkey); ok { - return val - } - } - } - if nested && v.isPathShadowedInFlatMap(path, v.env) != "" { - return nil - } - - // Config file next - val = v.searchIndexableWithPathPrefixes(v.config, path) - if val != nil { - return val - } - if nested && v.isPathShadowedInDeepMap(path, v.config) != "" { - return nil - } - - // K/V store next - val = v.searchMap(v.kvstore, path) - if val != nil { - return val - } - if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" { - return nil - } - - // Default next - val = v.searchMap(v.defaults, path) - if val != nil { - return val - } - if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" { - return nil - } - - if flagDefault { - // last chance: if no value is found and a flag does exist for the key, - // get the flag's default value even if the flag's value has not been set. - if flag, exists := v.pflags[lcaseKey]; exists { - switch flag.ValueType() { - case "int", "int8", "int16", "int32", "int64": - return cast.ToInt(flag.ValueString()) - case "bool": - return cast.ToBool(flag.ValueString()) - case "stringSlice", "stringArray": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return res - case "boolSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToBoolSlice(res) - case "intSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToIntSlice(res) - case "uintSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToUintSlice(res) - case "float64Slice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return cast.ToFloat64Slice(res) - case "stringToString": - return stringToStringConv(flag.ValueString()) - case "stringToInt": - return stringToIntConv(flag.ValueString()) - case "durationSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - slice := strings.Split(s, ",") - return cast.ToDurationSlice(slice) - default: - return flag.ValueString() - } - } - // last item, no need to check shadowing - } - - return nil -} - -func readAsCSV(val string) ([]string, error) { - if val == "" { - return []string{}, nil - } - stringReader := strings.NewReader(val) - csvReader := csv.NewReader(stringReader) - return csvReader.Read() -} - -// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79 -// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap. -func stringToStringConv(val string) any { - val = strings.Trim(val, "[]") - // An empty string would cause an empty map - if val == "" { - return map[string]any{} - } - r := csv.NewReader(strings.NewReader(val)) - ss, err := r.Read() - if err != nil { - return nil - } - out := make(map[string]any, len(ss)) - for _, pair := range ss { - k, vv, found := strings.Cut(pair, "=") - if !found { - return nil - } - out[k] = vv - } - return out -} - -// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/string_to_int.go#L68 -// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap. -func stringToIntConv(val string) any { - val = strings.Trim(val, "[]") - // An empty string would cause an empty map - if val == "" { - return map[string]any{} - } - ss := strings.Split(val, ",") - out := make(map[string]any, len(ss)) - for _, pair := range ss { - k, vv, found := strings.Cut(pair, "=") - if !found { - return nil - } - var err error - out[k], err = strconv.Atoi(vv) - if err != nil { - return nil - } - } - return out -} - -// IsSet checks to see if the key has been set in any of the data locations. -// IsSet is case-insensitive for a key. -func IsSet(key string) bool { return v.IsSet(key) } - -func (v *Viper) IsSet(key string) bool { - lcaseKey := strings.ToLower(key) - val := v.find(lcaseKey, false) - return val != nil -} - -// AutomaticEnv makes Viper check if environment variables match any of the existing keys -// (config, default or flags). If matching env vars are found, they are loaded into Viper. -func AutomaticEnv() { v.AutomaticEnv() } - -func (v *Viper) AutomaticEnv() { - v.automaticEnvApplied = true -} - -// SetEnvKeyReplacer sets the strings.Replacer on the viper object -// Useful for mapping an environmental variable to a key that does -// not match it. -func SetEnvKeyReplacer(r *strings.Replacer) { v.SetEnvKeyReplacer(r) } - -func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) { - v.envKeyReplacer = r -} - -// RegisterAlias creates an alias that provides another accessor for the same key. -// This enables one to change a name without breaking the application. -func RegisterAlias(alias, key string) { v.RegisterAlias(alias, key) } - -func (v *Viper) RegisterAlias(alias, key string) { - v.registerAlias(alias, strings.ToLower(key)) -} - -func (v *Viper) registerAlias(alias, key string) { - alias = strings.ToLower(alias) - if alias != key && alias != v.realKey(key) { - _, exists := v.aliases[alias] - - if !exists { - // if we alias something that exists in one of the maps to another - // name, we'll never be able to get that value using the original - // name, so move the config value to the new realkey. - if val, ok := v.config[alias]; ok { - delete(v.config, alias) - v.config[key] = val - } - if val, ok := v.kvstore[alias]; ok { - delete(v.kvstore, alias) - v.kvstore[key] = val - } - if val, ok := v.defaults[alias]; ok { - delete(v.defaults, alias) - v.defaults[key] = val - } - if val, ok := v.override[alias]; ok { - delete(v.override, alias) - v.override[key] = val - } - v.aliases[alias] = key - } - } else { - v.logger.Warn("creating circular reference alias", "alias", alias, "key", key, "real_key", v.realKey(key)) - } -} - -func (v *Viper) realKey(key string) string { - newkey, exists := v.aliases[key] - if exists { - v.logger.Debug("key is an alias", "alias", key, "to", newkey) - - return v.realKey(newkey) - } - return key -} - -// InConfig checks to see if the given key (or an alias) is in the config file. -func InConfig(key string) bool { return v.InConfig(key) } - -func (v *Viper) InConfig(key string) bool { - lcaseKey := strings.ToLower(key) - - // if the requested key is an alias, then return the proper key - lcaseKey = v.realKey(lcaseKey) - path := strings.Split(lcaseKey, v.keyDelim) - - return v.searchIndexableWithPathPrefixes(v.config, path) != nil -} - -// SetDefault sets the default value for this key. -// SetDefault is case-insensitive for a key. -// Default only used when no value is provided by the user via flag, config or ENV. -func SetDefault(key string, value any) { v.SetDefault(key, value) } - -func (v *Viper) SetDefault(key string, value any) { - // If alias passed in, then set the proper default - key = v.realKey(strings.ToLower(key)) - value = toCaseInsensitiveValue(value) - - path := strings.Split(key, v.keyDelim) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(v.defaults, path[0:len(path)-1]) - - // set innermost value - deepestMap[lastKey] = value -} - -// Set sets the value for the key in the override register. -// Set is case-insensitive for a key. -// Will be used instead of values obtained via -// flags, config file, ENV, default, or key/value store. -func Set(key string, value any) { v.Set(key, value) } - -func (v *Viper) Set(key string, value any) { - // If alias passed in, then set the proper override - key = v.realKey(strings.ToLower(key)) - value = toCaseInsensitiveValue(value) - - path := strings.Split(key, v.keyDelim) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(v.override, path[0:len(path)-1]) - - // set innermost value - deepestMap[lastKey] = value -} - -// ReadInConfig will discover and load the configuration file from disk -// and key/value stores, searching in one of the defined paths. -func ReadInConfig() error { return v.ReadInConfig() } - -func (v *Viper) ReadInConfig() error { - v.logger.Info("attempting to read in config file") - filename, err := v.getConfigFile() - if err != nil { - return err - } - - if !slices.Contains(SupportedExts, v.getConfigType()) { - return UnsupportedConfigError(v.getConfigType()) - } - - v.logger.Debug("reading file", "file", filename) - file, err := afero.ReadFile(v.fs, filename) - if err != nil { - return err - } - - config := make(map[string]any) - - err = v.unmarshalReader(bytes.NewReader(file), config) - if err != nil { - return err - } - - v.config = config - return nil -} - -// MergeInConfig merges a new configuration with an existing config. -func MergeInConfig() error { return v.MergeInConfig() } - -func (v *Viper) MergeInConfig() error { - v.logger.Info("attempting to merge in config file") - filename, err := v.getConfigFile() - if err != nil { - return err - } - - if !slices.Contains(SupportedExts, v.getConfigType()) { - return UnsupportedConfigError(v.getConfigType()) - } - - file, err := afero.ReadFile(v.fs, filename) - if err != nil { - return err - } - - return v.MergeConfig(bytes.NewReader(file)) -} - -// ReadConfig will read a configuration file, setting existing keys to nil if the -// key does not exist in the file. -func ReadConfig(in io.Reader) error { return v.ReadConfig(in) } - -func (v *Viper) ReadConfig(in io.Reader) error { - config := make(map[string]any) - - err := v.unmarshalReader(in, config) - if err != nil { - return err - } - - v.config = config - - return nil -} - -// MergeConfig merges a new configuration with an existing config. -func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } - -func (v *Viper) MergeConfig(in io.Reader) error { - config := make(map[string]any) - - if err := v.unmarshalReader(in, config); err != nil { - return err - } - - return v.MergeConfigMap(config) -} - -// MergeConfigMap merges the configuration from the map given with an existing config. -// Note that the map given may be modified. -func MergeConfigMap(cfg map[string]any) error { return v.MergeConfigMap(cfg) } - -func (v *Viper) MergeConfigMap(cfg map[string]any) error { - if v.config == nil { - v.config = make(map[string]any) - } - insensitiviseMap(cfg) - mergeMaps(cfg, v.config, nil) - return nil -} - -// WriteConfig writes the current configuration to a file. -func WriteConfig() error { return v.WriteConfig() } - -func (v *Viper) WriteConfig() error { - filename, err := v.getConfigFile() - if err != nil { - return err - } - return v.writeConfig(filename, true) -} - -// SafeWriteConfig writes current configuration to file only if the file does not exist. -func SafeWriteConfig() error { return v.SafeWriteConfig() } - -func (v *Viper) SafeWriteConfig() error { - if len(v.configPaths) < 1 { - return errors.New("missing configuration for 'configPath'") - } - return v.SafeWriteConfigAs(filepath.Join(v.configPaths[0], v.configName+"."+v.configType)) -} - -// WriteConfigAs writes current configuration to a given filename. -func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } - -func (v *Viper) WriteConfigAs(filename string) error { - return v.writeConfig(filename, true) -} - -// WriteConfigTo writes current configuration to an [io.Writer]. -func WriteConfigTo(w io.Writer) error { return v.WriteConfigTo(w) } - -func (v *Viper) WriteConfigTo(w io.Writer) error { - format := strings.ToLower(v.getConfigType()) - - if !slices.Contains(SupportedExts, format) { - return UnsupportedConfigError(format) - } - - return v.marshalWriter(w, format) -} - -// SafeWriteConfigAs writes current configuration to a given filename if it does not exist. -func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } - -func (v *Viper) SafeWriteConfigAs(filename string) error { - alreadyExists, err := afero.Exists(v.fs, filename) - if alreadyExists && err == nil { - return ConfigFileAlreadyExistsError(filename) - } - return v.writeConfig(filename, false) -} - -func (v *Viper) writeConfig(filename string, force bool) error { - v.logger.Info("attempting to write configuration to file") - - var configType string - - ext := filepath.Ext(filename) - if ext != "" && ext != filepath.Base(filename) { - configType = ext[1:] - } else { - configType = v.configType - } - if configType == "" { - return fmt.Errorf("config type could not be determined for %s", filename) - } - - if !slices.Contains(SupportedExts, configType) { - return UnsupportedConfigError(configType) - } - if v.config == nil { - v.config = make(map[string]any) - } - flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY - if !force { - flags |= os.O_EXCL - } - f, err := v.fs.OpenFile(filename, flags, v.configPermissions) - if err != nil { - return err - } - defer f.Close() - - if err := v.marshalWriter(f, configType); err != nil { - return err - } - - return f.Sync() -} - -func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error { - format := strings.ToLower(v.getConfigType()) - if format == "" { - return errors.New("cannot decode configuration: unable to determine config type") - } - - buf := new(bytes.Buffer) - _, err := buf.ReadFrom(in) - if err != nil { - return fmt.Errorf("failed to read configuration from input: %w", err) - } - - // TODO: remove this once SupportedExts is deprecated/removed - if !slices.Contains(SupportedExts, format) { - return UnsupportedConfigError(format) - } - - // TODO: return [UnsupportedConfigError] if the registry does not contain the format - // TODO: consider deprecating this error type - decoder, err := v.decoderRegistry.Decoder(format) - if err != nil { - return ConfigParseError{err} - } - - err = decoder.Decode(buf.Bytes(), c) - if err != nil { - return ConfigParseError{err} - } - - insensitiviseMap(c) - return nil -} - -// Marshal a map into Writer. -func (v *Viper) marshalWriter(w io.Writer, configType string) error { - c := v.AllSettings() - - encoder, err := v.encoderRegistry.Encoder(configType) - if err != nil { - return ConfigMarshalError{err} - } - - b, err := encoder.Encode(c) - if err != nil { - return ConfigMarshalError{err} - } - - _, err = w.Write(b) - if err != nil { - return ConfigMarshalError{err} - } - - return nil -} - -func keyExists(k string, m map[string]any) string { - lk := strings.ToLower(k) - for mk := range m { - lmk := strings.ToLower(mk) - if lmk == lk { - return mk - } - } - return "" -} - -func castToMapStringInterface( - src map[any]any, -) map[string]any { - tgt := map[string]any{} - for k, v := range src { - tgt[fmt.Sprintf("%v", k)] = v - } - return tgt -} - -func castMapStringSliceToMapInterface(src map[string][]string) map[string]any { - tgt := map[string]any{} - for k, v := range src { - tgt[k] = v - } - return tgt -} - -func castMapStringToMapInterface(src map[string]string) map[string]any { - tgt := map[string]any{} - for k, v := range src { - tgt[k] = v - } - return tgt -} - -func castMapFlagToMapInterface(src map[string]FlagValue) map[string]any { - tgt := map[string]any{} - for k, v := range src { - tgt[k] = v - } - return tgt -} - -// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's -// insistence on parsing nested structures as `map[any]any` -// instead of using a `string` as the key for nest structures beyond one level -// deep. Both map types are supported as there is a go-yaml fork that uses -// `map[string]any` instead. -func mergeMaps(src, tgt map[string]any, itgt map[any]any) { - for sk, sv := range src { - tk := keyExists(sk, tgt) - if tk == "" { - v.logger.Debug("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv) - tgt[sk] = sv - if itgt != nil { - itgt[sk] = sv - } - continue - } - - tv, ok := tgt[tk] - if !ok { - v.logger.Debug("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv) - tgt[sk] = sv - if itgt != nil { - itgt[sk] = sv - } - continue - } - - svType := reflect.TypeOf(sv) - tvType := reflect.TypeOf(tv) - - v.logger.Debug( - "processing", - "key", sk, - "st", svType, - "tt", tvType, - "sv", sv, - "tv", tv, - ) - - switch ttv := tv.(type) { - case map[any]any: - v.logger.Debug("merging maps (must convert)") - tsv, ok := sv.(map[any]any) - if !ok { - v.logger.Error( - "Could not cast sv to map[any]any", - "key", sk, - "st", svType, - "tt", tvType, - "sv", sv, - "tv", tv, - ) - continue - } - - ssv := castToMapStringInterface(tsv) - stv := castToMapStringInterface(ttv) - mergeMaps(ssv, stv, ttv) - case map[string]any: - v.logger.Debug("merging maps") - tsv, ok := sv.(map[string]any) - if !ok { - v.logger.Error( - "Could not cast sv to map[string]any", - "key", sk, - "st", svType, - "tt", tvType, - "sv", sv, - "tv", tv, - ) - continue - } - mergeMaps(tsv, ttv, nil) - default: - v.logger.Debug("setting value") - tgt[tk] = sv - if itgt != nil { - itgt[tk] = sv - } - } - } -} - -// AllKeys returns all keys holding a value, regardless of where they are set. -// Nested keys are returned with a v.keyDelim separator. -func AllKeys() []string { return v.AllKeys() } - -func (v *Viper) AllKeys() []string { - m := map[string]bool{} - // add all paths, by order of descending priority to ensure correct shadowing - m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "") - m = v.flattenAndMergeMap(m, v.override, "") - m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags)) - m = v.mergeFlatMap(m, castMapStringSliceToMapInterface(v.env)) - m = v.flattenAndMergeMap(m, v.config, "") - m = v.flattenAndMergeMap(m, v.kvstore, "") - m = v.flattenAndMergeMap(m, v.defaults, "") - - // convert set of paths to list - a := make([]string, 0, len(m)) - for x := range m { - a = append(a, x) - } - return a -} - -// 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. -// -// 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]any, prefix string) map[string]bool { - if shadow != nil && prefix != "" && shadow[prefix] { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]bool) - } - - var m2 map[string]any - if prefix != "" { - prefix += v.keyDelim - } - for k, val := range m { - fullKey := prefix + k - switch val := val.(type) { - case map[string]any: - m2 = val - case map[any]any: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = true - continue - } - // recursively merge to shadow map - shadow = v.flattenAndMergeMap(shadow, m2, fullKey) - } - return shadow -} - -// mergeFlatMap merges the given maps, excluding values of the second map -// shadowed by values from the first map. -func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]any) map[string]bool { - // scan keys -outer: - for k := range m { - path := strings.Split(k, v.keyDelim) - // scan intermediate paths - var parentKey string - for i := 1; i < len(path); i++ { - parentKey = strings.Join(path[0:i], v.keyDelim) - if shadow[parentKey] { - // path is shadowed, continue - continue outer - } - } - // add key - shadow[strings.ToLower(k)] = true - } - return shadow -} - -// AllSettings merges all settings and returns them as a map[string]any. -func AllSettings() map[string]any { return v.AllSettings() } - -func (v *Viper) AllSettings() map[string]any { - return v.getSettings(v.AllKeys()) -} - -func (v *Viper) getSettings(keys []string) map[string]any { - m := map[string]any{} - // start from the list of keys, and construct the map one value at a time - for _, k := range keys { - value := v.Get(k) - if value == nil { - // should not happen, since AllKeys() returns only keys holding a value, - // check just in case anything changes - continue - } - path := strings.Split(k, v.keyDelim) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(m, path[0:len(path)-1]) - // set innermost value - deepestMap[lastKey] = value - } - return m -} - -// SetFs sets the filesystem to use to read configuration. -func SetFs(fs afero.Fs) { v.SetFs(fs) } - -func (v *Viper) SetFs(fs afero.Fs) { - v.fs = fs -} - -// SetConfigName sets name for the config file. -// Does not include extension. -func SetConfigName(in string) { v.SetConfigName(in) } - -func (v *Viper) SetConfigName(in string) { - if v.finder != nil { - v.logger.Warn("ineffective call to function: custom finder takes precedence", slog.String("function", "SetConfigName")) - } - - if in != "" { - v.configName = in - v.configFile = "" - } -} - -// SetConfigType sets the type of the configuration returned by the -// remote source, e.g. "json". -func SetConfigType(in string) { v.SetConfigType(in) } - -func (v *Viper) SetConfigType(in string) { - if in != "" { - v.configType = in - } -} - -// SetConfigPermissions sets the permissions for the config file. -func SetConfigPermissions(perm os.FileMode) { v.SetConfigPermissions(perm) } - -func (v *Viper) SetConfigPermissions(perm os.FileMode) { - v.configPermissions = perm.Perm() -} - -func (v *Viper) getConfigType() string { - if v.configType != "" { - return v.configType - } - - cf, err := v.getConfigFile() - if err != nil { - return "" - } - - ext := filepath.Ext(cf) - - if len(ext) > 1 { - return ext[1:] - } - - return "" -} - -func (v *Viper) getConfigFile() (string, error) { - if v.configFile == "" { - cf, err := v.findConfigFile() - if err != nil { - return "", err - } - v.configFile = cf - } - return v.configFile, nil -} - -// Debug prints all configuration registries for debugging -// purposes. -func Debug() { v.Debug() } -func DebugTo(w io.Writer) { v.DebugTo(w) } - -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) -} |
