mirror of
https://github.com/spf13/viper
synced 2024-07-03 09:01:41 +00:00
Compare commits
No commits in common. "master" and "v1.0.1" have entirely different histories.
|
@ -1,18 +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
|
|
4
.envrc
4
.envrc
|
@ -1,4 +0,0 @@
|
||||||
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
|
|
||||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
|
|
||||||
fi
|
|
||||||
use flake . --impure
|
|
2
.github/.editorconfig
vendored
2
.github/.editorconfig
vendored
|
@ -1,2 +0,0 @@
|
||||||
[{*.yml,*.yaml}]
|
|
||||||
indent_size = 2
|
|
119
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
119
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
|
@ -1,119 +0,0 @@
|
||||||
name: 🐛 Bug report
|
|
||||||
description: Report a bug to help us improve Viper
|
|
||||||
labels: [kind/bug]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thank you for submitting a bug report!
|
|
||||||
|
|
||||||
Please fill out the template below to make it easier to debug your problem.
|
|
||||||
|
|
||||||
If you are not sure if it is a bug or not, you can contact us via the available [support channels](https://github.com/spf13/viper/issues/new/choose).
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Preflight Checklist
|
|
||||||
description: Please ensure you've completed all of the following.
|
|
||||||
options:
|
|
||||||
- label: I have searched the [issue tracker](https://www.github.com/spf13/viper/issues) for an issue that matches the one I want to file, without success.
|
|
||||||
required: true
|
|
||||||
- label: I am not looking for support or already pursued the available [support channels](https://github.com/spf13/viper/issues/new/choose) without success.
|
|
||||||
required: true
|
|
||||||
- label: I have checked the [troubleshooting guide](https://github.com/spf13/viper/blob/master/TROUBLESHOOTING.md) for my problem, without success.
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Viper Version
|
|
||||||
description: What version of Viper are you using?
|
|
||||||
placeholder: 1.8.1
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Go Version
|
|
||||||
description: What version of Go are you using?
|
|
||||||
placeholder: "1.16"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Config Source
|
|
||||||
description: What sources do you load configuration from?
|
|
||||||
options:
|
|
||||||
- Manual set
|
|
||||||
- Flags
|
|
||||||
- Environment variables
|
|
||||||
- Files
|
|
||||||
- Remove K/V stores
|
|
||||||
- Defaults
|
|
||||||
multiple: true
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Format
|
|
||||||
description: Which file formats do you use?
|
|
||||||
options:
|
|
||||||
- JSON
|
|
||||||
- YAML
|
|
||||||
- TOML
|
|
||||||
- Dotenv
|
|
||||||
- HCL
|
|
||||||
- Java properties
|
|
||||||
- INI
|
|
||||||
- Other (specify below)
|
|
||||||
multiple: true
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Repl.it link
|
|
||||||
description: Complete example on Repl.it reproducing the issue. [Here](https://repl.it/@sagikazarmark/Viper-example) is an example you can use.
|
|
||||||
placeholder: https://repl.it/@sagikazarmark/Viper-example
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Code reproducing the issue
|
|
||||||
description: Please provide a Repl.it link if possible.
|
|
||||||
render: go
|
|
||||||
placeholder: |
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
v := viper.New()
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
var config Config
|
|
||||||
|
|
||||||
err = v.Unmarshal(&config)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Expected Behavior
|
|
||||||
description: A clear and concise description of what you expected to happen.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Actual Behavior
|
|
||||||
description: A clear description of what actually happens.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Steps To Reproduce
|
|
||||||
description: Steps to reproduce the behavior if it is not self-explanatory.
|
|
||||||
placeholder: |
|
|
||||||
1. In this environment...
|
|
||||||
2. With this config...
|
|
||||||
3. Run '...'
|
|
||||||
4. See error...
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional Information
|
|
||||||
description: Links? References? Anything that will give us more context about the issue that you are encountering!
|
|
13
.github/ISSUE_TEMPLATE/config.yml
vendored
13
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1,13 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: ❓ Ask a question
|
|
||||||
url: https://github.com/spf13/viper/discussions/new?category=q-a
|
|
||||||
about: Ask and discuss questions with other Viper community members
|
|
||||||
|
|
||||||
- name: 📓 Reference
|
|
||||||
url: https://pkg.go.dev/mod/github.com/spf13/viper
|
|
||||||
about: Check the Go code reference
|
|
||||||
|
|
||||||
- name: 💬 Slack channel
|
|
||||||
url: https://gophers.slack.com/messages/viper
|
|
||||||
about: Please ask and answer questions here
|
|
39
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
39
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
|
@ -1,39 +0,0 @@
|
||||||
name: 🎉 Feature request
|
|
||||||
description: Suggest an idea for Viper
|
|
||||||
labels: [kind/enhancement]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thank you for submitting a feature request!
|
|
||||||
|
|
||||||
Please describe what you would like to change/add and why in detail by filling out the template below.
|
|
||||||
|
|
||||||
If you are not sure if your request fits into Viper, you can contact us via the available [support channels](https://github.com/spf13/viper/issues/new/choose).
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Preflight Checklist
|
|
||||||
description: Please ensure you've completed all of the following.
|
|
||||||
options:
|
|
||||||
- label: I have searched the [issue tracker](https://www.github.com/spf13/viper/issues) for an issue that matches the one I want to file, without success.
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Problem Description
|
|
||||||
description: A clear and concise description of the problem you are seeking to solve with this feature request.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Proposed Solution
|
|
||||||
description: A clear and concise description of what would you like to happen.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Alternatives Considered
|
|
||||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional Information
|
|
||||||
description: Add any other context about the problem here.
|
|
20
.github/PULL_REQUEST_TEMPLATES.md
vendored
20
.github/PULL_REQUEST_TEMPLATES.md
vendored
|
@ -1,20 +0,0 @@
|
||||||
<!--
|
|
||||||
Thank you for sending a pull request! Here some tips for contributors:
|
|
||||||
|
|
||||||
1. Fill the description template below.
|
|
||||||
2. Include appropriate tests (if necessary). Make sure that all CI checks passed.
|
|
||||||
3. If the Pull Request is a work in progress, make use of GitHub's "Draft PR" feature and mark it as such.
|
|
||||||
-->
|
|
||||||
|
|
||||||
**Overview**:
|
|
||||||
<!-- Describe your changes briefly here. -->
|
|
||||||
|
|
||||||
**What problem does it solve?**:
|
|
||||||
<!--
|
|
||||||
- Please state in detail why we need this PR and what it solves.
|
|
||||||
- If your PR closes some of the existing issues, please add links to them here.
|
|
||||||
Mentioned issues will be automatically closed.
|
|
||||||
Usage: "Closes #<issue number>", or "Closes (paste link of issue)"
|
|
||||||
-->
|
|
||||||
|
|
||||||
**Special notes for a reviewer**:
|
|
16
.github/dependabot.yaml
vendored
16
.github/dependabot.yaml
vendored
|
@ -1,16 +0,0 @@
|
||||||
version: 2
|
|
||||||
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: gomod
|
|
||||||
directory: /
|
|
||||||
labels:
|
|
||||||
- area/dependencies
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: /
|
|
||||||
labels:
|
|
||||||
- area/dependencies
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
BIN
.github/logo.png
vendored
BIN
.github/logo.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 38 KiB |
30
.github/release.yml
vendored
30
.github/release.yml
vendored
|
@ -1,30 +0,0 @@
|
||||||
changelog:
|
|
||||||
exclude:
|
|
||||||
labels:
|
|
||||||
- release-note/ignore
|
|
||||||
categories:
|
|
||||||
- title: Exciting New Features 🎉
|
|
||||||
labels:
|
|
||||||
- kind/feature
|
|
||||||
- release-note/new-feature
|
|
||||||
- title: Enhancements 🚀
|
|
||||||
labels:
|
|
||||||
- kind/enhancement
|
|
||||||
- release-note/enhancement
|
|
||||||
- title: Bug Fixes 🐛
|
|
||||||
labels:
|
|
||||||
- kind/bug
|
|
||||||
- release-note/bug-fix
|
|
||||||
- title: Breaking Changes 🛠
|
|
||||||
labels:
|
|
||||||
- release-note/breaking-change
|
|
||||||
- title: Deprecations ❌
|
|
||||||
labels:
|
|
||||||
- release-note/deprecation
|
|
||||||
- title: Dependency Updates ⬆️
|
|
||||||
labels:
|
|
||||||
- area/dependencies
|
|
||||||
- release-note/dependency-update
|
|
||||||
- title: Other Changes
|
|
||||||
labels:
|
|
||||||
- "*"
|
|
30
.github/workflows/checks.yaml
vendored
30
.github/workflows/checks.yaml
vendored
|
@ -1,30 +0,0 @@
|
||||||
name: PR Checks
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, labeled, unlabeled, synchronize]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release-label:
|
|
||||||
name: Release note label
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check minimum labels
|
|
||||||
uses: mheap/github-action-required-labels@5847eef68201219cf0a4643ea7be61e77837bbce # v5.4.1
|
|
||||||
with:
|
|
||||||
mode: minimum
|
|
||||||
count: 1
|
|
||||||
labels: |
|
|
||||||
release-note/ignore
|
|
||||||
kind/feature
|
|
||||||
release-note/new-feature
|
|
||||||
kind/enhancement
|
|
||||||
release-note/enhancement
|
|
||||||
kind/bug
|
|
||||||
release-note/bug-fix
|
|
||||||
release-note/breaking-change
|
|
||||||
release-note/deprecation
|
|
||||||
area/dependencies
|
|
||||||
release-note/dependency-update
|
|
||||||
release-note/misc
|
|
115
.github/workflows/ci.yaml
vendored
115
.github/workflows/ci.yaml
vendored
|
@ -1,115 +0,0 @@
|
||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- goos: js
|
|
||||||
goarch: wasm
|
|
||||||
- goos: aix
|
|
||||||
goarch: ppc64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
|
||||||
with:
|
|
||||||
go-version: "1.22"
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: go build .
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
|
|
||||||
test:
|
|
||||||
name: Test
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
# Fail fast is disabled because there are Go version specific features and tests
|
|
||||||
# that should be able to fail independently.
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
||||||
go: ["1.21", "1.22"]
|
|
||||||
tags: ["", "viper_finder", "viper_bind_struct"]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.go }}
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: go test -race -v -tags '${{ matrix.tags }}' -shuffle=on ./...
|
|
||||||
if: runner.os != 'Windows'
|
|
||||||
|
|
||||||
- name: Test (without race detector)
|
|
||||||
run: go test -v -tags '${{ matrix.tags }}' -shuffle=on ./...
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
|
|
||||||
lint:
|
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
|
||||||
with:
|
|
||||||
go-version: "1.22"
|
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1
|
|
||||||
with:
|
|
||||||
version: v1.59.0
|
|
||||||
|
|
||||||
dev:
|
|
||||||
name: Developer environment
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
|
||||||
|
|
||||||
- name: Set up Nix
|
|
||||||
uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
|
|
||||||
with:
|
|
||||||
extra_nix_config: |
|
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Check
|
|
||||||
run: nix flake check --impure
|
|
||||||
|
|
||||||
- name: Dev shell
|
|
||||||
run: nix develop --impure
|
|
||||||
|
|
||||||
dependency-review:
|
|
||||||
name: Dependency review
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
|
||||||
|
|
||||||
- name: Dependency Review
|
|
||||||
uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3
|
|
72
.github/workflows/codeql-analysis.yaml
vendored
72
.github/workflows/codeql-analysis.yaml
vendored
|
@ -1,72 +0,0 @@
|
||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [ master ]
|
|
||||||
schedule:
|
|
||||||
- cron: '22 16 * * 3'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'go' ]
|
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
|
||||||
# Learn more:
|
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 https://git.io/JvXDl
|
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
||||||
# and modify them (or add more) to build your code if your project
|
|
||||||
# uses a compiled language
|
|
||||||
|
|
||||||
#- run: |
|
|
||||||
# make bootstrap
|
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
|
|
||||||
|
|
32
.github/workflows/feedback_issue.yaml
vendored
32
.github/workflows/feedback_issue.yaml
vendored
|
@ -1,32 +0,0 @@
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
comment:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
|
||||||
with:
|
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
script: |
|
|
||||||
github.rest.issues.createComment({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
body: `👋 Thanks for reporting!
|
|
||||||
|
|
||||||
A maintainer will take a look at your issue shortly. 👀
|
|
||||||
|
|
||||||
In the meantime: We are working on **Viper v2** and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.
|
|
||||||
|
|
||||||
⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9
|
|
||||||
|
|
||||||
📣 If you've already given us your feedback, you can still help by spreading the news,
|
|
||||||
either by sharing the above link or telling people about this on Twitter:
|
|
||||||
|
|
||||||
https://twitter.com/sagikazarmark/status/1306904078967074816
|
|
||||||
|
|
||||||
**Thank you!** ❤️
|
|
||||||
`,
|
|
||||||
})
|
|
32
.github/workflows/feedback_pull_request.yaml
vendored
32
.github/workflows/feedback_pull_request.yaml
vendored
|
@ -1,32 +0,0 @@
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
comment:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
|
||||||
with:
|
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
script: |
|
|
||||||
github.rest.issues.createComment({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
body: `👋 Thanks for contributing to Viper! You are awesome! 🎉
|
|
||||||
|
|
||||||
A maintainer will take a look at your pull request shortly. 👀
|
|
||||||
|
|
||||||
In the meantime: We are working on **Viper v2** and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.
|
|
||||||
|
|
||||||
⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9
|
|
||||||
|
|
||||||
📣 If you've already given us your feedback, you can still help by spreading the news,
|
|
||||||
either by sharing the above link or telling people about this on Twitter:
|
|
||||||
|
|
||||||
https://twitter.com/sagikazarmark/status/1306904078967074816
|
|
||||||
|
|
||||||
**Thank you!** ❤️
|
|
||||||
`,
|
|
||||||
})
|
|
32
.gitignore
vendored
32
.gitignore
vendored
|
@ -1,8 +1,24 @@
|
||||||
/.devenv/
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
/.direnv/
|
*.o
|
||||||
/.idea/
|
*.a
|
||||||
/.pre-commit-config.yaml
|
*.so
|
||||||
/bin/
|
|
||||||
/build/
|
# Folders
|
||||||
/var/
|
_obj
|
||||||
/vendor/
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.bench
|
108
.golangci.yaml
108
.golangci.yaml
|
@ -1,108 +0,0 @@
|
||||||
run:
|
|
||||||
timeout: 5m
|
|
||||||
|
|
||||||
linters-settings:
|
|
||||||
gci:
|
|
||||||
sections:
|
|
||||||
- standard
|
|
||||||
- default
|
|
||||||
- prefix(github.com/spf13/viper)
|
|
||||||
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
|
|
||||||
golint:
|
|
||||||
min-confidence: 0
|
|
||||||
goimports:
|
|
||||||
local-prefixes: github.com/spf13/viper
|
|
||||||
|
|
||||||
linters:
|
|
||||||
disable-all: true
|
|
||||||
enable:
|
|
||||||
- bodyclose
|
|
||||||
- dogsled
|
|
||||||
- dupl
|
|
||||||
- durationcheck
|
|
||||||
- exhaustive
|
|
||||||
- exportloopref
|
|
||||||
- gci
|
|
||||||
- gocritic
|
|
||||||
- godot
|
|
||||||
- gofmt
|
|
||||||
- gofumpt
|
|
||||||
- goimports
|
|
||||||
- gomoddirectives
|
|
||||||
- goprintffuncname
|
|
||||||
- govet
|
|
||||||
- importas
|
|
||||||
- ineffassign
|
|
||||||
- makezero
|
|
||||||
- misspell
|
|
||||||
- nakedret
|
|
||||||
- nilerr
|
|
||||||
- noctx
|
|
||||||
- nolintlint
|
|
||||||
- prealloc
|
|
||||||
- predeclared
|
|
||||||
- revive
|
|
||||||
- rowserrcheck
|
|
||||||
- sqlclosecheck
|
|
||||||
- staticcheck
|
|
||||||
- stylecheck
|
|
||||||
- tparallel
|
|
||||||
- typecheck
|
|
||||||
- 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
|
|
||||||
|
|
||||||
# deprecated
|
|
||||||
# - deadcode
|
|
||||||
# - structcheck
|
|
||||||
# - varcheck
|
|
||||||
|
|
||||||
# don't enable:
|
|
||||||
# - asciicheck
|
|
||||||
# - funlen
|
|
||||||
# - godox
|
|
||||||
# - goerr113
|
|
||||||
# - gomnd
|
|
||||||
# - interfacer
|
|
||||||
# - maligned
|
|
||||||
# - nestif
|
|
||||||
# - testpackage
|
|
||||||
# - wsl
|
|
28
.travis.yml
Normal file
28
.travis.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
go_import_path: github.com/spf13/viper
|
||||||
|
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
fast_finish: true
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go install ./...
|
||||||
|
- diff -u <(echo -n) <(gofmt -d .)
|
||||||
|
- go test -v ./...
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- go get -u -d github.com/spf13/hugo
|
||||||
|
- cd $GOPATH/src/github.com/spf13/hugo && make && ./hugo -s docs && cd -
|
||||||
|
|
||||||
|
sudo: false
|
|
@ -1,2 +0,0 @@
|
||||||
# TODO: FIXME
|
|
||||||
/.github/
|
|
|
@ -1,6 +0,0 @@
|
||||||
ignore-from-file: [.gitignore, .yamlignore]
|
|
||||||
|
|
||||||
extends: default
|
|
||||||
|
|
||||||
rules:
|
|
||||||
line-length: disable
|
|
87
Makefile
87
Makefile
|
@ -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 $*=$($*)
|
|
483
README.md
483
README.md
|
@ -1,20 +1,6 @@
|
||||||
> ## Viper v2 feedback
|
![viper logo](https://cloud.githubusercontent.com/assets/173412/10886745/998df88a-8151-11e5-9448-4736db51020d.png)
|
||||||
> 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!**
|
|
||||||
|
|
||||||
![Viper](.github/logo.png?raw=true)
|
Go configuration with fangs!
|
||||||
|
|
||||||
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
|
|
||||||
[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)
|
|
||||||
|
|
||||||
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
|
|
||||||
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
|
|
||||||
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.21-61CFDD.svg?style=flat-square)
|
|
||||||
[![PkgGoDev](https://pkg.go.dev/badge/mod/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:
|
Many Go projects are built using Viper including:
|
||||||
|
|
||||||
|
@ -26,30 +12,18 @@ Many Go projects are built using Viper including:
|
||||||
* [BloomApi](https://www.bloomapi.com/)
|
* [BloomApi](https://www.bloomapi.com/)
|
||||||
* [doctl](https://github.com/digitalocean/doctl)
|
* [doctl](https://github.com/digitalocean/doctl)
|
||||||
* [Clairctl](https://github.com/jgsqware/clairctl)
|
* [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/)
|
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper)
|
||||||
## Install
|
|
||||||
|
|
||||||
```shell
|
|
||||||
go get github.com/spf13/viper
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Viper uses [Go Modules](https://go.dev/wiki/Modules) to manage dependencies.
|
|
||||||
|
|
||||||
|
|
||||||
## What is Viper?
|
## What is Viper?
|
||||||
|
|
||||||
Viper is a complete configuration solution for Go applications including [12-Factor apps](https://12factor.net/#the_twelve_factors).
|
Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed
|
||||||
It is designed to work within an application, and can handle all types of configuration needs
|
to work within an application, and can handle all types of configuration needs
|
||||||
and formats. It supports:
|
and formats. It supports:
|
||||||
|
|
||||||
* setting defaults
|
* setting defaults
|
||||||
* reading from JSON, TOML, YAML, HCL, envfile and Java properties config files
|
* reading from JSON, TOML, YAML, HCL, and Java properties config files
|
||||||
* live watching and re-reading of config files (optional)
|
* live watching and re-reading of config files (optional)
|
||||||
* reading from environment variables
|
* reading from environment variables
|
||||||
* reading from remote config systems (etcd or Consul), and watching changes
|
* reading from remote config systems (etcd or Consul), and watching changes
|
||||||
|
@ -57,8 +31,8 @@ and formats. It supports:
|
||||||
* reading from buffer
|
* reading from buffer
|
||||||
* setting explicit values
|
* setting explicit values
|
||||||
|
|
||||||
Viper can be thought of as a registry for all of your applications configuration needs.
|
Viper can be thought of as a registry for all of your applications
|
||||||
|
configuration needs.
|
||||||
|
|
||||||
## Why Viper?
|
## Why Viper?
|
||||||
|
|
||||||
|
@ -68,31 +42,34 @@ Viper is here to help with that.
|
||||||
|
|
||||||
Viper does the following for you:
|
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.
|
1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, or Java properties formats.
|
||||||
2. Provide a mechanism to set default values for your different configuration options.
|
2. Provide a mechanism to set default values for your different
|
||||||
3. Provide a mechanism to set override values for options specified through command line flags.
|
configuration options.
|
||||||
4. Provide an alias system to easily rename parameters without breaking existing code.
|
3. Provide a mechanism to set override values for options specified through
|
||||||
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.
|
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:
|
Viper uses the following precedence order. Each item takes precedence over the
|
||||||
|
item below it:
|
||||||
|
|
||||||
* explicit call to `Set`
|
* explicit call to Set
|
||||||
* flag
|
* flag
|
||||||
* env
|
* env
|
||||||
* config
|
* config
|
||||||
* key/value store
|
* key/value store
|
||||||
* default
|
* default
|
||||||
|
|
||||||
**Important:** Viper configuration keys are case insensitive.
|
Viper configuration keys are case insensitive.
|
||||||
There are ongoing discussions about making that optional.
|
|
||||||
|
|
||||||
|
|
||||||
## Putting Values into Viper
|
## Putting Values into Viper
|
||||||
|
|
||||||
### Establishing Defaults
|
### Establishing Defaults
|
||||||
|
|
||||||
A good configuration system will support default values. A default value is not
|
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
|
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.
|
config file, environment variable, remote configuration or flag.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
@ -106,7 +83,7 @@ viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "cat
|
||||||
### Reading Config Files
|
### Reading Config Files
|
||||||
|
|
||||||
Viper requires minimal configuration so it knows where to look for 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
|
Viper supports JSON, TOML, YAML, HCL, and Java Properties files. Viper can search multiple paths, but
|
||||||
currently a single Viper instance only supports a single configuration file.
|
currently a single Viper instance only supports a single configuration file.
|
||||||
Viper does not default to any configuration search paths leaving defaults decision
|
Viper does not default to any configuration search paths leaving defaults decision
|
||||||
to an application.
|
to an application.
|
||||||
|
@ -117,54 +94,15 @@ where a configuration file is expected.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
viper.SetConfigName("config") // name of config file (without extension)
|
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("/etc/appname/") // path to look for the config file in
|
||||||
viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
|
viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
|
||||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||||
err := viper.ReadInConfig() // Find and read the config file
|
err := viper.ReadInConfig() // Find and read the config file
|
||||||
if err != nil { // Handle errors reading the config file
|
if err != nil { // Handle errors reading the config file
|
||||||
panic(fmt.Errorf("fatal error config file: %w", err))
|
panic(fmt.Errorf("Fatal error config file: %s \n", 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
|
### Watching and re-reading config files
|
||||||
|
|
||||||
Viper supports the ability to have your application live read a config file while running.
|
Viper supports the ability to have your application live read a config file while running.
|
||||||
|
@ -179,10 +117,10 @@ 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()`**
|
**Make sure you add all of the configPaths prior to calling `WatchConfig()`**
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
viper.WatchConfig()
|
||||||
viper.OnConfigChange(func(e fsnotify.Event) {
|
viper.OnConfigChange(func(e fsnotify.Event) {
|
||||||
fmt.Println("Config file changed:", e.Name)
|
fmt.Println("Config file changed:", e.Name)
|
||||||
})
|
})
|
||||||
viper.WatchConfig()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reading Config from io.Reader
|
### Reading Config from io.Reader
|
||||||
|
@ -222,7 +160,6 @@ These could be from a command line flag, or from your own application logic.
|
||||||
```go
|
```go
|
||||||
viper.Set("Verbose", true)
|
viper.Set("Verbose", true)
|
||||||
viper.Set("LogFile", LogFile)
|
viper.Set("LogFile", LogFile)
|
||||||
viper.Set("host.port", 5899) // set subset
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Registering and Using Aliases
|
### Registering and Using Aliases
|
||||||
|
@ -242,30 +179,28 @@ viper.GetBool("verbose") // true
|
||||||
### Working with Environment Variables
|
### Working with Environment Variables
|
||||||
|
|
||||||
Viper has full support for environment variables. This enables 12 factor
|
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
|
applications out of the box. There are four methods that exist to aid working
|
||||||
with ENV:
|
with ENV:
|
||||||
|
|
||||||
* `AutomaticEnv()`
|
* `AutomaticEnv()`
|
||||||
* `BindEnv(string...) : error`
|
* `BindEnv(string...) : error`
|
||||||
* `SetEnvPrefix(string)`
|
* `SetEnvPrefix(string)`
|
||||||
* `SetEnvKeyReplacer(string...) *strings.Replacer`
|
* `SetEnvKeyReplacer(string...) *strings.Replacer`
|
||||||
* `AllowEmptyEnv(bool)`
|
|
||||||
|
|
||||||
_When working with ENV variables, it’s important to recognize that Viper
|
_When working with ENV variables, it’s important to recognize that Viper
|
||||||
treats ENV variables as case sensitive._
|
treats ENV variables as case sensitive._
|
||||||
|
|
||||||
Viper provides a mechanism to try to ensure that ENV variables are unique. By
|
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
|
using `SetEnvPrefix`, you can tell Viper to use add a prefix while reading from
|
||||||
the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
|
the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
|
||||||
prefix.
|
prefix.
|
||||||
|
|
||||||
`BindEnv` takes one or more parameters. The first parameter is the key name, the
|
`BindEnv` takes one or two 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
|
second is the name of the environment variable. The name of the environment
|
||||||
one are provided, they will take precedence in the specified order. The name of
|
variable is case sensitive. If the ENV variable name is not provided, then
|
||||||
the environment variable is case sensitive. If the ENV variable name is not provided, then
|
Viper will automatically assume that the key name matches the ENV variable name,
|
||||||
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),
|
but the ENV variable is IN ALL CAPS. When you explicitly provide the ENV
|
||||||
it **does not** automatically add the prefix. For example if the second parameter is "id",
|
variable name, it **does not** automatically add the prefix.
|
||||||
Viper will look for the ENV variable "ID".
|
|
||||||
|
|
||||||
One important thing to recognize when working with ENV variables is that the
|
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
|
value will be read each time it is accessed. Viper does not fix the value when
|
||||||
|
@ -274,7 +209,7 @@ the `BindEnv` is called.
|
||||||
`AutomaticEnv` is a powerful helper especially when combined with
|
`AutomaticEnv` is a powerful helper especially when combined with
|
||||||
`SetEnvPrefix`. When called, Viper will check for an environment variable any
|
`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
|
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
|
check for a environment variable with a name matching the key uppercased and
|
||||||
prefixed with the `EnvPrefix` if set.
|
prefixed with the `EnvPrefix` if set.
|
||||||
|
|
||||||
`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env
|
`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env
|
||||||
|
@ -282,13 +217,6 @@ 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
|
`Get()` calls, but want your environmental variables to use `_` delimiters. An
|
||||||
example of using it can be found in `viper_test.go`.
|
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
|
#### Env example
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -359,7 +287,7 @@ func main() {
|
||||||
|
|
||||||
i := viper.GetInt("flagname") // retrieve value from viper
|
i := viper.GetInt("flagname") // retrieve value from viper
|
||||||
|
|
||||||
// ...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -413,14 +341,12 @@ package:
|
||||||
|
|
||||||
`import _ "github.com/spf13/viper/remote"`
|
`import _ "github.com/spf13/viper/remote"`
|
||||||
|
|
||||||
Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path
|
Viper will read a config string (as JSON, TOML, YAML or HCL) retrieved from a path
|
||||||
in a Key/Value store such as etcd or Consul. These values take precedence over
|
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,
|
default values, but are overridden by configuration values retrieved from disk,
|
||||||
flags, or environment variables.
|
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/xordataexchange/crypt) to retrieve
|
||||||
|
|
||||||
Viper uses [crypt](https://github.com/sagikazarmark/crypt) to retrieve
|
|
||||||
configuration from the K/V store, which means that you can store your
|
configuration from the K/V store, which means that you can store your
|
||||||
configuration values encrypted and have them automatically decrypted if you have
|
configuration values encrypted and have them automatically decrypted if you have
|
||||||
the correct gpg keyring. Encryption is optional.
|
the correct gpg keyring. Encryption is optional.
|
||||||
|
@ -432,7 +358,7 @@ independently of it.
|
||||||
K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001.
|
K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go get github.com/sagikazarmark/crypt/bin/crypt
|
$ go get github.com/xordataexchange/crypt/bin/crypt
|
||||||
$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
|
$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -447,56 +373,9 @@ how to use Consul.
|
||||||
|
|
||||||
### Remote Key/Value Store Example - Unencrypted
|
### Remote Key/Value Store Example - Unencrypted
|
||||||
|
|
||||||
#### etcd
|
|
||||||
```go
|
```go
|
||||||
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
|
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"
|
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
|
||||||
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()
|
err := viper.ReadRemoteConfig()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -504,7 +383,7 @@ err := viper.ReadRemoteConfig()
|
||||||
|
|
||||||
```go
|
```go
|
||||||
viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
|
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"
|
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
|
||||||
err := viper.ReadRemoteConfig()
|
err := viper.ReadRemoteConfig()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -515,7 +394,7 @@ err := viper.ReadRemoteConfig()
|
||||||
var runtime_viper = viper.New()
|
var runtime_viper = viper.New()
|
||||||
|
|
||||||
runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
|
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"
|
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"
|
||||||
|
|
||||||
// read from remote config the first time.
|
// read from remote config the first time.
|
||||||
err := runtime_viper.ReadRemoteConfig()
|
err := runtime_viper.ReadRemoteConfig()
|
||||||
|
@ -526,18 +405,18 @@ runtime_viper.Unmarshal(&runtime_conf)
|
||||||
// open a goroutine to watch remote changes forever
|
// open a goroutine to watch remote changes forever
|
||||||
go func(){
|
go func(){
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Second * 5) // delay after each request
|
time.Sleep(time.Second * 5) // delay after each request
|
||||||
|
|
||||||
// currently, only tested with etcd support
|
// currently, only tested with etcd support
|
||||||
err := runtime_viper.WatchRemoteConfig()
|
err := runtime_viper.WatchRemoteConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to read remote config: %v", err)
|
log.Errorf("unable to read remote config: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshal new config into our runtime config struct. you can also use channel
|
// unmarshal new config into our runtime config struct. you can also use channel
|
||||||
// to implement a signal to notify the system of the changes
|
// to implement a signal to notify the system of the changes
|
||||||
runtime_viper.Unmarshal(&runtime_conf)
|
runtime_viper.Unmarshal(&runtime_conf)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
```
|
```
|
||||||
|
@ -547,32 +426,27 @@ go func(){
|
||||||
In Viper, there are a few ways to get a value depending on the value’s type.
|
In Viper, there are a few ways to get a value depending on the value’s type.
|
||||||
The following functions and methods exist:
|
The following functions and methods exist:
|
||||||
|
|
||||||
* `Get(key string) : any`
|
* `Get(key string) : interface{}`
|
||||||
* `GetBool(key string) : bool`
|
* `GetBool(key string) : bool`
|
||||||
* `GetFloat64(key string) : float64`
|
* `GetFloat64(key string) : float64`
|
||||||
* `GetInt(key string) : int`
|
* `GetInt(key string) : int`
|
||||||
* `GetIntSlice(key string) : []int`
|
|
||||||
* `GetString(key string) : string`
|
* `GetString(key string) : string`
|
||||||
* `GetStringMap(key string) : map[string]any`
|
* `GetStringMap(key string) : map[string]interface{}`
|
||||||
* `GetStringMapString(key string) : map[string]string`
|
* `GetStringMapString(key string) : map[string]string`
|
||||||
* `GetStringSlice(key string) : []string`
|
* `GetStringSlice(key string) : []string`
|
||||||
* `GetTime(key string) : time.Time`
|
* `GetTime(key string) : time.Time`
|
||||||
* `GetDuration(key string) : time.Duration`
|
* `GetDuration(key string) : time.Duration`
|
||||||
* `IsSet(key string) : bool`
|
* `IsSet(key string) : bool`
|
||||||
* `AllSettings() : map[string]any`
|
|
||||||
|
|
||||||
One important thing to recognize is that each Get function will return a zero
|
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
|
value if it’s not found. To check if a given key exists, the `IsSet()` method
|
||||||
has been provided.
|
has been provided.
|
||||||
|
|
||||||
The zero value will also be returned if the value is set, but fails to parse
|
|
||||||
as the requested type.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```go
|
```go
|
||||||
viper.GetString("logfile") // case-insensitive Setting & Getting
|
viper.GetString("logfile") // case-insensitive Setting & Getting
|
||||||
if viper.GetBool("verbose") {
|
if viper.GetBool("verbose") {
|
||||||
fmt.Println("verbose enabled")
|
fmt.Println("verbose enabled")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### Accessing nested keys
|
### Accessing nested keys
|
||||||
|
@ -618,37 +492,10 @@ the `Set()` method, …) with an immediate value, then all sub-keys of
|
||||||
`datastore.metric` become undefined, they are “shadowed” by the higher-priority
|
`datastore.metric` become undefined, they are “shadowed” by the higher-priority
|
||||||
configuration level.
|
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
|
Lastly, if there exists a key that matches the delimited key path, its value
|
||||||
will be returned instead. E.g.
|
will be returned instead. E.g.
|
||||||
|
|
||||||
```jsonc
|
```json
|
||||||
{
|
{
|
||||||
"datastore.metric.host": "0.0.0.0",
|
"datastore.metric.host": "0.0.0.0",
|
||||||
"host": {
|
"host": {
|
||||||
|
@ -670,15 +517,14 @@ will be returned instead. E.g.
|
||||||
GetString("datastore.metric.host") // returns "0.0.0.0"
|
GetString("datastore.metric.host") // returns "0.0.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Extracting a sub-tree
|
### Extract sub-tree
|
||||||
|
|
||||||
When developing reusable modules, it's often useful to extract a subset of the configuration
|
Extract sub-tree from Viper.
|
||||||
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:
|
For example, `viper` represents:
|
||||||
|
|
||||||
```yaml
|
```json
|
||||||
cache:
|
app:
|
||||||
cache1:
|
cache1:
|
||||||
max-items: 100
|
max-items: 100
|
||||||
item-size: 64
|
item-size: 64
|
||||||
|
@ -687,36 +533,35 @@ cache:
|
||||||
item-size: 80
|
item-size: 80
|
||||||
```
|
```
|
||||||
|
|
||||||
We could pass the cache name to a module (eg. `NewCache("cache1")`),
|
After executing:
|
||||||
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
|
```go
|
||||||
cache1Config := viper.Sub("cache.cache1")
|
subv := viper.Sub("app.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.
|
`subv` represents:
|
||||||
|
|
||||||
Internally, the `NewCache` function can address `max-items` and `item-size` keys directly:
|
```json
|
||||||
|
max-items: 100
|
||||||
|
item-size: 64
|
||||||
|
```
|
||||||
|
|
||||||
|
Suppose we have:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func NewCache(v *Viper) *Cache {
|
func NewCache(cfg *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,
|
which creates a cache based on config information formatted as `subv`.
|
||||||
and easier to reuse (for the same reason).
|
Now it’s easy to create these 2 caches separately as:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg1 := viper.Sub("app.cache1")
|
||||||
|
cache1 := NewCache(cfg1)
|
||||||
|
|
||||||
|
cfg2 := viper.Sub("app.cache2")
|
||||||
|
cache2 := NewCache(cfg2)
|
||||||
|
```
|
||||||
|
|
||||||
### Unmarshaling
|
### Unmarshaling
|
||||||
|
|
||||||
|
@ -725,8 +570,8 @@ etc.
|
||||||
|
|
||||||
There are two methods to do this:
|
There are two methods to do this:
|
||||||
|
|
||||||
* `Unmarshal(rawVal any) : error`
|
* `Unmarshal(rawVal interface{}) : error`
|
||||||
* `UnmarshalKey(key string, rawVal any) : error`
|
* `UnmarshalKey(key string, rawVal interface{}) : error`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -739,112 +584,21 @@ type config struct {
|
||||||
|
|
||||||
var C config
|
var C config
|
||||||
|
|
||||||
err := viper.Unmarshal(&C)
|
err := Unmarshal(&C)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to decode into struct, %v", err)
|
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 "gopkg.in/yaml.v2"
|
|
||||||
// ...
|
|
||||||
)
|
|
||||||
|
|
||||||
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 or Vipers?
|
||||||
|
|
||||||
Viper comes with a global instance (singleton) out of the box.
|
Viper comes ready to use out of the box. There is no configuration or
|
||||||
|
initialization needed to begin using Viper. Since most applications will want
|
||||||
|
to use a single central repository for their configuration, the viper package
|
||||||
|
provides this. It is similar to a singleton.
|
||||||
|
|
||||||
Although it makes setting up configuration easy,
|
In all of the examples above, they demonstrate using viper in its singleton
|
||||||
using it is generally discouraged as it makes testing harder and can lead to unexpected behavior.
|
style approach.
|
||||||
|
|
||||||
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
|
### Working with multiple vipers
|
||||||
|
|
||||||
|
@ -868,63 +622,22 @@ y.SetDefault("ContentDir", "foobar")
|
||||||
When working with multiple vipers, it is up to the user to keep track of the
|
When working with multiple vipers, it is up to the user to keep track of the
|
||||||
different vipers.
|
different vipers.
|
||||||
|
|
||||||
|
|
||||||
## Q & A
|
## Q & A
|
||||||
|
|
||||||
### Why is it called “Viper”?
|
Q: Why not INI files?
|
||||||
|
|
||||||
|
A: Ini files are pretty awful. There’s no standard format, and they are hard to
|
||||||
|
validate. Viper is designed to work with JSON, TOML or YAML files. If someone
|
||||||
|
really wants to add this feature, I’d be happy to merge it. It’s easy to specify
|
||||||
|
which formats your application will permit.
|
||||||
|
|
||||||
|
Q: Why is it called “Viper”?
|
||||||
|
|
||||||
A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe))
|
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
|
to [Cobra](https://github.com/spf13/cobra). While both can operate completely
|
||||||
independently, together they make a powerful pair to handle much of your
|
independently, together they make a powerful pair to handle much of your
|
||||||
application foundation needs.
|
application foundation needs.
|
||||||
|
|
||||||
### Why is it called “Cobra”?
|
Q: Why is it called “Cobra”?
|
||||||
|
|
||||||
Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)?
|
A: 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).
|
|
||||||
|
|
|
@ -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)
|
|
181
encoding.go
181
encoding.go
|
@ -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
|
|
||||||
}
|
|
105
encoding_test.go
105
encoding_test.go
|
@ -1,105 +0,0 @@
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type codec struct{}
|
|
||||||
|
|
||||||
func (codec) Encode(_ map[string]any) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec) Decode(_ []byte, _ map[string]any) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultCodecRegistry(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
registry := NewCodecRegistry()
|
|
||||||
|
|
||||||
c := codec{}
|
|
||||||
|
|
||||||
err := registry.RegisterCodec("myformat", c)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
encoder, err := registry.Encoder("myformat")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, encoder)
|
|
||||||
|
|
||||||
decoder, err := registry.Decoder("myformat")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, decoder)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("CodecNotFound", func(t *testing.T) {
|
|
||||||
registry := NewCodecRegistry()
|
|
||||||
|
|
||||||
_, err := registry.Encoder("myformat")
|
|
||||||
require.Error(t, err)
|
|
||||||
|
|
||||||
_, err = registry.Decoder("myformat")
|
|
||||||
require.Error(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("FormatIsCaseInsensitive", func(t *testing.T) {
|
|
||||||
registry := NewCodecRegistry()
|
|
||||||
|
|
||||||
c := codec{}
|
|
||||||
|
|
||||||
err := registry.RegisterCodec("MYFORMAT", c)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
{
|
|
||||||
encoder, err := registry.Encoder("myformat")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, encoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
encoder, err := registry.Encoder("MYFORMAT")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, encoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
decoder, err := registry.Decoder("myformat")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, decoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
decoder, err := registry.Decoder("MYFORMAT")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, decoder)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("OverrideDefault", func(t *testing.T) {
|
|
||||||
registry := NewCodecRegistry()
|
|
||||||
|
|
||||||
c := codec{}
|
|
||||||
|
|
||||||
err := registry.RegisterCodec("yaml", c)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
encoder, err := registry.Encoder("yaml")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, encoder)
|
|
||||||
|
|
||||||
decoder, err := registry.Decoder("yaml")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, c, decoder)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
})
|
|
||||||
}
|
|
104
file.go
104
file.go
|
@ -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
|
|
||||||
}
|
|
55
finder.go
55
finder.go
|
@ -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...)
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package viper_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/sagikazarmark/locafero"
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleFinder() {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
fs.Mkdir("/home/user", 0o777)
|
|
||||||
|
|
||||||
f, _ := fs.Create("/home/user/myapp.yaml")
|
|
||||||
f.WriteString("foo: bar")
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
// HCL will have a "lower" priority in the search order
|
|
||||||
fs.Create("/home/user/myapp.hcl")
|
|
||||||
|
|
||||||
finder := locafero.Finder{
|
|
||||||
Paths: []string{"/home/user"},
|
|
||||||
Names: locafero.NameWithExtensions("myapp", viper.SupportedExts...),
|
|
||||||
Type: locafero.FileTypeFile, // This is important!
|
|
||||||
}
|
|
||||||
|
|
||||||
v := viper.NewWithOptions(viper.WithFinder(finder))
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.ReadInConfig()
|
|
||||||
|
|
||||||
fmt.Println(v.GetString("foo"))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// bar
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleFinders() {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
fs.Mkdir("/home/user", 0o777)
|
|
||||||
f, _ := fs.Create("/home/user/myapp.yaml")
|
|
||||||
f.WriteString("foo: bar")
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
fs.Mkdir("/etc/myapp", 0o777)
|
|
||||||
fs.Create("/etc/myapp/config.yaml")
|
|
||||||
|
|
||||||
// Combine multiple finders to search for files in multiple locations with different criteria
|
|
||||||
finder := viper.Finders(
|
|
||||||
locafero.Finder{
|
|
||||||
Paths: []string{"/home/user"},
|
|
||||||
Names: locafero.NameWithExtensions("myapp", viper.SupportedExts...),
|
|
||||||
Type: locafero.FileTypeFile, // This is important!
|
|
||||||
},
|
|
||||||
locafero.Finder{
|
|
||||||
Paths: []string{"/etc/myapp"},
|
|
||||||
Names: []string{"config.yaml"}, // Only accept YAML files in the system config directory
|
|
||||||
Type: locafero.FileTypeFile, // This is important!
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
v := viper.NewWithOptions(viper.WithFinder(finder))
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.ReadInConfig()
|
|
||||||
|
|
||||||
fmt.Println(v.GetString("foo"))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// bar
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type finderStub struct {
|
|
||||||
results []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f finderStub) Find(_ afero.Fs) ([]string, error) {
|
|
||||||
return f.results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFinders(t *testing.T) {
|
|
||||||
finder := Finders(
|
|
||||||
finderStub{
|
|
||||||
results: []string{
|
|
||||||
"/home/user/.viper.yaml",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
finderStub{
|
|
||||||
results: []string{
|
|
||||||
"/etc/viper/config.yaml",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
results, err := finder.Find(afero.NewMemMapFs())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := []string{
|
|
||||||
"/home/user/.viper.yaml",
|
|
||||||
"/etc/viper/config.yaml",
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, results)
|
|
||||||
}
|
|
6
flags.go
6
flags.go
|
@ -30,13 +30,13 @@ func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// pflagValue is a wrapper around *pflag.flag
|
// pflagValue is a wrapper aroung *pflag.flag
|
||||||
// that implements FlagValue.
|
// that implements FlagValue
|
||||||
type pflagValue struct {
|
type pflagValue struct {
|
||||||
flag *pflag.Flag
|
flag *pflag.Flag
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasChanged returns whether the flag has changes or not.
|
// HasChanges returns whether the flag has changes or not.
|
||||||
func (p pflagValue) HasChanged() bool {
|
func (p pflagValue) HasChanged() bool {
|
||||||
return p.flag.Changed
|
return p.flag.Changed
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,20 +5,18 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBindFlagValueSet(t *testing.T) {
|
func TestBindFlagValueSet(t *testing.T) {
|
||||||
Reset()
|
|
||||||
flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||||
|
|
||||||
testValues := map[string]*string{
|
var testValues = map[string]*string{
|
||||||
"host": nil,
|
"host": nil,
|
||||||
"port": nil,
|
"port": nil,
|
||||||
"endpoint": nil,
|
"endpoint": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
mutatedTestValues := map[string]string{
|
var mutatedTestValues = map[string]string{
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": "6060",
|
"port": "6060",
|
||||||
"endpoint": "/public",
|
"endpoint": "/public",
|
||||||
|
@ -31,7 +29,9 @@ func TestBindFlagValueSet(t *testing.T) {
|
||||||
flagValueSet := pflagValueSet{flagSet}
|
flagValueSet := pflagValueSet{flagSet}
|
||||||
|
|
||||||
err := BindFlagValues(flagValueSet)
|
err := BindFlagValues(flagValueSet)
|
||||||
require.NoError(t, err, "error binding flag set")
|
if err != nil {
|
||||||
|
t.Fatalf("error binding flag set, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
flagSet.VisitAll(func(flag *pflag.Flag) {
|
flagSet.VisitAll(func(flag *pflag.Flag) {
|
||||||
flag.Value.Set(mutatedTestValues[flag.Name])
|
flag.Value.Set(mutatedTestValues[flag.Name])
|
||||||
|
@ -39,13 +39,13 @@ func TestBindFlagValueSet(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
for name, expected := range mutatedTestValues {
|
for name, expected := range mutatedTestValues {
|
||||||
assert.Equal(t, expected, Get(name))
|
assert.Equal(t, Get(name), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBindFlagValue(t *testing.T) {
|
func TestBindFlagValue(t *testing.T) {
|
||||||
testString := "testing"
|
var testString = "testing"
|
||||||
testValue := newStringValue(testString, &testString)
|
var testValue = newStringValue(testString, &testString)
|
||||||
|
|
||||||
flag := &pflag.Flag{
|
flag := &pflag.Flag{
|
||||||
Name: "testflag",
|
Name: "testflag",
|
||||||
|
@ -59,7 +59,7 @@ func TestBindFlagValue(t *testing.T) {
|
||||||
assert.Equal(t, testString, Get("testvalue"))
|
assert.Equal(t, testString, Get("testvalue"))
|
||||||
|
|
||||||
flag.Value.Set("testing_mutate")
|
flag.Value.Set("testing_mutate")
|
||||||
flag.Changed = true // hack for pflag usage
|
flag.Changed = true //hack for pflag usage
|
||||||
|
|
||||||
assert.Equal(t, "testing_mutate", Get("testvalue"))
|
assert.Equal(t, "testing_mutate", Get("testvalue"))
|
||||||
}
|
}
|
||||||
|
|
472
flake.lock
472
flake.lock
|
@ -1,472 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"cachix": {
|
|
||||||
"inputs": {
|
|
||||||
"devenv": "devenv_2",
|
|
||||||
"flake-compat": [
|
|
||||||
"devenv",
|
|
||||||
"flake-compat"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
|
||||||
"devenv",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"pre-commit-hooks": [
|
|
||||||
"devenv",
|
|
||||||
"pre-commit-hooks"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1712055811,
|
|
||||||
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "cachix",
|
|
||||||
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "cachix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"devenv": {
|
|
||||||
"inputs": {
|
|
||||||
"cachix": "cachix",
|
|
||||||
"flake-compat": "flake-compat_2",
|
|
||||||
"nix": "nix_2",
|
|
||||||
"nixpkgs": "nixpkgs_2",
|
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1717245169,
|
|
||||||
"narHash": "sha256-+mW3rTBjGU8p1THJN0lX/Dd/8FbnF+3dB+mJuSaxewE=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "devenv",
|
|
||||||
"rev": "c3f9f053c077c6f88a3de5276d9178c62baa3fc3",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "devenv",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"devenv_2": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": [
|
|
||||||
"devenv",
|
|
||||||
"cachix",
|
|
||||||
"flake-compat"
|
|
||||||
],
|
|
||||||
"nix": "nix",
|
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"poetry2nix": "poetry2nix",
|
|
||||||
"pre-commit-hooks": [
|
|
||||||
"devenv",
|
|
||||||
"cachix",
|
|
||||||
"pre-commit-hooks"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1708704632,
|
|
||||||
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "devenv",
|
|
||||||
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"ref": "python-rewrite",
|
|
||||||
"repo": "devenv",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-compat": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1673956053,
|
|
||||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-compat_2": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1696426674,
|
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-parts": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1717285511,
|
|
||||||
"narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "hercules-ci",
|
|
||||||
"repo": "flake-parts",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1689068808,
|
|
||||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils_2": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1710146030,
|
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gitignore": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"devenv",
|
|
||||||
"pre-commit-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": "flake-compat",
|
|
||||||
"nixpkgs": [
|
|
||||||
"devenv",
|
|
||||||
"cachix",
|
|
||||||
"devenv",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-regression": "nixpkgs-regression"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1712911606,
|
|
||||||
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
|
||||||
"owner": "domenkozar",
|
|
||||||
"repo": "nix",
|
|
||||||
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "domenkozar",
|
|
||||||
"ref": "devenv-2.21",
|
|
||||||
"repo": "nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix-github-actions": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"devenv",
|
|
||||||
"cachix",
|
|
||||||
"devenv",
|
|
||||||
"poetry2nix",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1688870561,
|
|
||||||
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-github-actions",
|
|
||||||
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-github-actions",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix_2": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": [
|
|
||||||
"devenv",
|
|
||||||
"flake-compat"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
|
||||||
"devenv",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-regression": "nixpkgs-regression_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1712911606,
|
|
||||||
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
|
||||||
"owner": "domenkozar",
|
|
||||||
"repo": "nix",
|
|
||||||
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "domenkozar",
|
|
||||||
"ref": "devenv-2.21",
|
|
||||||
"repo": "nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1692808169,
|
|
||||||
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-lib": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1717284937,
|
|
||||||
"narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
|
|
||||||
"type": "tarball",
|
|
||||||
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "tarball",
|
|
||||||
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-regression": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1643052045,
|
|
||||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-regression_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1643052045,
|
|
||||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-stable": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1710695816,
|
|
||||||
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-23.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1713361204,
|
|
||||||
"narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "devenv-nixpkgs",
|
|
||||||
"rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"ref": "rolling",
|
|
||||||
"repo": "devenv-nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_3": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1717112898,
|
|
||||||
"narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"poetry2nix": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nix-github-actions": "nix-github-actions",
|
|
||||||
"nixpkgs": [
|
|
||||||
"devenv",
|
|
||||||
"cachix",
|
|
||||||
"devenv",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1692876271,
|
|
||||||
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "poetry2nix",
|
|
||||||
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "poetry2nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pre-commit-hooks": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": [
|
|
||||||
"devenv",
|
|
||||||
"flake-compat"
|
|
||||||
],
|
|
||||||
"flake-utils": "flake-utils_2",
|
|
||||||
"gitignore": "gitignore",
|
|
||||||
"nixpkgs": [
|
|
||||||
"devenv",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1713775815,
|
|
||||||
"narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"devenv": "devenv",
|
|
||||||
"flake-parts": "flake-parts",
|
|
||||||
"nixpkgs": "nixpkgs_3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
57
flake.nix
57
flake.nix
|
@ -1,57 +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 = { config, self', inputs', pkgs, system, ... }: rec {
|
|
||||||
devenv.shells = {
|
|
||||||
default = {
|
|
||||||
languages = {
|
|
||||||
go.enable = true;
|
|
||||||
go.package = pkgs.go_1_22;
|
|
||||||
};
|
|
||||||
|
|
||||||
pre-commit.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 { };
|
|
||||||
};
|
|
||||||
|
|
||||||
ci = devenv.shells.default;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
27
go.mod
27
go.mod
|
@ -1,27 +0,0 @@
|
||||||
module github.com/spf13/viper
|
|
||||||
|
|
||||||
go 1.21
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2
|
|
||||||
github.com/sagikazarmark/locafero v0.6.0
|
|
||||||
github.com/spf13/afero v1.11.0
|
|
||||||
github.com/spf13/cast v1.6.0
|
|
||||||
github.com/spf13/pflag v1.0.5
|
|
||||||
github.com/stretchr/testify v1.9.0
|
|
||||||
github.com/subosito/gotenv v1.6.0
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
|
||||||
golang.org/x/text v0.15.0 // indirect
|
|
||||||
)
|
|
57
go.sum
57
go.sum
|
@ -1,57 +0,0 @@
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
||||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
|
||||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
|
@ -1,61 +0,0 @@
|
||||||
package encoding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Decoder decodes the contents of b into v.
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ErrDecoderNotFound is returned when there is no decoder registered for a format.
|
|
||||||
ErrDecoderNotFound = encodingError("decoder not found for this format")
|
|
||||||
|
|
||||||
// ErrDecoderFormatAlreadyRegistered is returned when an decoder is already registered for a format.
|
|
||||||
ErrDecoderFormatAlreadyRegistered = encodingError("decoder already registered for this format")
|
|
||||||
)
|
|
||||||
|
|
||||||
// DecoderRegistry can choose an appropriate Decoder based on the provided format.
|
|
||||||
type DecoderRegistry struct {
|
|
||||||
decoders map[string]Decoder
|
|
||||||
|
|
||||||
mu sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDecoderRegistry returns a new, initialized DecoderRegistry.
|
|
||||||
func NewDecoderRegistry() *DecoderRegistry {
|
|
||||||
return &DecoderRegistry{
|
|
||||||
decoders: make(map[string]Decoder),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterDecoder registers a Decoder for a format.
|
|
||||||
// Registering a Decoder for an already existing format is not supported.
|
|
||||||
func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
|
|
||||||
if _, ok := e.decoders[format]; ok {
|
|
||||||
return ErrDecoderFormatAlreadyRegistered
|
|
||||||
}
|
|
||||||
|
|
||||||
e.decoders[format] = enc
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode calls the underlying Decoder based on the format.
|
|
||||||
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]any) error {
|
|
||||||
e.mu.RLock()
|
|
||||||
decoder, ok := e.decoders[format]
|
|
||||||
e.mu.RUnlock()
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return ErrDecoderNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoder.Decode(b, v)
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package encoding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type decoder struct {
|
|
||||||
v map[string]any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d decoder) Decode(_ []byte, v map[string]any) error {
|
|
||||||
for key, value := range d.v {
|
|
||||||
v[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecoderRegistry_RegisterDecoder(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
registry := NewDecoderRegistry()
|
|
||||||
|
|
||||||
err := registry.RegisterDecoder("myformat", decoder{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("AlreadyRegistered", func(t *testing.T) {
|
|
||||||
registry := NewDecoderRegistry()
|
|
||||||
|
|
||||||
err := registry.RegisterDecoder("myformat", decoder{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = registry.RegisterDecoder("myformat", decoder{})
|
|
||||||
assert.ErrorIs(t, err, ErrDecoderFormatAlreadyRegistered)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecoderRegistry_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
registry := NewDecoderRegistry()
|
|
||||||
decoder := decoder{
|
|
||||||
v: map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := registry.RegisterDecoder("myformat", decoder)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err = registry.Decode("myformat", []byte("key: value"), v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, decoder.v, v)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("DecoderNotFound", func(t *testing.T) {
|
|
||||||
registry := NewDecoderRegistry()
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := registry.Decode("myformat", nil, v)
|
|
||||||
assert.ErrorIs(t, err, ErrDecoderNotFound)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package dotenv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// original form of the data.
|
|
||||||
const original = `# key-value pair
|
|
||||||
KEY=value
|
|
||||||
`
|
|
||||||
|
|
||||||
// encoded form of the data.
|
|
||||||
const encoded = `KEY=value
|
|
||||||
`
|
|
||||||
|
|
||||||
// data is Viper's internal representation.
|
|
||||||
var data = map[string]any{
|
|
||||||
"KEY": "value",
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, encoded, string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, data, v)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
require.Error(t, err)
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package encoding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encoder encodes the contents of v 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ErrEncoderNotFound is returned when there is no encoder registered for a format.
|
|
||||||
ErrEncoderNotFound = encodingError("encoder not found for this format")
|
|
||||||
|
|
||||||
// ErrEncoderFormatAlreadyRegistered is returned when an encoder is already registered for a format.
|
|
||||||
ErrEncoderFormatAlreadyRegistered = encodingError("encoder already registered for this format")
|
|
||||||
)
|
|
||||||
|
|
||||||
// EncoderRegistry can choose an appropriate Encoder based on the provided format.
|
|
||||||
type EncoderRegistry struct {
|
|
||||||
encoders map[string]Encoder
|
|
||||||
|
|
||||||
mu sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoderRegistry returns a new, initialized EncoderRegistry.
|
|
||||||
func NewEncoderRegistry() *EncoderRegistry {
|
|
||||||
return &EncoderRegistry{
|
|
||||||
encoders: make(map[string]Encoder),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterEncoder registers an Encoder for a format.
|
|
||||||
// Registering a Encoder for an already existing format is not supported.
|
|
||||||
func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
|
|
||||||
if _, ok := e.encoders[format]; ok {
|
|
||||||
return ErrEncoderFormatAlreadyRegistered
|
|
||||||
}
|
|
||||||
|
|
||||||
e.encoders[format] = enc
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EncoderRegistry) Encode(format string, v map[string]any) ([]byte, error) {
|
|
||||||
e.mu.RLock()
|
|
||||||
encoder, ok := e.encoders[format]
|
|
||||||
e.mu.RUnlock()
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrEncoderNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return encoder.Encode(v)
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package encoding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type encoder struct {
|
|
||||||
b []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e encoder) Encode(_ map[string]any) ([]byte, error) {
|
|
||||||
return e.b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncoderRegistry_RegisterEncoder(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
registry := NewEncoderRegistry()
|
|
||||||
|
|
||||||
err := registry.RegisterEncoder("myformat", encoder{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("AlreadyRegistered", func(t *testing.T) {
|
|
||||||
registry := NewEncoderRegistry()
|
|
||||||
|
|
||||||
err := registry.RegisterEncoder("myformat", encoder{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = registry.RegisterEncoder("myformat", encoder{})
|
|
||||||
assert.ErrorIs(t, err, ErrEncoderFormatAlreadyRegistered)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncoderRegistry_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
registry := NewEncoderRegistry()
|
|
||||||
encoder := encoder{
|
|
||||||
b: []byte("key: value"),
|
|
||||||
}
|
|
||||||
|
|
||||||
err := registry.RegisterEncoder("myformat", encoder)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
b, err := registry.Encode("myformat", map[string]any{"key": "value"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "key: value", string(b))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("EncoderNotFound", func(t *testing.T) {
|
|
||||||
registry := NewEncoderRegistry()
|
|
||||||
|
|
||||||
_, err := registry.Encode("myformat", map[string]any{"key": "value"})
|
|
||||||
assert.ErrorIs(t, err, ErrEncoderNotFound)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package encoding
|
|
||||||
|
|
||||||
type encodingError string
|
|
||||||
|
|
||||||
func (e encodingError) Error() string {
|
|
||||||
return string(e)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
package json
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// encoded form of the data.
|
|
||||||
const encoded = `{
|
|
||||||
"key": "value",
|
|
||||||
"list": [
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3"
|
|
||||||
],
|
|
||||||
"map": {
|
|
||||||
"key": "value"
|
|
||||||
},
|
|
||||||
"nested_map": {
|
|
||||||
"map": {
|
|
||||||
"key": "value",
|
|
||||||
"list": [
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
// data is Viper's internal representation.
|
|
||||||
var data = map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]any{
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, encoded, string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(encoded), v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, data, v)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
require.Error(t, err)
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// original form of the data.
|
|
||||||
const original = `# key-value pair
|
|
||||||
key = "value"
|
|
||||||
list = ["item1", "item2", "item3"]
|
|
||||||
|
|
||||||
[map]
|
|
||||||
key = "value"
|
|
||||||
|
|
||||||
# nested
|
|
||||||
# map
|
|
||||||
[nested_map]
|
|
||||||
[nested_map.map]
|
|
||||||
key = "value"
|
|
||||||
list = [
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
]
|
|
||||||
`
|
|
||||||
|
|
||||||
// encoded form of the data.
|
|
||||||
const encoded = `key = 'value'
|
|
||||||
list = ['item1', 'item2', 'item3']
|
|
||||||
|
|
||||||
[map]
|
|
||||||
key = 'value'
|
|
||||||
|
|
||||||
[nested_map]
|
|
||||||
[nested_map.map]
|
|
||||||
key = 'value'
|
|
||||||
list = ['item1', 'item2', 'item3']
|
|
||||||
`
|
|
||||||
|
|
||||||
// data is Viper's internal representation.
|
|
||||||
var data = map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]any{
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, encoded, string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, data, v)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
require.Error(t, err)
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package yaml
|
|
||||||
|
|
||||||
import "gopkg.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)
|
|
||||||
}
|
|
|
@ -1,128 +0,0 @@
|
||||||
package yaml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// original form of the data.
|
|
||||||
const original = `# key-value pair
|
|
||||||
key: value
|
|
||||||
list:
|
|
||||||
- item1
|
|
||||||
- item2
|
|
||||||
- item3
|
|
||||||
map:
|
|
||||||
key: value
|
|
||||||
|
|
||||||
# nested
|
|
||||||
# map
|
|
||||||
nested_map:
|
|
||||||
map:
|
|
||||||
key: value
|
|
||||||
list:
|
|
||||||
- item1
|
|
||||||
- item2
|
|
||||||
- item3
|
|
||||||
`
|
|
||||||
|
|
||||||
// encoded form of the data.
|
|
||||||
const encoded = `key: value
|
|
||||||
list:
|
|
||||||
- item1
|
|
||||||
- item2
|
|
||||||
- item3
|
|
||||||
map:
|
|
||||||
key: value
|
|
||||||
nested_map:
|
|
||||||
map:
|
|
||||||
key: value
|
|
||||||
list:
|
|
||||||
- item1
|
|
||||||
- item2
|
|
||||||
- item3
|
|
||||||
`
|
|
||||||
|
|
||||||
// decoded form of the data.
|
|
||||||
//
|
|
||||||
// In case of YAML it's slightly different from Viper's internal representation
|
|
||||||
// (e.g. map is decoded into a map with interface key).
|
|
||||||
var decoded = map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]any{
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// data is Viper's internal representation.
|
|
||||||
var data = map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]any{
|
|
||||||
"map": map[string]any{
|
|
||||||
"key": "value",
|
|
||||||
"list": []any{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, encoded, string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, decoded, v)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]any{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
require.Error(t, err)
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
//go:build viper_bind_struct
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
const BindStruct = true
|
|
|
@ -1,5 +0,0 @@
|
||||||
//go:build !viper_bind_struct
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
const BindStruct = false
|
|
|
@ -1,5 +0,0 @@
|
||||||
//go:build viper_finder
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
const Finder = true
|
|
|
@ -1,5 +0,0 @@
|
||||||
//go:build !viper_finder
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
const Finder = false
|
|
|
@ -1,18 +0,0 @@
|
||||||
package testutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AbsFilePath calls filepath.Abs on path.
|
|
||||||
func AbsFilePath(t *testing.T, path string) string {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
s, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
31
logger.go
31
logger.go
|
@ -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
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
package viper
|
package viper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -45,11 +46,11 @@ func TestNestedOverrides(t *testing.T) {
|
||||||
deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4)
|
deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4)
|
||||||
|
|
||||||
// Case 4: key:value overridden by a map
|
// Case 4: key:value overridden by a map
|
||||||
v = overrideDefault(assert, "tom.size", 4, "tom", map[string]any{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10}
|
v = overrideDefault(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10}
|
||||||
assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable
|
assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable
|
||||||
assert.Equal(10, v.Get("tom.age")) // new value should be there
|
assert.Equal(10, v.Get("tom.age")) // new value should be there
|
||||||
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there
|
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there
|
||||||
v = override(assert, "tom.size", 4, "tom", map[string]any{"age": 10})
|
v = override(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10})
|
||||||
assert.Nil(v.Get("tom.size"))
|
assert.Nil(v.Get("tom.size"))
|
||||||
assert.Equal(10, v.Get("tom.age"))
|
assert.Equal(10, v.Get("tom.age"))
|
||||||
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10)
|
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10)
|
||||||
|
@ -74,11 +75,10 @@ func TestNestedOverrides(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func overrideDefault(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
|
func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
|
||||||
return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
|
return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
|
||||||
}
|
}
|
||||||
|
func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
|
||||||
func override(assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
|
|
||||||
return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
|
return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,11 +93,11 @@ func override(assert *assert.Assertions, firstPath string, firstValue any, secon
|
||||||
//
|
//
|
||||||
// After each assignment, the value is checked, retrieved both by its full path
|
// After each assignment, the value is checked, retrieved both by its full path
|
||||||
// and by its key sequence (successive maps).
|
// and by its key sequence (successive maps).
|
||||||
func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue any, secondPath string, secondValue any) *Viper {
|
func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
|
||||||
v := New()
|
v := New()
|
||||||
firstKeys := strings.Split(firstPath, v.keyDelim)
|
firstKeys := strings.Split(firstPath, v.keyDelim)
|
||||||
if assert == nil ||
|
if assert == nil ||
|
||||||
len(firstKeys) == 0 || firstKeys[0] == "" {
|
len(firstKeys) == 0 || len(firstKeys[0]) == 0 {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, fir
|
||||||
|
|
||||||
// Override and check new value
|
// Override and check new value
|
||||||
secondKeys := strings.Split(secondPath, v.keyDelim)
|
secondKeys := strings.Split(secondPath, v.keyDelim)
|
||||||
if len(secondKeys) == 0 || secondKeys[0] == "" {
|
if len(secondKeys) == 0 || len(secondKeys[0]) == 0 {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
v.Set(secondPath, secondValue)
|
v.Set(secondPath, secondValue)
|
||||||
|
@ -126,15 +126,15 @@ func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, fir
|
||||||
}
|
}
|
||||||
|
|
||||||
// deepCheckValue checks that all given keys correspond to a valid path in the
|
// deepCheckValue checks that all given keys correspond to a valid path in the
|
||||||
// configuration map of the given layer, and that the final value equals the one given.
|
// configuration map of the given layer, and that the final value equals the one given
|
||||||
func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value any) {
|
func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value interface{}) {
|
||||||
if assert == nil || v == nil ||
|
if assert == nil || v == nil ||
|
||||||
len(keys) == 0 || keys[0] == "" {
|
len(keys) == 0 || len(keys[0]) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
var val any
|
var val interface{}
|
||||||
var ms string
|
var ms string
|
||||||
switch l {
|
switch l {
|
||||||
case defaultLayer:
|
case defaultLayer:
|
||||||
|
@ -146,25 +146,28 @@ func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop through map
|
// loop through map
|
||||||
var m map[string]any
|
var m map[string]interface{}
|
||||||
|
err := false
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if val == nil {
|
if val == nil {
|
||||||
assert.Failf("%s is not a map[string]any", ms)
|
assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// deep scan of the map to get the final value
|
// deep scan of the map to get the final value
|
||||||
switch val := val.(type) {
|
switch val.(type) {
|
||||||
case map[any]any:
|
case map[interface{}]interface{}:
|
||||||
m = cast.ToStringMap(val)
|
m = cast.ToStringMap(val)
|
||||||
case map[string]any:
|
case map[string]interface{}:
|
||||||
m = val
|
m = val.(map[string]interface{})
|
||||||
default:
|
default:
|
||||||
assert.Failf("%s is not a map[string]any", ms)
|
assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ms = ms + "[\"" + k + "\"]"
|
ms = ms + "[\"" + k + "\"]"
|
||||||
val = m[k]
|
val = m[k]
|
||||||
}
|
}
|
||||||
assert.Equal(value, val)
|
if !err {
|
||||||
|
assert.Equal(value, val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
255
remote.go
255
remote.go
|
@ -1,255 +0,0 @@
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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 !stringInSlice(provider, SupportedRemoteProviders) {
|
|
||||||
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 !stringInSlice(provider, SupportedRemoteProviders) {
|
|
||||||
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)
|
|
||||||
v.unmarshalReader(reader, v.kvstore)
|
|
||||||
}
|
|
||||||
}(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
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
module github.com/spf13/viper/remote
|
|
||||||
|
|
||||||
go 1.21
|
|
||||||
|
|
||||||
replace github.com/spf13/viper => ../
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/sagikazarmark/crypt v0.22.0
|
|
||||||
github.com/spf13/viper v1.20.0-alpha.5
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
cloud.google.com/go v0.115.0 // indirect
|
|
||||||
cloud.google.com/go/auth v0.6.0 // indirect
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
|
||||||
cloud.google.com/go/firestore v1.15.0 // indirect
|
|
||||||
cloud.google.com/go/longrunning v0.5.7 // indirect
|
|
||||||
github.com/armon/go-metrics v0.4.1 // indirect
|
|
||||||
github.com/coreos/go-semver v0.3.0 // indirect
|
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
|
||||||
github.com/hashicorp/consul/api v1.29.1 // indirect
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
|
||||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
|
||||||
github.com/hashicorp/serf v0.10.1 // indirect
|
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
|
||||||
github.com/klauspost/compress v1.17.2 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
|
||||||
github.com/nats-io/nats.go v1.36.0 // indirect
|
|
||||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
|
||||||
go.etcd.io/etcd/api/v3 v3.5.14 // indirect
|
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
|
|
||||||
go.etcd.io/etcd/client/v2 v2.305.14 // indirect
|
|
||||||
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
|
|
||||||
go.opencensus.io v0.24.0 // indirect
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
|
||||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
go.uber.org/zap v1.21.0 // indirect
|
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
|
|
||||||
golang.org/x/net v0.26.0 // indirect
|
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
|
||||||
golang.org/x/text v0.16.0 // indirect
|
|
||||||
golang.org/x/time v0.5.0 // indirect
|
|
||||||
google.golang.org/api v0.186.0 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20240617180043-68d350f18fd4 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 // indirect
|
|
||||||
google.golang.org/grpc v1.64.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
|
460
remote/go.sum
460
remote/go.sum
|
@ -1,460 +0,0 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
|
||||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
|
||||||
cloud.google.com/go/auth v0.6.0 h1:5x+d6b5zdezZ7gmLWD1m/xNjnaQ2YDhmIz/HH3doy1g=
|
|
||||||
cloud.google.com/go/auth v0.6.0/go.mod h1:b4acV+jLQDyjwm4OXHYjNvRi4jvGBzHWJRtJcy+2P4g=
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
|
||||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
|
||||||
cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8=
|
|
||||||
cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk=
|
|
||||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
|
||||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
|
||||||
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
|
|
||||||
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
|
||||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
|
|
||||||
github.com/hashicorp/consul/api v1.29.1 h1:UEwOjYJrd3lG1x5w7HxDRMGiAUPrb3f103EoeKuuEcc=
|
|
||||||
github.com/hashicorp/consul/api v1.29.1/go.mod h1:lumfRkY/coLuqMICkI7Fh3ylMG31mQSRZyef2c5YvJI=
|
|
||||||
github.com/hashicorp/consul/proto-public v0.6.1 h1:+uzH3olCrksXYWAYHKqK782CtK9scfqH+Unlw3UHhCg=
|
|
||||||
github.com/hashicorp/consul/proto-public v0.6.1/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg=
|
|
||||||
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
|
|
||||||
github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
|
||||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
|
||||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
|
|
||||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
|
||||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
|
||||||
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
|
|
||||||
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
|
||||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
|
||||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
|
||||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
|
||||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
|
||||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
|
|
||||||
github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
|
||||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
|
||||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
|
||||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
|
||||||
github.com/sagikazarmark/crypt v0.22.0 h1:zGAI4zBcXv2dQ0bAzZXrNA6LIrUmTgvGrY21mLUX6dI=
|
|
||||||
github.com/sagikazarmark/crypt v0.22.0/go.mod h1:5lA2e9dniGB9XbbuBoljtjsHFD0a3bmOe2JiOOHV+mU=
|
|
||||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
|
||||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0=
|
|
||||||
go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU=
|
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ=
|
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI=
|
|
||||||
go.etcd.io/etcd/client/v2 v2.305.14 h1:v5ASLyFuMlVd/gKU6uf6Cod+vSWKa4Rsv9+eghl0Nwk=
|
|
||||||
go.etcd.io/etcd/client/v2 v2.305.14/go.mod h1:AWYT0lLEkBuqVaGw0UVMtA4rxCb3/oGE8PxZ8cUS4tI=
|
|
||||||
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
|
|
||||||
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
|
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
|
||||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
|
||||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
|
||||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
|
||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
|
||||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
|
||||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
|
||||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
|
||||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.186.0 h1:n2OPp+PPXX0Axh4GuSsL5QL8xQCTb2oDwyzPnQvqUug=
|
|
||||||
google.golang.org/api v0.186.0/go.mod h1:hvRbBmgoje49RV3xqVXrmP6w93n6ehGgIVPYrGtBFFc=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto v0.0.0-20240617180043-68d350f18fd4 h1:CUiCqkPw1nNrNQzCCG4WA65m0nAmQiwXHpub3dNyruU=
|
|
||||||
google.golang.org/genproto v0.0.0-20240617180043-68d350f18fd4/go.mod h1:EvuUDCulqGgV80RvP1BHuom+smhX4qtlhnNatHuroGQ=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 h1:Di6ANFilr+S60a4S61ZM00vLdw0IrQOSMS2/6mrnOU0=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
|
@ -10,11 +10,9 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
crypt "github.com/sagikazarmark/crypt/config"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
crypt "github.com/xordataexchange/crypt/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type remoteConfigProvider struct{}
|
type remoteConfigProvider struct{}
|
||||||
|
@ -65,7 +63,9 @@ func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *vi
|
||||||
Error: resp.Error,
|
Error: resp.Error,
|
||||||
Value: resp.Value,
|
Value: resp.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}(cryptoResponseCh, viperResponsCh, quitwc, quit)
|
}(cryptoResponseCh, viperResponsCh, quitwc, quit)
|
||||||
|
|
||||||
|
@ -76,38 +76,22 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
|
||||||
var cm crypt.ConfigManager
|
var cm crypt.ConfigManager
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
endpoints := strings.Split(rp.Endpoint(), ";")
|
|
||||||
if rp.SecretKeyring() != "" {
|
if rp.SecretKeyring() != "" {
|
||||||
var kr *os.File
|
kr, err := os.Open(rp.SecretKeyring())
|
||||||
kr, err = os.Open(rp.SecretKeyring())
|
defer kr.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer kr.Close()
|
if rp.Provider() == "etcd" {
|
||||||
switch rp.Provider() {
|
cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
|
||||||
case "etcd":
|
} else {
|
||||||
cm, err = crypt.NewEtcdConfigManager(endpoints, kr)
|
cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
|
||||||
case "etcd3":
|
|
||||||
cm, err = crypt.NewEtcdV3ConfigManager(endpoints, kr)
|
|
||||||
case "firestore":
|
|
||||||
cm, err = crypt.NewFirestoreConfigManager(endpoints, kr)
|
|
||||||
case "nats":
|
|
||||||
cm, err = crypt.NewNatsConfigManager(endpoints, kr)
|
|
||||||
default:
|
|
||||||
cm, err = crypt.NewConsulConfigManager(endpoints, kr)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch rp.Provider() {
|
if rp.Provider() == "etcd" {
|
||||||
case "etcd":
|
cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
|
||||||
cm, err = crypt.NewStandardEtcdConfigManager(endpoints)
|
} else {
|
||||||
case "etcd3":
|
cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
|
||||||
cm, err = crypt.NewStandardEtcdV3ConfigManager(endpoints)
|
|
||||||
case "firestore":
|
|
||||||
cm, err = crypt.NewStandardFirestoreConfigManager(endpoints)
|
|
||||||
case "nats":
|
|
||||||
cm, err = crypt.NewStandardNatsConfigManager(endpoints)
|
|
||||||
default:
|
|
||||||
cm, err = crypt.NewStandardConsulConfigManager(endpoints)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
94
util.go
94
util.go
|
@ -12,14 +12,15 @@ package viper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigParseError denotes failing to parse configuration file.
|
// ConfigParseError denotes failing to parse configuration file.
|
||||||
|
@ -32,18 +33,13 @@ func (pe ConfigParseError) Error() string {
|
||||||
return fmt.Sprintf("While parsing config: %s", pe.err.Error())
|
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;
|
// toCaseInsensitiveValue checks if the value is a map;
|
||||||
// if so, create a copy and lower-case the keys recursively.
|
// if so, create a copy and lower-case the keys recursively.
|
||||||
func toCaseInsensitiveValue(value any) any {
|
func toCaseInsensitiveValue(value interface{}) interface{} {
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case map[any]any:
|
case map[interface{}]interface{}:
|
||||||
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
||||||
case map[string]any:
|
case map[string]interface{}:
|
||||||
value = copyAndInsensitiviseMap(v)
|
value = copyAndInsensitiviseMap(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,15 +48,15 @@ func toCaseInsensitiveValue(value any) any {
|
||||||
|
|
||||||
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
|
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
|
||||||
// any map it makes case insensitive.
|
// any map it makes case insensitive.
|
||||||
func copyAndInsensitiviseMap(m map[string]any) map[string]any {
|
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
||||||
nm := make(map[string]any)
|
nm := make(map[string]interface{})
|
||||||
|
|
||||||
for key, val := range m {
|
for key, val := range m {
|
||||||
lkey := strings.ToLower(key)
|
lkey := strings.ToLower(key)
|
||||||
switch v := val.(type) {
|
switch v := val.(type) {
|
||||||
case map[any]any:
|
case map[interface{}]interface{}:
|
||||||
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
||||||
case map[string]any:
|
case map[string]interface{}:
|
||||||
nm[lkey] = copyAndInsensitiviseMap(v)
|
nm[lkey] = copyAndInsensitiviseMap(v)
|
||||||
default:
|
default:
|
||||||
nm[lkey] = v
|
nm[lkey] = v
|
||||||
|
@ -70,25 +66,18 @@ func copyAndInsensitiviseMap(m map[string]any) map[string]any {
|
||||||
return nm
|
return nm
|
||||||
}
|
}
|
||||||
|
|
||||||
func insensitiviseVal(val any) any {
|
func insensitiviseMap(m map[string]interface{}) {
|
||||||
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 {
|
for key, val := range m {
|
||||||
val = insensitiviseVal(val)
|
switch val.(type) {
|
||||||
|
case map[interface{}]interface{}:
|
||||||
|
// nested map: cast and recursively insensitivise
|
||||||
|
val = cast.ToStringMap(val)
|
||||||
|
insensitiviseMap(val.(map[string]interface{}))
|
||||||
|
case map[string]interface{}:
|
||||||
|
// nested map: recursively insensitivise
|
||||||
|
insensitiviseMap(val.(map[string]interface{}))
|
||||||
|
}
|
||||||
|
|
||||||
lower := strings.ToLower(key)
|
lower := strings.ToLower(key)
|
||||||
if key != lower {
|
if key != lower {
|
||||||
// remove old key (not lower-cased)
|
// remove old key (not lower-cased)
|
||||||
|
@ -99,20 +88,17 @@ func insensitiviseMap(m map[string]any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func insensitiveArray(a []any) {
|
func absPathify(inPath string) string {
|
||||||
for i, val := range a {
|
jww.INFO.Println("Trying to resolve absolute path to", inPath)
|
||||||
a[i] = insensitiviseVal(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func absPathify(logger *slog.Logger, inPath string) string {
|
if strings.HasPrefix(inPath, "$HOME") {
|
||||||
logger.Info("trying to resolve absolute path", "path", inPath)
|
|
||||||
|
|
||||||
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
|
|
||||||
inPath = userHomeDir() + inPath[5:]
|
inPath = userHomeDir() + inPath[5:]
|
||||||
}
|
}
|
||||||
|
|
||||||
inPath = os.ExpandEnv(inPath)
|
if strings.HasPrefix(inPath, "$") {
|
||||||
|
end := strings.Index(inPath, string(os.PathSeparator))
|
||||||
|
inPath = os.Getenv(inPath[1:end]) + inPath[end:]
|
||||||
|
}
|
||||||
|
|
||||||
if filepath.IsAbs(inPath) {
|
if filepath.IsAbs(inPath) {
|
||||||
return filepath.Clean(inPath)
|
return filepath.Clean(inPath)
|
||||||
|
@ -123,11 +109,23 @@ func absPathify(logger *slog.Logger, inPath string) string {
|
||||||
return filepath.Clean(p)
|
return filepath.Clean(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error(fmt.Errorf("could not discover absolute path: %w", err).Error())
|
jww.ERROR.Println("Couldn't discover absolute path")
|
||||||
|
jww.ERROR.Println(err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if File / Directory Exists
|
||||||
|
func exists(fs afero.Fs, path string) (bool, error) {
|
||||||
|
_, err := fs.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
func stringInSlice(a string, list []string) bool {
|
func stringInSlice(a string, list []string) bool {
|
||||||
for _, b := range list {
|
for _, b := range list {
|
||||||
if b == a {
|
if b == a {
|
||||||
|
@ -156,7 +154,7 @@ func safeMul(a, b uint) uint {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes.
|
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
|
||||||
func parseSizeInBytes(sizeStr string) uint {
|
func parseSizeInBytes(sizeStr string) uint {
|
||||||
sizeStr = strings.TrimSpace(sizeStr)
|
sizeStr = strings.TrimSpace(sizeStr)
|
||||||
lastChar := len(sizeStr) - 1
|
lastChar := len(sizeStr) - 1
|
||||||
|
@ -198,22 +196,22 @@ func parseSizeInBytes(sizeStr string) uint {
|
||||||
// In case intermediate keys do not exist, or map to a non-map value,
|
// 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:
|
// a new map is created and inserted, and the search continues from there:
|
||||||
// the initial map "m" may be modified!
|
// the initial map "m" may be modified!
|
||||||
func deepSearch(m map[string]any, path []string) map[string]any {
|
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
||||||
for _, k := range path {
|
for _, k := range path {
|
||||||
m2, ok := m[k]
|
m2, ok := m[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
// intermediate key does not exist
|
// intermediate key does not exist
|
||||||
// => create it and continue from there
|
// => create it and continue from there
|
||||||
m3 := make(map[string]any)
|
m3 := make(map[string]interface{})
|
||||||
m[k] = m3
|
m[k] = m3
|
||||||
m = m3
|
m = m3
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m3, ok := m2.(map[string]any)
|
m3, ok := m2.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
// intermediate key is a value
|
// intermediate key is a value
|
||||||
// => replace with a new map
|
// => replace with a new map
|
||||||
m3 = make(map[string]any)
|
m3 = make(map[string]interface{})
|
||||||
m[k] = m3
|
m[k] = m3
|
||||||
}
|
}
|
||||||
// continue search from here
|
// continue search from here
|
||||||
|
|
76
util_test.go
76
util_test.go
|
@ -11,76 +11,44 @@
|
||||||
package viper
|
package viper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log/slog"
|
"reflect"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCopyAndInsensitiviseMap(t *testing.T) {
|
func TestCopyAndInsensitiviseMap(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
given = map[string]any{
|
given = map[string]interface{}{
|
||||||
"Foo": 32,
|
"Foo": 32,
|
||||||
"Bar": map[any]any{
|
"Bar": map[interface{}]interface {
|
||||||
|
}{
|
||||||
"ABc": "A",
|
"ABc": "A",
|
||||||
"cDE": "B",
|
"cDE": "B"},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
expected = map[string]any{
|
expected = map[string]interface{}{
|
||||||
"foo": 32,
|
"foo": 32,
|
||||||
"bar": map[string]any{
|
"bar": map[string]interface {
|
||||||
|
}{
|
||||||
"abc": "A",
|
"abc": "A",
|
||||||
"cde": "B",
|
"cde": "B"},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
got := copyAndInsensitiviseMap(given)
|
got := copyAndInsensitiviseMap(given)
|
||||||
|
|
||||||
assert.Equal(t, expected, got)
|
if !reflect.DeepEqual(got, expected) {
|
||||||
_, ok := given["foo"]
|
t.Fatalf("Got %q\nexpected\n%q", got, expected)
|
||||||
assert.False(t, ok)
|
|
||||||
_, ok = given["bar"]
|
|
||||||
assert.False(t, ok)
|
|
||||||
|
|
||||||
m := given["Bar"].(map[any]any)
|
|
||||||
_, ok = m["ABc"]
|
|
||||||
assert.True(t, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAbsPathify(t *testing.T) {
|
|
||||||
skipWindows(t)
|
|
||||||
|
|
||||||
home := userHomeDir()
|
|
||||||
homer := filepath.Join(home, "homer")
|
|
||||||
wd, _ := os.Getwd()
|
|
||||||
|
|
||||||
t.Setenv("HOMER_ABSOLUTE_PATH", homer)
|
|
||||||
t.Setenv("VAR_WITH_RELATIVE_PATH", "relative")
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
output string
|
|
||||||
}{
|
|
||||||
{"", wd},
|
|
||||||
{"sub", filepath.Join(wd, "sub")},
|
|
||||||
{"./", wd},
|
|
||||||
{"./sub", filepath.Join(wd, "sub")},
|
|
||||||
{"$HOME", home},
|
|
||||||
{"$HOME/", home},
|
|
||||||
{"$HOME/sub", filepath.Join(home, "sub")},
|
|
||||||
{"$HOMER_ABSOLUTE_PATH", homer},
|
|
||||||
{"$HOMER_ABSOLUTE_PATH/", homer},
|
|
||||||
{"$HOMER_ABSOLUTE_PATH/sub", filepath.Join(homer, "sub")},
|
|
||||||
{"$VAR_WITH_RELATIVE_PATH", filepath.Join(wd, "relative")},
|
|
||||||
{"$VAR_WITH_RELATIVE_PATH/", filepath.Join(wd, "relative")},
|
|
||||||
{"$VAR_WITH_RELATIVE_PATH/sub", filepath.Join(wd, "relative", "sub")},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
if _, ok := given["foo"]; ok {
|
||||||
got := absPathify(slog.Default(), test.input)
|
t.Fatal("Input map changed")
|
||||||
assert.Equal(t, test.output, got)
|
}
|
||||||
|
|
||||||
|
if _, ok := given["bar"]; ok {
|
||||||
|
t.Fatal("Input map changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
m := given["Bar"].(map[interface{}]interface{})
|
||||||
|
if _, ok := m["ABc"]; !ok {
|
||||||
|
t.Fatal("Input map changed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2593
viper_test.go
2593
viper_test.go
File diff suppressed because it is too large
Load diff
|
@ -1,53 +0,0 @@
|
||||||
package viper
|
|
||||||
|
|
||||||
var yamlExample = []byte(`Hacker: true
|
|
||||||
name: steve
|
|
||||||
hobbies:
|
|
||||||
- skateboarding
|
|
||||||
- snowboarding
|
|
||||||
- go
|
|
||||||
clothing:
|
|
||||||
jacket: leather
|
|
||||||
trousers: denim
|
|
||||||
pants:
|
|
||||||
size: large
|
|
||||||
age: 35
|
|
||||||
eyes : brown
|
|
||||||
beard: true
|
|
||||||
`)
|
|
||||||
|
|
||||||
var yamlWriteExpected = []byte(`age: 35
|
|
||||||
beard: true
|
|
||||||
clothing:
|
|
||||||
jacket: leather
|
|
||||||
pants:
|
|
||||||
size: large
|
|
||||||
trousers: denim
|
|
||||||
eyes: brown
|
|
||||||
hacker: true
|
|
||||||
hobbies:
|
|
||||||
- skateboarding
|
|
||||||
- snowboarding
|
|
||||||
- go
|
|
||||||
name: steve
|
|
||||||
`)
|
|
||||||
|
|
||||||
var yamlExampleWithDot = []byte(`Hacker: true
|
|
||||||
name: steve
|
|
||||||
hobbies:
|
|
||||||
- skateboarding
|
|
||||||
- snowboarding
|
|
||||||
- go
|
|
||||||
clothing:
|
|
||||||
jacket: leather
|
|
||||||
trousers: denim
|
|
||||||
pants:
|
|
||||||
size: large
|
|
||||||
age: 35
|
|
||||||
eyes : brown
|
|
||||||
beard: true
|
|
||||||
emails:
|
|
||||||
steve@hacker.com:
|
|
||||||
created: 01/02/03
|
|
||||||
active: true
|
|
||||||
`)
|
|
Loading…
Reference in a new issue