forked from mirror/viper
Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
Mark Sagi-Kazar | 99e9188c7c | |
Mark Sagi-Kazar | c943b3ef27 | |
Mark Sagi-Kazar | a0f1caa4ed | |
Mark Sagi-Kazar | d2e3a7e5c2 |
|
@ -11,5 +11,5 @@ trim_trailing_whitespace = true
|
||||||
[*.go]
|
[*.go]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
[{Makefile,*.mk}]
|
[{Makefile, *.mk}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
|
@ -9,32 +9,23 @@ body:
|
||||||
|
|
||||||
Please fill out the template below to make it easier to debug your problem.
|
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://git.internal/re/viper/issues/new/choose).
|
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
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Preflight Checklist
|
label: Preflight Checklist
|
||||||
description: Please ensure you've completed all of the following.
|
description: Please ensure you've completed all of the following.
|
||||||
options:
|
options:
|
||||||
- label: I have searched the [issue tracker](https://www.git.internal/re/viper/issues) for an issue that matches the one I want to file, without success.
|
- 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
|
required: true
|
||||||
- label: I am not looking for support or already pursued the available [support channels](https://git.internal/re/viper/issues/new/choose) without success.
|
- 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://git.internal/re/viper/blob/master/TROUBLESHOOTING.md) for my problem, without success.
|
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Viper Version
|
label: Version
|
||||||
description: What version of Viper are you using?
|
description: What version of Viper are you using?
|
||||||
placeholder: 1.8.1
|
placeholder: 1.8.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Go Version
|
|
||||||
description: What version of Go are you using?
|
|
||||||
placeholder: "1.16"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
label: Config Source
|
label: Config Source
|
||||||
|
@ -77,7 +68,7 @@ body:
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.internal/re/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: ❓ Ask a question
|
- name: ❓ Ask a question
|
||||||
url: https://git.internal/re/viper/discussions/new?category=q-a
|
url: https://github.com/spf13/viper/discussions/new?category=q-a
|
||||||
about: Ask and discuss questions with other Viper community members
|
about: Ask and discuss questions with other Viper community members
|
||||||
|
|
||||||
- name: 📓 Reference
|
- name: 📓 Reference
|
||||||
url: https://pkg.go.dev/mod/git.internal/re/viper
|
url: https://pkg.go.dev/mod/github.com/spf13/viper
|
||||||
about: Check the Go code reference
|
about: Check the Go code reference
|
||||||
|
|
||||||
- name: 💬 Slack channel
|
- name: 💬 Slack channel
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: 🎉 Feature request
|
name: 🚀 Feature request
|
||||||
description: Suggest an idea for Viper
|
description: Suggest an idea for Viper
|
||||||
labels: [kind/enhancement]
|
labels: [kind/enhancement]
|
||||||
body:
|
body:
|
||||||
|
@ -9,13 +9,13 @@ body:
|
||||||
|
|
||||||
Please describe what you would like to change/add and why in detail by filling out the template below.
|
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://git.internal/re/viper/issues/new/choose).
|
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
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Preflight Checklist
|
label: Preflight Checklist
|
||||||
description: Please ensure you've completed all of the following.
|
description: Please ensure you've completed all of the following.
|
||||||
options:
|
options:
|
||||||
- label: I have searched the [issue tracker](https://www.git.internal/re/viper/issues) for an issue that matches the one I want to file, without success.
|
- 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
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
|
|
@ -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:
|
|
||||||
- "*"
|
|
|
@ -1,18 +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@v2
|
|
||||||
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"
|
|
|
@ -1,86 +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: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.19
|
|
||||||
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- 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.16', '1.17', '1.18', '1.19']
|
|
||||||
tags: ['', 'viper_yaml2', 'viper_toml1']
|
|
||||||
env:
|
|
||||||
GOFLAGS: -mod=readonly
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.go }}
|
|
||||||
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: go test -race -tags '${{ matrix.tags }}' -v ./...
|
|
||||||
if: runner.os != 'Windows'
|
|
||||||
|
|
||||||
- name: Test (without race detector)
|
|
||||||
run: go test -tags '${{ matrix.tags }}' -v ./...
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
|
|
||||||
lint:
|
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
GOFLAGS: -mod=readonly
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.19
|
|
||||||
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
uses: golangci/golangci-lint-action@v3
|
|
||||||
with:
|
|
||||||
version: v1.49
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest]
|
||||||
|
go: ['1.14', '1.15', '1.16']
|
||||||
|
env:
|
||||||
|
VERBOSE: 1
|
||||||
|
GOFLAGS: -mod=readonly
|
||||||
|
GOPROXY: https://proxy.golang.org
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: make lint
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: make test
|
|
@ -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@v3
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
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@v2
|
|
||||||
|
|
||||||
# ℹ️ 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@v2
|
|
||||||
|
|
|
@ -6,11 +6,11 @@ jobs:
|
||||||
comment:
|
comment:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.createComment({
|
github.issues.createComment({
|
||||||
issue_number: context.issue.number,
|
issue_number: context.issue.number,
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
|
@ -6,11 +6,11 @@ jobs:
|
||||||
comment:
|
comment:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.createComment({
|
github.issues.createComment({
|
||||||
issue_number: context.issue.number,
|
issue_number: context.issue.number,
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
|
@ -0,0 +1,26 @@
|
||||||
|
name: WASM
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GOFLAGS: -mod=readonly
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.16'
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Ensure Viper compiles for WASM
|
||||||
|
run: GOOS=js GOARCH=wasm go build .
|
|
@ -3,14 +3,11 @@ run:
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
gci:
|
gci:
|
||||||
sections:
|
local-prefixes: github.com/spf13/viper
|
||||||
- standard
|
|
||||||
- default
|
|
||||||
- prefix(git.internal/re/viper)
|
|
||||||
golint:
|
golint:
|
||||||
min-confidence: 0
|
min-confidence: 0
|
||||||
goimports:
|
goimports:
|
||||||
local-prefixes: git.internal/re/viper
|
local-prefixes: github.com/spf13/viper
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
|
@ -23,6 +20,7 @@ linters:
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exportloopref
|
- exportloopref
|
||||||
- gci
|
- gci
|
||||||
|
- goconst
|
||||||
- gofmt
|
- gofmt
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- goimports
|
- goimports
|
||||||
|
@ -64,7 +62,6 @@ linters:
|
||||||
# - gochecknoglobals
|
# - gochecknoglobals
|
||||||
# - gochecknoinits
|
# - gochecknoinits
|
||||||
# - gocognit
|
# - gocognit
|
||||||
# - goconst
|
|
||||||
# - gocritic
|
# - gocritic
|
||||||
# - gocyclo
|
# - gocyclo
|
||||||
# - godot
|
# - godot
|
6
Makefile
6
Makefile
|
@ -15,8 +15,8 @@ TEST_FORMAT = short-verbose
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Dependency versions
|
# Dependency versions
|
||||||
GOTESTSUM_VERSION = 1.8.0
|
GOTESTSUM_VERSION = 1.6.4
|
||||||
GOLANGCI_VERSION = 1.49.0
|
GOLANGCI_VERSION = 1.40.1
|
||||||
|
|
||||||
# Add the ability to override some variables
|
# Add the ability to override some variables
|
||||||
# Use with care
|
# Use with care
|
||||||
|
@ -48,7 +48,7 @@ bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION}
|
||||||
@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
|
@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
|
||||||
bin/golangci-lint-${GOLANGCI_VERSION}:
|
bin/golangci-lint-${GOLANGCI_VERSION}:
|
||||||
@mkdir -p bin
|
@mkdir -p bin
|
||||||
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
|
||||||
@mv bin/golangci-lint "$@"
|
@mv bin/golangci-lint "$@"
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
|
|
105
README.md
105
README.md
|
@ -8,11 +8,11 @@
|
||||||
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
|
[![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)
|
[![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/workflow/status/spf13/viper/CI?style=flat-square)](https://git.internal/re/viper/actions?query=workflow%3ACI)
|
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?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)
|
[![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/git.internal/re/viper?style=flat-square)](https://goreportcard.com/report/git.internal/re/viper)
|
[![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.16-61CFDD.svg?style=flat-square)
|
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.14-61CFDD.svg?style=flat-square)
|
||||||
[![PkgGoDev](https://pkg.go.dev/badge/mod/git.internal/re/viper)](https://pkg.go.dev/mod/git.internal/re/viper)
|
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)
|
||||||
|
|
||||||
**Go configuration with fangs!**
|
**Go configuration with fangs!**
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ Many Go projects are built using Viper including:
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
go get git.internal/re/viper
|
go get github.com/spf13/viper
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** Viper uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
|
**Note:** Viper uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
|
||||||
|
@ -119,7 +119,7 @@ viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search
|
||||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
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: %w \n", err))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -127,11 +127,11 @@ You can handle the specific case where no config file is found like this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
// Config file not found; ignore error if desired
|
// Config file not found; ignore error if desired
|
||||||
} else {
|
} else {
|
||||||
// Config file was found but another error was produced
|
// Config file was found but another error was produced
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config file found and successfully parsed
|
// Config file found and successfully parsed
|
||||||
|
@ -175,10 +175,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
|
||||||
|
@ -354,7 +354,7 @@ func main() {
|
||||||
|
|
||||||
i := viper.GetInt("flagname") // retrieve value from viper
|
i := viper.GetInt("flagname") // retrieve value from viper
|
||||||
|
|
||||||
// ...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ viper.BindFlagValues("my-flags", fSet)
|
||||||
To enable remote support in Viper, do a blank import of the `viper/remote`
|
To enable remote support in Viper, do a blank import of the `viper/remote`
|
||||||
package:
|
package:
|
||||||
|
|
||||||
`import _ "git.internal/re/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, HCL or envfile) 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
|
||||||
|
@ -447,13 +447,6 @@ viper.SetConfigType("json") // because there is no file extension in a stream of
|
||||||
err := viper.ReadRemoteConfig()
|
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
|
#### Consul
|
||||||
You need to set a key to Consul key/value storage with JSON value containing your desired config.
|
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:
|
For example, create a Consul key/value store key `MY_CONSUL_KEY` with value:
|
||||||
|
@ -510,18 +503,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)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
```
|
```
|
||||||
|
@ -553,7 +546,7 @@ 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
|
||||||
|
@ -601,7 +594,7 @@ configuration level.
|
||||||
|
|
||||||
Viper can access array indices by using numbers in the path. For example:
|
Viper can access array indices by using numbers in the path. For example:
|
||||||
|
|
||||||
```jsonc
|
```json
|
||||||
{
|
{
|
||||||
"host": {
|
"host": {
|
||||||
"address": "localhost",
|
"address": "localhost",
|
||||||
|
@ -629,7 +622,7 @@ 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": {
|
||||||
|
@ -676,7 +669,7 @@ So instead of doing that let's pass a Viper instance to the constructor that rep
|
||||||
```go
|
```go
|
||||||
cache1Config := viper.Sub("cache.cache1")
|
cache1Config := viper.Sub("cache.cache1")
|
||||||
if cache1Config == nil { // Sub returns nil if the key cannot be found
|
if cache1Config == nil { // Sub returns nil if the key cannot be found
|
||||||
panic("cache configuration not found")
|
panic("cache configuration not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
cache1 := NewCache(cache1Config)
|
cache1 := NewCache(cache1Config)
|
||||||
|
@ -688,10 +681,10 @@ Internally, the `NewCache` function can address `max-items` and `item-size` keys
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func NewCache(v *Viper) *Cache {
|
func NewCache(v *Viper) *Cache {
|
||||||
return &Cache{
|
return &Cache{
|
||||||
MaxItems: v.GetInt("max-items"),
|
MaxItems: v.GetInt("max-items"),
|
||||||
ItemSize: v.GetInt("item-size"),
|
ItemSize: v.GetInt("item-size"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -733,18 +726,18 @@ you have to change the delimiter:
|
||||||
v := viper.NewWithOptions(viper.KeyDelimiter("::"))
|
v := viper.NewWithOptions(viper.KeyDelimiter("::"))
|
||||||
|
|
||||||
v.SetDefault("chart::values", map[string]interface{}{
|
v.SetDefault("chart::values", map[string]interface{}{
|
||||||
"ingress": map[string]interface{}{
|
"ingress": map[string]interface{}{
|
||||||
"annotations": map[string]interface{}{
|
"annotations": map[string]interface{}{
|
||||||
"traefik.frontend.rule.type": "PathPrefix",
|
"traefik.frontend.rule.type": "PathPrefix",
|
||||||
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
|
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
Chart struct{
|
Chart struct{
|
||||||
Values map[string]interface{}
|
Values map[string]interface{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var C config
|
var C config
|
||||||
|
@ -801,17 +794,17 @@ You can use your favorite format's marshaller with the config returned by `AllSe
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
// ...
|
// ...
|
||||||
)
|
)
|
||||||
|
|
||||||
func yamlStringSettings() string {
|
func yamlStringSettings() string {
|
||||||
c := viper.AllSettings()
|
c := viper.AllSettings()
|
||||||
bs, err := yaml.Marshal(c)
|
bs, err := yaml.Marshal(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to marshal config to YAML: %v", err)
|
log.Fatalf("unable to marshal config to YAML: %v", err)
|
||||||
}
|
}
|
||||||
return string(bs)
|
return string(bs)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -868,7 +861,7 @@ Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Comm
|
||||||
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).
|
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.
|
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://git.internal/re/viper/issues/772), but despite the initial noise, it does not seem to be requested that much.
|
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
|
You can vote for case sensitivity by filling out this feedback form: https://forms.gle/R6faU74qPRPAzchZ9
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,3 @@ The solution is easy: switch to using Go Modules.
|
||||||
Please refer to the [wiki](https://github.com/golang/go/wiki/Modules) on how to do that.
|
Please refer to the [wiki](https://github.com/golang/go/wiki/Modules) on how to do that.
|
||||||
|
|
||||||
**tl;dr* `export GO111MODULE=on`
|
**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)
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# 1. Record architecture decisions
|
||||||
|
|
||||||
|
Date: 2021-07-20
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
We need to record the architectural decisions made on this project.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
|
|
@ -0,0 +1,32 @@
|
||||||
|
# 2. Prefer making backward compatible changes
|
||||||
|
|
||||||
|
Date: 2021-07-20
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
Referenced by [3. Extract components with heavy dependencies from the core](0003-extract-components-with-heavy-dependencies-from-the-core.md)
|
||||||
|
|
||||||
|
Referenced by [4. Use separate GitHub organization for new packages](0004-use-separate-github-organization-for-new-packages.md)
|
||||||
|
|
||||||
|
Referenced by [7. Drop writing support](0007-drop-writing-support.md)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Architecturally speaking Viper became a giant over the years: it hides a lot of complexity behind a simple interface.
|
||||||
|
That simple interface, however, is what makes Viper extremely popular.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
In order to keep the library useful to people, we should prefer making backward compatible changes to Viper, even between major releases.
|
||||||
|
This is not a hard rule forbiding breaking changes though: when it makes sense, breaking changes are allowed,
|
||||||
|
but keeping things backward compatible is a priority.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Although major versions allow breaking changes, a major release is no reason to break things that already work for a lot of people,
|
||||||
|
even if it might not be the best possible solution.
|
||||||
|
|
||||||
|
Instead of breaking things, introducing new interfaces should be the default way of fixing architectural problems,
|
||||||
|
leaving old interfaces intact.
|
|
@ -0,0 +1,26 @@
|
||||||
|
# 3. Extract components with heavy dependencies from the core
|
||||||
|
|
||||||
|
Date: 2021-07-20
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
References [2. Prefer making backward compatible changes](0002-prefer-making-backward-compatible-changes.md)
|
||||||
|
|
||||||
|
Referenced by [4. Use separate GitHub organization for new packages](0004-use-separate-github-organization-for-new-packages.md)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Viper (v1) currently imports a bunch of external dependencies (for encoding/decoding, remote stores, etc)
|
||||||
|
that make the library itself quite a heavy dependency.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Move components with external dependencies out of the core to separate packages.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Viper 1 will have to continue importing all of these packages to maintain backwards compatibility.
|
||||||
|
|
||||||
|
Viper 2 (and future versions) on the other hand can break backwards compatibility and require users to import the required packages.
|
|
@ -0,0 +1,24 @@
|
||||||
|
# 4. Use separate GitHub organization for new packages
|
||||||
|
|
||||||
|
Date: 2021-07-20
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
References [2. Prefer making backward compatible changes](0002-prefer-making-backward-compatible-changes.md)
|
||||||
|
|
||||||
|
References [3. Extract components with heavy dependencies from the core](0003-extract-components-with-heavy-dependencies-from-the-core.md)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The core Viper package is under a personal GitHub account which makes collaborative development a bit difficult.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Create new Go modules in the [go-viper](https://github.com/go-viper) organization.
|
||||||
|
Keep the core library under [Steve's personal account](https://github.com/spf13/viper) for backward compatibility purposes.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
It'll be easier to create new modules and to add new functionality to Viper without having to add new dependencies to the core library.
|
|
@ -0,0 +1,30 @@
|
||||||
|
# 5. Deprecate setters in favor of functional options during initialization
|
||||||
|
|
||||||
|
Date: 2021-07-20
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
Referenced by [8. Deprecate the global Viper instance](0008-deprecate-the-global-viper-instance.md)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The Viper struct currently acts as a facade for reading, writing and watching configuration for changes.
|
||||||
|
Some of the configuration parameters can be changed runtime using setters which often lead to issues
|
||||||
|
with concurrent activities.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Deprecate setters in favor of using functional options for configuring Viper when it's initialized.
|
||||||
|
|
||||||
|
Drop setters in Viper 2.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Since Viper's interface is usually invoked from a lot of places,
|
||||||
|
moving configuration to the place where it is initialized makes using Viper safer
|
||||||
|
(ie. someone can't just randomly call `Set` when they are only supposed to call `Get*`).
|
||||||
|
|
||||||
|
This change will also clarify what roles Viper can be used in and
|
||||||
|
makes the separation of internal components easier based on these roles.
|
|
@ -0,0 +1,20 @@
|
||||||
|
# 6. Go version support
|
||||||
|
|
||||||
|
Date: 2021-09-16
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
From time to time new features are released in the Go language.
|
||||||
|
Relying on those features means dropping support for older Go versions.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Follow the [Go release policy](https://golang.org/doc/devel/release#policy) and support the last two major versions of Go.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Support for older Go versions will happen every 6 months according to the Go release cycle.
|
|
@ -0,0 +1,22 @@
|
||||||
|
# 7. Drop writing support
|
||||||
|
|
||||||
|
Date: 2021-09-22
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
References [2. Prefer making backward compatible changes](0002-prefer-making-backward-compatible-changes.md)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The number one source of issues for Viper comes from the fact that it supports both reading and writing.
|
||||||
|
It causes concurrency issues and has lots of inconsistencies.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Drop file writing support from Viper in v2.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
This is going to be a major breaking change in the library, but it will make maintenance significantly easier.
|
|
@ -0,0 +1,23 @@
|
||||||
|
# 8. Deprecate the global Viper instance
|
||||||
|
|
||||||
|
Date: 2021-09-23
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Proposed
|
||||||
|
|
||||||
|
References [5. Deprecate setters in favor of functional options during initialization](0005-deprecate-setters-in-favor-of-functional-options-during-initialization.md)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
With the deprecation of setters in favor of functional options, it becomes almost impossible to get away with instantiating Viper.
|
||||||
|
In addition to that, people should be discouraged from accessing a global Viper instance.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Deprecate the global Viper instance and the global access functions.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
People will still be able to create a global instance of their own,
|
||||||
|
but instantiating a custom Viper instance will become the primary solution for using Viper.
|
|
@ -1,11 +0,0 @@
|
||||||
//go:build viper_logger
|
|
||||||
// +build viper_logger
|
|
||||||
|
|
||||||
package viper
|
|
||||||
|
|
||||||
// WithLogger sets a custom logger.
|
|
||||||
func WithLogger(l Logger) Option {
|
|
||||||
return optionFunc(func(v *Viper) {
|
|
||||||
v.logger = l
|
|
||||||
})
|
|
||||||
}
|
|
65
fs.go
65
fs.go
|
@ -1,65 +0,0 @@
|
||||||
//go:build go1.16 && finder
|
|
||||||
// +build go1.16,finder
|
|
||||||
|
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io/fs"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
type finder struct {
|
|
||||||
paths []string
|
|
||||||
fileNames []string
|
|
||||||
extensions []string
|
|
||||||
|
|
||||||
withoutExtension bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f finder) Find(fsys fs.FS) (string, error) {
|
|
||||||
for _, searchPath := range f.paths {
|
|
||||||
for _, fileName := range f.fileNames {
|
|
||||||
for _, extension := range f.extensions {
|
|
||||||
filePath := path.Join(searchPath, fileName+"."+extension)
|
|
||||||
|
|
||||||
ok, err := fileExists(fsys, filePath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
return filePath, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.withoutExtension {
|
|
||||||
filePath := path.Join(searchPath, fileName)
|
|
||||||
|
|
||||||
ok, err := fileExists(fsys, filePath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
return filePath, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileExists(fsys fs.FS, filePath string) (bool, error) {
|
|
||||||
fileInfo, err := fs.Stat(fsys, filePath)
|
|
||||||
if err == nil {
|
|
||||||
return !fileInfo.IsDir(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, err
|
|
||||||
}
|
|
100
fs_test.go
100
fs_test.go
|
@ -1,100 +0,0 @@
|
||||||
//go:build go1.16 && finder
|
|
||||||
// +build go1.16,finder
|
|
||||||
|
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/fs"
|
|
||||||
"testing"
|
|
||||||
"testing/fstest"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFinder(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
fsys := fstest.MapFS{
|
|
||||||
"home/user/.config": &fstest.MapFile{},
|
|
||||||
"home/user/config.json": &fstest.MapFile{},
|
|
||||||
"home/user/config.yaml": &fstest.MapFile{},
|
|
||||||
"home/user/data.json": &fstest.MapFile{},
|
|
||||||
"etc/config/.config": &fstest.MapFile{},
|
|
||||||
"etc/config/a_random_file.txt": &fstest.MapFile{},
|
|
||||||
"etc/config/config.json": &fstest.MapFile{},
|
|
||||||
"etc/config/config.yaml": &fstest.MapFile{},
|
|
||||||
"etc/config/config.xml": &fstest.MapFile{},
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
fsys func() fs.FS
|
|
||||||
finder finder
|
|
||||||
result string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "find file",
|
|
||||||
fsys: func() fs.FS { return fsys },
|
|
||||||
finder: finder{
|
|
||||||
paths: []string{"etc/config"},
|
|
||||||
fileNames: []string{"config"},
|
|
||||||
extensions: []string{"json"},
|
|
||||||
},
|
|
||||||
result: "etc/config/config.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "file not found",
|
|
||||||
fsys: func() fs.FS { return fsys },
|
|
||||||
finder: finder{
|
|
||||||
paths: []string{"var/config"},
|
|
||||||
fileNames: []string{"config"},
|
|
||||||
extensions: []string{"json"},
|
|
||||||
},
|
|
||||||
result: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty search params",
|
|
||||||
fsys: func() fs.FS { return fsys },
|
|
||||||
finder: finder{},
|
|
||||||
result: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "precedence",
|
|
||||||
fsys: func() fs.FS { return fsys },
|
|
||||||
finder: finder{
|
|
||||||
paths: []string{"var/config", "home/user", "etc/config"},
|
|
||||||
fileNames: []string{"aconfig", "config"},
|
|
||||||
extensions: []string{"zml", "xml", "json"},
|
|
||||||
},
|
|
||||||
result: "home/user/config.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "without extension",
|
|
||||||
fsys: func() fs.FS { return fsys },
|
|
||||||
finder: finder{
|
|
||||||
paths: []string{"var/config", "home/user", "etc/config"},
|
|
||||||
fileNames: []string{".config"},
|
|
||||||
extensions: []string{"zml", "xml", "json"},
|
|
||||||
|
|
||||||
withoutExtension: true,
|
|
||||||
},
|
|
||||||
result: "home/user/.config",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
|
||||||
testCase := testCase
|
|
||||||
|
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
fsys := testCase.fsys()
|
|
||||||
|
|
||||||
result, err := testCase.finder.Find(fsys)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.result, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
36
go.mod
36
go.mod
|
@ -1,29 +1,21 @@
|
||||||
module git.internal/re/viper
|
module github.com/spf13/viper
|
||||||
|
|
||||||
go 1.17
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.internal/re/afero v1.9.3
|
github.com/bketelsen/crypt v0.0.4
|
||||||
git.internal/re/cast v1.5.1
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/hashicorp/hcl v1.0.0
|
||||||
github.com/magiconair/properties v1.8.7
|
github.com/magiconair/properties v1.8.5
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.4.1
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.3
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
|
github.com/spf13/afero v1.6.0
|
||||||
|
github.com/spf13/cast v1.3.1
|
||||||
github.com/spf13/jwalterweatherman v1.1.0
|
github.com/spf13/jwalterweatherman v1.1.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/subosito/gotenv v1.4.1
|
github.com/subosito/gotenv v1.2.0
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.62.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
|
|
||||||
golang.org/x/text v0.4.0 // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
279
go.sum
279
go.sum
|
@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
@ -16,7 +15,10 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||||
|
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||||
|
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
|
||||||
|
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
@ -25,6 +27,8 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
@ -34,14 +38,17 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
git.internal/re/afero v1.9.3 h1:3E6gTxdfID9str9lOLWuOQu+QILArbRLUBWi8MywP18=
|
|
||||||
git.internal/re/afero v1.9.3/go.mod h1:uyj2gu0urns+v+G7KIkNO49qShF4zekJMCflZVvB84M=
|
|
||||||
git.internal/re/cast v1.5.1 h1:lmg2A9oeAp9CZjvC70Bcv2hOuQO5RB027bIbixTbok4=
|
|
||||||
git.internal/re/cast v1.5.1/go.mod h1:ydbf5sDush+h+PruReXSzPmL7F0nRL1ogRYehzUSHnE=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
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 h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bketelsen/crypt v0.0.4 h1:w/jqZtC9YD4DS/Vp9GhWfWcCpuAL58oTnLoI8vE9YHU=
|
||||||
|
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
@ -50,7 +57,9 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
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/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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -59,17 +68,21 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
@ -78,6 +91,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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.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.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
@ -92,7 +106,12 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
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.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
@ -102,10 +121,11 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/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.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
@ -118,80 +138,157 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
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 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
||||||
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
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/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||||
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||||
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
|
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||||
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
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/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.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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
|
||||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
|
||||||
|
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||||
|
go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs=
|
||||||
|
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
|
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||||
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -215,6 +312,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
|
||||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
@ -225,9 +324,12 @@ 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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||||
|
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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
@ -254,10 +356,13 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -267,6 +372,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
||||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -277,8 +386,11 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -288,6 +400,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -309,24 +422,23 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -336,6 +448,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
@ -345,6 +458,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
@ -367,6 +481,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
|
||||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
@ -375,12 +490,14 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
|
||||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
|
||||||
|
golang.org/x/tools v0.1.2/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-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
@ -401,12 +518,17 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
|
||||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
|
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||||
|
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||||
|
google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA=
|
||||||
|
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
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/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
@ -431,6 +553,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
|
||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
@ -442,8 +565,13 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D
|
||||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||||
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
|
||||||
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -457,9 +585,14 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
|
||||||
|
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
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-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 v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
@ -470,19 +603,23 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoder decodes the contents of b into v.
|
// Decoder decodes the contents of b into a v representation.
|
||||||
// It's primarily used for decoding contents of a file into a map[string]interface{}.
|
// It's primarily used for decoding contents of a file into a map[string]interface{}.
|
||||||
type Decoder interface {
|
type Decoder interface {
|
||||||
Decode(b []byte, v map[string]interface{}) error
|
Decode(b []byte, v interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -48,7 +48,7 @@ func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode calls the underlying Decoder based on the format.
|
// Decode calls the underlying Decoder based on the format.
|
||||||
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
|
func (e *DecoderRegistry) Decode(format string, b []byte, v interface{}) error {
|
||||||
e.mu.RLock()
|
e.mu.RLock()
|
||||||
decoder, ok := e.decoders[format]
|
decoder, ok := e.decoders[format]
|
||||||
e.mu.RUnlock()
|
e.mu.RUnlock()
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
package encoding
|
package encoding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type decoder struct {
|
type decoder struct {
|
||||||
v map[string]interface{}
|
v interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d decoder) Decode(_ []byte, v map[string]interface{}) error {
|
func (d decoder) Decode(_ []byte, v interface{}) error {
|
||||||
for key, value := range d.v {
|
rv := v.(*string)
|
||||||
v[key] = value
|
*rv = d.v.(string)
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -46,9 +44,7 @@ func TestDecoderRegistry_Decode(t *testing.T) {
|
||||||
t.Run("OK", func(t *testing.T) {
|
t.Run("OK", func(t *testing.T) {
|
||||||
registry := NewDecoderRegistry()
|
registry := NewDecoderRegistry()
|
||||||
decoder := decoder{
|
decoder := decoder{
|
||||||
v: map[string]interface{}{
|
v: "decoded value",
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := registry.RegisterDecoder("myformat", decoder)
|
err := registry.RegisterDecoder("myformat", decoder)
|
||||||
|
@ -56,24 +52,24 @@ func TestDecoderRegistry_Decode(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
var v string
|
||||||
|
|
||||||
err = registry.Decode("myformat", []byte("key: value"), v)
|
err = registry.Decode("myformat", []byte("some value"), &v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(decoder.v, v) {
|
if v != "decoded value" {
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %+v\nexpected: %+v", v, decoder.v)
|
t.Fatalf("expected 'decoded value', got: %#v", v)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("DecoderNotFound", func(t *testing.T) {
|
t.Run("DecoderNotFound", func(t *testing.T) {
|
||||||
registry := NewDecoderRegistry()
|
registry := NewDecoderRegistry()
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
var v string
|
||||||
|
|
||||||
err := registry.Decode("myformat", nil, v)
|
err := registry.Decode("myformat", []byte("some value"), &v)
|
||||||
if err != ErrDecoderNotFound {
|
if err != ErrDecoderNotFound {
|
||||||
t.Fatalf("expected ErrDecoderNotFound, got: %v", err)
|
t.Fatalf("expected ErrDecoderNotFound, got: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]interface{}) ([]byte, error) {
|
|
||||||
flattened := map[string]interface{}{}
|
|
||||||
|
|
||||||
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]interface{}) 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,63 +0,0 @@
|
||||||
package dotenv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// original form of the data
|
|
||||||
const original = `# key-value pair
|
|
||||||
KEY=value
|
|
||||||
`
|
|
||||||
|
|
||||||
// encoded form of the data
|
|
||||||
const encoded = `KEY=value
|
|
||||||
`
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"KEY": "value",
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(data, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected decoding to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package dotenv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.internal/re/cast"
|
|
||||||
)
|
|
||||||
|
|
||||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
|
||||||
// Code is based on the function with the same name in tha main package.
|
|
||||||
// TODO: move it to a common place
|
|
||||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
|
|
||||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
|
||||||
// prefix is shadowed => nothing more to flatten
|
|
||||||
return shadow
|
|
||||||
}
|
|
||||||
if shadow == nil {
|
|
||||||
shadow = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
var m2 map[string]interface{}
|
|
||||||
if prefix != "" {
|
|
||||||
prefix += delimiter
|
|
||||||
}
|
|
||||||
for k, val := range m {
|
|
||||||
fullKey := prefix + k
|
|
||||||
switch val.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
m2 = val.(map[string]interface{})
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
// Encoder encodes the contents of v into a byte representation.
|
// Encoder encodes the contents of v into a byte representation.
|
||||||
// It's primarily used for encoding a map[string]interface{} into a file format.
|
// It's primarily used for encoding a map[string]interface{} into a file format.
|
||||||
type Encoder interface {
|
type Encoder interface {
|
||||||
Encode(v map[string]interface{}) ([]byte, error)
|
Encode(v interface{}) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -47,7 +47,7 @@ func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EncoderRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
|
func (e *EncoderRegistry) Encode(format string, v interface{}) ([]byte, error) {
|
||||||
e.mu.RLock()
|
e.mu.RLock()
|
||||||
encoder, ok := e.encoders[format]
|
encoder, ok := e.encoders[format]
|
||||||
e.mu.RUnlock()
|
e.mu.RUnlock()
|
||||||
|
|
|
@ -8,7 +8,7 @@ type encoder struct {
|
||||||
b []byte
|
b []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e encoder) Encode(_ map[string]interface{}) ([]byte, error) {
|
func (e encoder) Encode(_ interface{}) ([]byte, error) {
|
||||||
return e.b, nil
|
return e.b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func TestEncoderRegistry_Decode(t *testing.T) {
|
||||||
t.Run("OK", func(t *testing.T) {
|
t.Run("OK", func(t *testing.T) {
|
||||||
registry := NewEncoderRegistry()
|
registry := NewEncoderRegistry()
|
||||||
encoder := encoder{
|
encoder := encoder{
|
||||||
b: []byte("key: value"),
|
b: []byte("encoded value"),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := registry.RegisterEncoder("myformat", encoder)
|
err := registry.RegisterEncoder("myformat", encoder)
|
||||||
|
@ -49,20 +49,20 @@ func TestEncoderRegistry_Decode(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
|
b, err := registry.Encode("myformat", "some value")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(b) != "key: value" {
|
if string(b) != "encoded value" {
|
||||||
t.Fatalf("expected 'key: value', got: %#v", string(b))
|
t.Fatalf("expected 'encoded value', got: %#v", string(b))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("EncoderNotFound", func(t *testing.T) {
|
t.Run("EncoderNotFound", func(t *testing.T) {
|
||||||
registry := NewEncoderRegistry()
|
registry := NewEncoderRegistry()
|
||||||
|
|
||||||
_, err := registry.Encode("myformat", map[string]interface{}{"key": "value"})
|
_, err := registry.Encode("myformat", "some value")
|
||||||
if err != ErrEncoderNotFound {
|
if err != ErrEncoderNotFound {
|
||||||
t.Fatalf("expected ErrEncoderNotFound, got: %v", err)
|
t.Fatalf("expected ErrEncoderNotFound, got: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,36 +2,39 @@ package hcl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/hashicorp/hcl"
|
||||||
|
"github.com/hashicorp/hcl/hcl/printer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for HCL encoding.
|
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for HCL encoding.
|
||||||
// TODO: add printer config to the codec?
|
// TODO: add printer config to the codec?
|
||||||
type Codec struct{}
|
type Codec struct{}
|
||||||
|
|
||||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
func (Codec) Encode(v interface{}) ([]byte, error) {
|
||||||
// b, err := json.Marshal(v)
|
b, err := json.Marshal(v)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// TODO: use printer.Format? Is the trailing newline an issue?
|
// TODO: use printer.Format? Is the trailing newline an issue?
|
||||||
|
|
||||||
// ast, err := hcl.Parse(string(b))
|
ast, err := hcl.Parse(string(b))
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
// err = printer.Fprint(&buf, ast.Node)
|
err = printer.Fprint(&buf, ast.Node)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
func (Codec) Decode(b []byte, v interface{}) error {
|
||||||
// return hcl.Unmarshal(b, &v)
|
return hcl.Unmarshal(b, v)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,140 +0,0 @@
|
||||||
package hcl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// original form of the data
|
|
||||||
const original = `# key-value pair
|
|
||||||
"key" = "value"
|
|
||||||
|
|
||||||
// list
|
|
||||||
"list" = ["item1", "item2", "item3"]
|
|
||||||
|
|
||||||
/* map */
|
|
||||||
"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 HCL it's slightly different from Viper's internal representation
|
|
||||||
// (eg. map is decoded into a list of maps)
|
|
||||||
var decoded = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"nested_map": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"map": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(decoded, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected decoding to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
package ini
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.internal/re/cast"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LoadOptions contains all customized options used for load data source(s).
|
|
||||||
// This type is added here for convenience: this way consumers can import a single package called "ini".
|
|
||||||
type LoadOptions = ini.LoadOptions
|
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for INI encoding.
|
|
||||||
type Codec struct {
|
|
||||||
KeyDelimiter string
|
|
||||||
LoadOptions LoadOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
|
||||||
cfg := ini.Empty()
|
|
||||||
ini.PrettyFormat = false
|
|
||||||
|
|
||||||
flattened := map[string]interface{}{}
|
|
||||||
|
|
||||||
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
|
|
||||||
|
|
||||||
keys := make([]string, 0, len(flattened))
|
|
||||||
|
|
||||||
for key := range flattened {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
for _, key := range keys {
|
|
||||||
sectionName, keyName := "", key
|
|
||||||
|
|
||||||
lastSep := strings.LastIndex(key, ".")
|
|
||||||
if lastSep != -1 {
|
|
||||||
sectionName = key[:(lastSep)]
|
|
||||||
keyName = key[(lastSep + 1):]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is this a good idea?
|
|
||||||
if sectionName == "default" {
|
|
||||||
sectionName = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.Section(sectionName).Key(keyName).SetValue(cast.ToString(flattened[key]))
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
_, err := cfg.WriteTo(&buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Codec) Decode(b []byte, v map[string]interface{}) error {
|
|
||||||
cfg := ini.Empty(c.LoadOptions)
|
|
||||||
|
|
||||||
err := cfg.Append(b)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sections := cfg.Sections()
|
|
||||||
|
|
||||||
for i := 0; i < len(sections); i++ {
|
|
||||||
section := sections[i]
|
|
||||||
keys := section.Keys()
|
|
||||||
|
|
||||||
for j := 0; j < len(keys); j++ {
|
|
||||||
key := keys[j]
|
|
||||||
value := cfg.Section(section.Name()).Key(key.Name()).String()
|
|
||||||
|
|
||||||
deepestMap := deepSearch(v, strings.Split(section.Name(), c.keyDelimiter()))
|
|
||||||
|
|
||||||
// set innermost value
|
|
||||||
deepestMap[key.Name()] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Codec) keyDelimiter() string {
|
|
||||||
if c.KeyDelimiter == "" {
|
|
||||||
return "."
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.KeyDelimiter
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
package ini
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// original form of the data
|
|
||||||
const original = `; key-value pair
|
|
||||||
key=value ; key-value pair
|
|
||||||
|
|
||||||
# map
|
|
||||||
[map] # map
|
|
||||||
key=%(key)s
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
// encoded form of the data
|
|
||||||
const encoded = `key=value
|
|
||||||
|
|
||||||
[map]
|
|
||||||
key=value
|
|
||||||
`
|
|
||||||
|
|
||||||
// decoded form of the data
|
|
||||||
//
|
|
||||||
// in case of INI it's slightly different from Viper's internal representation
|
|
||||||
// (eg. top level keys land in a section called default)
|
|
||||||
var decoded = map[string]interface{}{
|
|
||||||
"DEFAULT": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Default", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"default": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(decoded, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected decoding to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package ini
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.internal/re/cast"
|
|
||||||
)
|
|
||||||
|
|
||||||
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
|
|
||||||
// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
|
|
||||||
// deepSearch scans deep maps, following the key indexes listed in the
|
|
||||||
// sequence "path".
|
|
||||||
// The last value is expected to be another map, and is returned.
|
|
||||||
//
|
|
||||||
// In case intermediate keys do not exist, or map to a non-map value,
|
|
||||||
// a new map is created and inserted, and the search continues from there:
|
|
||||||
// the initial map "m" may be modified!
|
|
||||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
|
||||||
for _, k := range path {
|
|
||||||
m2, ok := m[k]
|
|
||||||
if !ok {
|
|
||||||
// intermediate key does not exist
|
|
||||||
// => create it and continue from there
|
|
||||||
m3 := make(map[string]interface{})
|
|
||||||
m[k] = m3
|
|
||||||
m = m3
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m3, ok := m2.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
// intermediate key is a value
|
|
||||||
// => replace with a new map
|
|
||||||
m3 = make(map[string]interface{})
|
|
||||||
m[k] = m3
|
|
||||||
}
|
|
||||||
// continue search from here
|
|
||||||
m = m3
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
|
||||||
// Code is based on the function with the same name in tha main package.
|
|
||||||
// TODO: move it to a common place
|
|
||||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
|
|
||||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
|
||||||
// prefix is shadowed => nothing more to flatten
|
|
||||||
return shadow
|
|
||||||
}
|
|
||||||
if shadow == nil {
|
|
||||||
shadow = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
var m2 map[string]interface{}
|
|
||||||
if prefix != "" {
|
|
||||||
prefix += delimiter
|
|
||||||
}
|
|
||||||
for k, val := range m {
|
|
||||||
fullKey := prefix + k
|
|
||||||
switch val.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
m2 = val.(map[string]interface{})
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
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,86 +0,0 @@
|
||||||
package javaproperties
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.internal/re/cast"
|
|
||||||
"github.com/magiconair/properties"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding.
|
|
||||||
type Codec struct {
|
|
||||||
KeyDelimiter string
|
|
||||||
|
|
||||||
// Store read properties on the object so that we can write back in order with comments.
|
|
||||||
// This will only be used if the configuration read is a properties file.
|
|
||||||
// TODO: drop this feature in v2
|
|
||||||
// TODO: make use of the global properties object optional
|
|
||||||
Properties *properties.Properties
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
|
||||||
if c.Properties == nil {
|
|
||||||
c.Properties = properties.NewProperties()
|
|
||||||
}
|
|
||||||
|
|
||||||
flattened := map[string]interface{}{}
|
|
||||||
|
|
||||||
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
|
|
||||||
|
|
||||||
keys := make([]string, 0, len(flattened))
|
|
||||||
|
|
||||||
for key := range flattened {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
for _, key := range keys {
|
|
||||||
_, _, err := c.Properties.Set(key, cast.ToString(flattened[key]))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
_, err := c.Properties.WriteComment(&buf, "#", properties.UTF8)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
|
|
||||||
var err error
|
|
||||||
c.Properties, err = properties.Load(b, properties.UTF8)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, key := range c.Properties.Keys() {
|
|
||||||
// ignore existence check: we know it's there
|
|
||||||
value, _ := c.Properties.Get(key)
|
|
||||||
|
|
||||||
// recursively build nested maps
|
|
||||||
path := strings.Split(key, c.keyDelimiter())
|
|
||||||
lastKey := strings.ToLower(path[len(path)-1])
|
|
||||||
deepestMap := deepSearch(v, path[0:len(path)-1])
|
|
||||||
|
|
||||||
// set innermost value
|
|
||||||
deepestMap[lastKey] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Codec) keyDelimiter() string {
|
|
||||||
if c.KeyDelimiter == "" {
|
|
||||||
return "."
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.KeyDelimiter
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package javaproperties
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// original form of the data
|
|
||||||
const original = `#key-value pair
|
|
||||||
key = value
|
|
||||||
map.key = value
|
|
||||||
`
|
|
||||||
|
|
||||||
// encoded form of the data
|
|
||||||
const encoded = `key = value
|
|
||||||
map.key = value
|
|
||||||
`
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(data, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
t.Skip("TODO: needs invalid data example")
|
|
||||||
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
codec.Decode([]byte(``), v)
|
|
||||||
|
|
||||||
if len(v) > 0 {
|
|
||||||
t.Fatalf("expected map to be empty when data is invalid\nactual: %#v", v)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_DecodeEncode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if original != string(b) {
|
|
||||||
t.Fatalf("encoded value does not match the original\nactual: %#v\nexpected: %#v", string(b), original)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package javaproperties
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.internal/re/cast"
|
|
||||||
)
|
|
||||||
|
|
||||||
// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED
|
|
||||||
// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE
|
|
||||||
// deepSearch scans deep maps, following the key indexes listed in the
|
|
||||||
// sequence "path".
|
|
||||||
// The last value is expected to be another map, and is returned.
|
|
||||||
//
|
|
||||||
// In case intermediate keys do not exist, or map to a non-map value,
|
|
||||||
// a new map is created and inserted, and the search continues from there:
|
|
||||||
// the initial map "m" may be modified!
|
|
||||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
|
||||||
for _, k := range path {
|
|
||||||
m2, ok := m[k]
|
|
||||||
if !ok {
|
|
||||||
// intermediate key does not exist
|
|
||||||
// => create it and continue from there
|
|
||||||
m3 := make(map[string]interface{})
|
|
||||||
m[k] = m3
|
|
||||||
m = m3
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m3, ok := m2.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
// intermediate key is a value
|
|
||||||
// => replace with a new map
|
|
||||||
m3 = make(map[string]interface{})
|
|
||||||
m[k] = m3
|
|
||||||
}
|
|
||||||
// continue search from here
|
|
||||||
m = m3
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
|
||||||
// Code is based on the function with the same name in tha main package.
|
|
||||||
// TODO: move it to a common place
|
|
||||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
|
|
||||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
|
||||||
// prefix is shadowed => nothing more to flatten
|
|
||||||
return shadow
|
|
||||||
}
|
|
||||||
if shadow == nil {
|
|
||||||
shadow = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
var m2 map[string]interface{}
|
|
||||||
if prefix != "" {
|
|
||||||
prefix += delimiter
|
|
||||||
}
|
|
||||||
for k, val := range m {
|
|
||||||
fullKey := prefix + k
|
|
||||||
switch val.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
m2 = val.(map[string]interface{})
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -7,11 +7,11 @@ import (
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
|
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
|
||||||
type Codec struct{}
|
type Codec struct{}
|
||||||
|
|
||||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
func (Codec) Encode(v interface{}) ([]byte, error) {
|
||||||
// TODO: expose prefix and indent in the Codec as setting?
|
// TODO: expose prefix and indent in the Codec as setting?
|
||||||
return json.MarshalIndent(v, "", " ")
|
return json.MarshalIndent(v, "", " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
func (Codec) Decode(b []byte, v interface{}) error {
|
||||||
return json.Unmarshal(b, &v)
|
return json.Unmarshal(b, v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
package json
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(encoded), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(data, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected decoding to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,6 +1,3 @@
|
||||||
//go:build viper_toml1
|
|
||||||
// +build viper_toml1
|
|
||||||
|
|
||||||
package toml
|
package toml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -10,30 +7,39 @@ import (
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
|
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
|
||||||
type Codec struct{}
|
type Codec struct{}
|
||||||
|
|
||||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
func (Codec) Encode(v interface{}) ([]byte, error) {
|
||||||
t, err := toml.TreeFromMap(v)
|
if m, ok := v.(map[string]interface{}); ok {
|
||||||
if err != nil {
|
t, err := toml.TreeFromMap(m)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := t.ToTomlString()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := t.ToTomlString()
|
return toml.Marshal(v)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(s), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
func (Codec) Decode(b []byte, v interface{}) error {
|
||||||
tree, err := toml.LoadBytes(b)
|
tree, err := toml.LoadBytes(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmap := tree.ToMap()
|
if m, ok := v.(*map[string]interface{}); ok {
|
||||||
for key, value := range tmap {
|
vmap := *m
|
||||||
v[key] = value
|
tmap := tree.ToMap()
|
||||||
|
for k, v := range tmap {
|
||||||
|
vmap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return tree.Unmarshal(v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
//go:build !viper_toml1
|
|
||||||
// +build !viper_toml1
|
|
||||||
|
|
||||||
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]interface{}) ([]byte, error) {
|
|
||||||
return toml.Marshal(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
|
||||||
return toml.Unmarshal(b, &v)
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
//go:build !viper_toml1
|
|
||||||
// +build !viper_toml1
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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']
|
|
||||||
`
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(data, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected decoding to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
//go:build viper_toml1
|
|
||||||
// +build viper_toml1
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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"]
|
|
||||||
`
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(data, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected decoding to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,14 +1,14 @@
|
||||||
package yaml
|
package yaml
|
||||||
|
|
||||||
// import "gopkg.in/yaml.v2"
|
import "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
|
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
|
||||||
type Codec struct{}
|
type Codec struct{}
|
||||||
|
|
||||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
func (Codec) Encode(v interface{}) ([]byte, error) {
|
||||||
return yaml.Marshal(v)
|
return yaml.Marshal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
func (Codec) Decode(b []byte, v interface{}) error {
|
||||||
return yaml.Unmarshal(b, &v)
|
return yaml.Unmarshal(b, v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
package yaml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCodec_Encode(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
b, err := codec.Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded != string(b) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", string(b), encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCodec_Decode(t *testing.T) {
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(original), v)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(decoded, v) {
|
|
||||||
t.Fatalf("decoded value does not match the expected one\nactual: %#v\nexpected: %#v", v, decoded)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("InvalidData", func(t *testing.T) {
|
|
||||||
codec := Codec{}
|
|
||||||
|
|
||||||
v := map[string]interface{}{}
|
|
||||||
|
|
||||||
err := codec.Decode([]byte(`invalid data`), v)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected decoding to fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("decoding failed as expected: %s", err)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
//go:build viper_yaml2
|
|
||||||
// +build viper_yaml2
|
|
||||||
|
|
||||||
package yaml
|
|
||||||
|
|
||||||
import yamlv2 "gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
var yaml = struct {
|
|
||||||
Marshal func(in interface{}) (out []byte, err error)
|
|
||||||
Unmarshal func(in []byte, out interface{}) (err error)
|
|
||||||
}{
|
|
||||||
Marshal: yamlv2.Marshal,
|
|
||||||
Unmarshal: yamlv2.Unmarshal,
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
//go:build viper_yaml2
|
|
||||||
// +build viper_yaml2
|
|
||||||
|
|
||||||
package yaml
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// (eg. map is decoded into a map with interface key)
|
|
||||||
var decoded = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[interface{}]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[interface{}]interface{}{
|
|
||||||
"map": map[interface{}]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
//go:build !viper_yaml2
|
|
||||||
// +build !viper_yaml2
|
|
||||||
|
|
||||||
package yaml
|
|
||||||
|
|
||||||
import yamlv3 "gopkg.in/yaml.v3"
|
|
||||||
|
|
||||||
var yaml = struct {
|
|
||||||
Marshal func(in interface{}) (out []byte, err error)
|
|
||||||
Unmarshal func(in []byte, out interface{}) (err error)
|
|
||||||
}{
|
|
||||||
Marshal: yamlv3.Marshal,
|
|
||||||
Unmarshal: yamlv3.Unmarshal,
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
//go:build !viper_yaml2
|
|
||||||
// +build !viper_yaml2
|
|
||||||
|
|
||||||
package yaml
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// (eg. map is decoded into a map with interface key)
|
|
||||||
var decoded = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Viper's internal representation
|
|
||||||
var data = map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
},
|
|
||||||
"nested_map": map[string]interface{}{
|
|
||||||
"map": map[string]interface{}{
|
|
||||||
"key": "value",
|
|
||||||
"list": []interface{}{
|
|
||||||
"item1",
|
|
||||||
"item2",
|
|
||||||
"item3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,6 +1,3 @@
|
||||||
//go:build !go1.17
|
|
||||||
// +build !go1.17
|
|
||||||
|
|
||||||
package testutil
|
package testutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -21,6 +18,11 @@ func Setenv(t *testing.T, name, val string) {
|
||||||
setenv(t, name, val, true)
|
setenv(t, name, val, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unsetenv unsets an environment variable for the duration of a test.
|
||||||
|
func Unsetenv(t *testing.T, name string) {
|
||||||
|
setenv(t, name, "", false)
|
||||||
|
}
|
||||||
|
|
||||||
// setenv sets or unsets an environment variable to a temporary value for the
|
// setenv sets or unsets an environment variable to a temporary value for the
|
||||||
// duration of the test
|
// duration of the test
|
||||||
func setenv(t *testing.T, name, val string, valOK bool) {
|
func setenv(t *testing.T, name, val string, valOK bool) {
|
|
@ -1,18 +0,0 @@
|
||||||
//go:build go1.17
|
|
||||||
// +build go1.17
|
|
||||||
|
|
||||||
package testutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Setenv sets an environment variable to a temporary value for the
|
|
||||||
// duration of the test.
|
|
||||||
//
|
|
||||||
// This shim can be removed once support for Go <1.17 is dropped.
|
|
||||||
func Setenv(t *testing.T, name, val string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
t.Setenv(name, val)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
77
logger.go
77
logger.go
|
@ -1,77 +0,0 @@
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logger is a unified interface for various logging use cases and practices, including:
|
|
||||||
// - leveled logging
|
|
||||||
// - structured logging
|
|
||||||
type Logger interface {
|
|
||||||
// Trace logs a Trace event.
|
|
||||||
//
|
|
||||||
// Even more fine-grained information than Debug events.
|
|
||||||
// Loggers not supporting this level should fall back to Debug.
|
|
||||||
Trace(msg string, keyvals ...interface{})
|
|
||||||
|
|
||||||
// Debug logs a Debug event.
|
|
||||||
//
|
|
||||||
// A verbose series of information events.
|
|
||||||
// They are useful when debugging the system.
|
|
||||||
Debug(msg string, keyvals ...interface{})
|
|
||||||
|
|
||||||
// Info logs an Info event.
|
|
||||||
//
|
|
||||||
// General information about what's happening inside the system.
|
|
||||||
Info(msg string, keyvals ...interface{})
|
|
||||||
|
|
||||||
// Warn logs a Warn(ing) event.
|
|
||||||
//
|
|
||||||
// Non-critical events that should be looked at.
|
|
||||||
Warn(msg string, keyvals ...interface{})
|
|
||||||
|
|
||||||
// Error logs an Error event.
|
|
||||||
//
|
|
||||||
// Critical events that require immediate attention.
|
|
||||||
// Loggers commonly provide Fatal and Panic levels above Error level,
|
|
||||||
// but exiting and panicing is out of scope for a logging library.
|
|
||||||
Error(msg string, keyvals ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type jwwLogger struct{}
|
|
||||||
|
|
||||||
func (jwwLogger) Trace(msg string, keyvals ...interface{}) {
|
|
||||||
jww.TRACE.Printf(jwwLogMessage(msg, keyvals...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jwwLogger) Debug(msg string, keyvals ...interface{}) {
|
|
||||||
jww.DEBUG.Printf(jwwLogMessage(msg, keyvals...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jwwLogger) Info(msg string, keyvals ...interface{}) {
|
|
||||||
jww.INFO.Printf(jwwLogMessage(msg, keyvals...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jwwLogger) Warn(msg string, keyvals ...interface{}) {
|
|
||||||
jww.WARN.Printf(jwwLogMessage(msg, keyvals...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jwwLogger) Error(msg string, keyvals ...interface{}) {
|
|
||||||
jww.ERROR.Printf(jwwLogMessage(msg, keyvals...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func jwwLogMessage(msg string, keyvals ...interface{}) string {
|
|
||||||
out := msg
|
|
||||||
|
|
||||||
if len(keyvals) > 0 && len(keyvals)%2 == 1 {
|
|
||||||
keyvals = append(keyvals, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i <= len(keyvals)-2; i += 2 {
|
|
||||||
out = fmt.Sprintf("%s %v=%v", out, keyvals[i], keyvals[i+1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.internal/re/cast"
|
"github.com/spf13/cast"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
188
remote/remote.go
188
remote/remote.go
|
@ -6,110 +6,106 @@
|
||||||
// Package remote integrates the remote features of Viper.
|
// Package remote integrates the remote features of Viper.
|
||||||
package remote
|
package remote
|
||||||
|
|
||||||
// import (
|
import (
|
||||||
// "bytes"
|
"bytes"
|
||||||
// "io"
|
"io"
|
||||||
// "os"
|
"os"
|
||||||
|
|
||||||
// crypt "github.com/sagikazarmark/crypt/config"
|
crypt "github.com/bketelsen/crypt/config"
|
||||||
|
|
||||||
// "git.internal/re/viper"
|
"github.com/spf13/viper"
|
||||||
// )
|
)
|
||||||
|
|
||||||
// type remoteConfigProvider struct{}
|
type remoteConfigProvider struct{}
|
||||||
|
|
||||||
// func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
|
func (rc remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
|
||||||
// cm, err := getConfigManager(rp)
|
cm, err := getConfigManager(rp)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
// b, err := cm.Get(rp.Path())
|
b, err := cm.Get(rp.Path())
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
// return bytes.NewReader(b), nil
|
return bytes.NewReader(b), nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
|
func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
|
||||||
// cm, err := getConfigManager(rp)
|
cm, err := getConfigManager(rp)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
// resp, err := cm.Get(rp.Path())
|
resp, err := cm.Get(rp.Path())
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return bytes.NewReader(resp), nil
|
return bytes.NewReader(resp), nil
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
|
func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
|
||||||
// cm, err := getConfigManager(rp)
|
cm, err := getConfigManager(rp)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, nil
|
return nil, nil
|
||||||
// }
|
}
|
||||||
// quit := make(chan bool)
|
quit := make(chan bool)
|
||||||
// quitwc := make(chan bool)
|
quitwc := make(chan bool)
|
||||||
// viperResponsCh := make(chan *viper.RemoteResponse)
|
viperResponsCh := make(chan *viper.RemoteResponse)
|
||||||
// cryptoResponseCh := cm.Watch(rp.Path(), quit)
|
cryptoResponseCh := cm.Watch(rp.Path(), quit)
|
||||||
// // need this function to convert the Channel response form crypt.Response to viper.Response
|
// need this function to convert the Channel response form crypt.Response to viper.Response
|
||||||
// go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
|
go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
|
||||||
// for {
|
for {
|
||||||
// select {
|
select {
|
||||||
// case <-quitwc:
|
case <-quitwc:
|
||||||
// quit <- true
|
quit <- true
|
||||||
// return
|
return
|
||||||
// case resp := <-cr:
|
case resp := <-cr:
|
||||||
// vr <- &viper.RemoteResponse{
|
vr <- &viper.RemoteResponse{
|
||||||
// Error: resp.Error,
|
Error: resp.Error,
|
||||||
// Value: resp.Value,
|
Value: resp.Value,
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }(cryptoResponseCh, viperResponsCh, quitwc, quit)
|
}(cryptoResponseCh, viperResponsCh, quitwc, quit)
|
||||||
|
|
||||||
// return viperResponsCh, quitwc
|
return viperResponsCh, quitwc
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
|
func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
|
||||||
// var cm crypt.ConfigManager
|
var cm crypt.ConfigManager
|
||||||
// var err error
|
var err error
|
||||||
|
|
||||||
// if rp.SecretKeyring() != "" {
|
if rp.SecretKeyring() != "" {
|
||||||
// var kr *os.File
|
var kr *os.File
|
||||||
// kr, err = os.Open(rp.SecretKeyring())
|
kr, err = os.Open(rp.SecretKeyring())
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, err
|
return nil, err
|
||||||
// }
|
}
|
||||||
// defer kr.Close()
|
defer kr.Close()
|
||||||
// switch rp.Provider() {
|
switch rp.Provider() {
|
||||||
// case "etcd":
|
case "etcd":
|
||||||
// cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
|
cm, err = crypt.NewEtcdConfigManager([]string{rp.Endpoint()}, kr)
|
||||||
// case "etcd3":
|
case "firestore":
|
||||||
// cm, err = crypt.NewEtcdV3ConfigManager([]string{rp.Endpoint()}, kr)
|
cm, err = crypt.NewFirestoreConfigManager([]string{rp.Endpoint()}, kr)
|
||||||
// case "firestore":
|
default:
|
||||||
// cm, err = crypt.NewFirestoreConfigManager([]string{rp.Endpoint()}, kr)
|
cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
|
||||||
// default:
|
}
|
||||||
// cm, err = crypt.NewConsulConfigManager([]string{rp.Endpoint()}, kr)
|
} else {
|
||||||
// }
|
switch rp.Provider() {
|
||||||
// } else {
|
case "etcd":
|
||||||
// switch rp.Provider() {
|
cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
|
||||||
// case "etcd":
|
case "firestore":
|
||||||
// cm, err = crypt.NewStandardEtcdConfigManager([]string{rp.Endpoint()})
|
cm, err = crypt.NewStandardFirestoreConfigManager([]string{rp.Endpoint()})
|
||||||
// case "etcd3":
|
default:
|
||||||
// cm, err = crypt.NewStandardEtcdV3ConfigManager([]string{rp.Endpoint()})
|
cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
|
||||||
// case "firestore":
|
}
|
||||||
// cm, err = crypt.NewStandardFirestoreConfigManager([]string{rp.Endpoint()})
|
}
|
||||||
// default:
|
if err != nil {
|
||||||
// cm, err = crypt.NewStandardConsulConfigManager([]string{rp.Endpoint()})
|
return nil, err
|
||||||
// }
|
}
|
||||||
// }
|
return cm, nil
|
||||||
// if err != nil {
|
}
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// return cm, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func init() {
|
func init() {
|
||||||
// viper.RemoteConfig = &remoteConfigProvider{}
|
viper.RemoteConfig = &remoteConfigProvider{}
|
||||||
// }
|
}
|
||||||
|
|
71
util.go
71
util.go
|
@ -18,7 +18,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"git.internal/re/cast"
|
"github.com/spf13/afero"
|
||||||
|
"github.com/spf13/cast"
|
||||||
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigParseError denotes failing to parse configuration file.
|
// ConfigParseError denotes failing to parse configuration file.
|
||||||
|
@ -64,25 +66,18 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
||||||
return nm
|
return nm
|
||||||
}
|
}
|
||||||
|
|
||||||
func insensitiviseVal(val interface{}) interface{} {
|
|
||||||
switch val.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
// nested map: cast and recursively insensitivise
|
|
||||||
val = cast.ToStringMap(val)
|
|
||||||
insensitiviseMap(val.(map[string]interface{}))
|
|
||||||
case map[string]interface{}:
|
|
||||||
// nested map: recursively insensitivise
|
|
||||||
insensitiviseMap(val.(map[string]interface{}))
|
|
||||||
case []interface{}:
|
|
||||||
// nested array: recursively insensitivise
|
|
||||||
insensitiveArray(val.([]interface{}))
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func insensitiviseMap(m map[string]interface{}) {
|
func insensitiviseMap(m map[string]interface{}) {
|
||||||
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)
|
||||||
|
@ -93,20 +88,26 @@ func insensitiviseMap(m map[string]interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func insensitiveArray(a []interface{}) {
|
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 Logger, inPath string) string {
|
|
||||||
logger.Info("trying to resolve absolute path", "path", inPath)
|
|
||||||
|
|
||||||
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
|
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))
|
||||||
|
|
||||||
|
var value, suffix string
|
||||||
|
if end == -1 {
|
||||||
|
value = os.Getenv(inPath[1:])
|
||||||
|
} else {
|
||||||
|
value = os.Getenv(inPath[1:end])
|
||||||
|
suffix = inPath[end:]
|
||||||
|
}
|
||||||
|
|
||||||
|
inPath = value + suffix
|
||||||
|
}
|
||||||
|
|
||||||
if filepath.IsAbs(inPath) {
|
if filepath.IsAbs(inPath) {
|
||||||
return filepath.Clean(inPath)
|
return filepath.Clean(inPath)
|
||||||
|
@ -117,11 +118,23 @@ func absPathify(logger 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 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
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.internal/re/viper/internal/testutil"
|
"github.com/spf13/viper/internal/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCopyAndInsensitiviseMap(t *testing.T) {
|
func TestCopyAndInsensitiviseMap(t *testing.T) {
|
||||||
|
@ -58,8 +58,6 @@ func TestCopyAndInsensitiviseMap(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAbsPathify(t *testing.T) {
|
func TestAbsPathify(t *testing.T) {
|
||||||
skipWindows(t)
|
|
||||||
|
|
||||||
home := userHomeDir()
|
home := userHomeDir()
|
||||||
homer := filepath.Join(home, "homer")
|
homer := filepath.Join(home, "homer")
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
|
@ -87,7 +85,7 @@ func TestAbsPathify(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
got := absPathify(jwwLogger{}, test.input)
|
got := absPathify(test.input)
|
||||||
if got != test.output {
|
if got != test.output {
|
||||||
t.Errorf("Got %v\nexpected\n%q", got, test.output)
|
t.Errorf("Got %v\nexpected\n%q", got, test.output)
|
||||||
}
|
}
|
||||||
|
|
506
viper.go
506
viper.go
|
@ -34,20 +34,21 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.internal/re/afero"
|
|
||||||
"git.internal/re/cast"
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/magiconair/properties"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
"github.com/spf13/cast"
|
||||||
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
"github.com/subosito/gotenv"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"git.internal/re/viper/internal/encoding"
|
"github.com/spf13/viper/internal/encoding"
|
||||||
"git.internal/re/viper/internal/encoding/dotenv"
|
"github.com/spf13/viper/internal/encoding/hcl"
|
||||||
"git.internal/re/viper/internal/encoding/hcl"
|
"github.com/spf13/viper/internal/encoding/json"
|
||||||
"git.internal/re/viper/internal/encoding/ini"
|
"github.com/spf13/viper/internal/encoding/toml"
|
||||||
"git.internal/re/viper/internal/encoding/javaproperties"
|
"github.com/spf13/viper/internal/encoding/yaml"
|
||||||
"git.internal/re/viper/internal/encoding/json"
|
|
||||||
"git.internal/re/viper/internal/encoding/toml"
|
|
||||||
"git.internal/re/viper/internal/encoding/yaml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigMarshalError happens when failing to marshal the configuration.
|
// ConfigMarshalError happens when failing to marshal the configuration.
|
||||||
|
@ -67,8 +68,44 @@ type RemoteResponse struct {
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
encoderRegistry = encoding.NewEncoderRegistry()
|
||||||
|
decoderRegistry = encoding.NewDecoderRegistry()
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
v = New()
|
v = New()
|
||||||
|
|
||||||
|
{
|
||||||
|
codec := yaml.Codec{}
|
||||||
|
|
||||||
|
encoderRegistry.RegisterEncoder("yaml", codec)
|
||||||
|
decoderRegistry.RegisterDecoder("yaml", codec)
|
||||||
|
|
||||||
|
encoderRegistry.RegisterEncoder("yml", codec)
|
||||||
|
decoderRegistry.RegisterDecoder("yml", codec)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
codec := json.Codec{}
|
||||||
|
|
||||||
|
encoderRegistry.RegisterEncoder("json", codec)
|
||||||
|
decoderRegistry.RegisterDecoder("json", codec)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
codec := toml.Codec{}
|
||||||
|
|
||||||
|
encoderRegistry.RegisterEncoder("toml", codec)
|
||||||
|
decoderRegistry.RegisterDecoder("toml", codec)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
codec := hcl.Codec{}
|
||||||
|
|
||||||
|
encoderRegistry.RegisterEncoder("hcl", codec)
|
||||||
|
decoderRegistry.RegisterDecoder("hcl", codec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type remoteConfigFactory interface {
|
type remoteConfigFactory interface {
|
||||||
|
@ -132,10 +169,10 @@ type DecoderConfigOption func(*mapstructure.DecoderConfig)
|
||||||
// DecodeHook returns a DecoderConfigOption which overrides the default
|
// DecodeHook returns a DecoderConfigOption which overrides the default
|
||||||
// DecoderConfig.DecodeHook value, the default is:
|
// DecoderConfig.DecodeHook value, the default is:
|
||||||
//
|
//
|
||||||
// mapstructure.ComposeDecodeHookFunc(
|
// mapstructure.ComposeDecodeHookFunc(
|
||||||
// mapstructure.StringToTimeDurationHookFunc(),
|
// mapstructure.StringToTimeDurationHookFunc(),
|
||||||
// mapstructure.StringToSliceHookFunc(","),
|
// mapstructure.StringToSliceHookFunc(","),
|
||||||
// )
|
// )
|
||||||
func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
|
func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
|
||||||
return func(c *mapstructure.DecoderConfig) {
|
return func(c *mapstructure.DecoderConfig) {
|
||||||
c.DecodeHook = hook
|
c.DecodeHook = hook
|
||||||
|
@ -156,18 +193,18 @@ func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption {
|
||||||
//
|
//
|
||||||
// For example, if values from the following sources were loaded:
|
// For example, if values from the following sources were loaded:
|
||||||
//
|
//
|
||||||
// Defaults : {
|
// Defaults : {
|
||||||
// "secret": "",
|
// "secret": "",
|
||||||
// "user": "default",
|
// "user": "default",
|
||||||
// "endpoint": "https://localhost"
|
// "endpoint": "https://localhost"
|
||||||
// }
|
// }
|
||||||
// Config : {
|
// Config : {
|
||||||
// "user": "root"
|
// "user": "root"
|
||||||
// "secret": "defaultsecret"
|
// "secret": "defaultsecret"
|
||||||
// }
|
// }
|
||||||
// Env : {
|
// Env : {
|
||||||
// "secret": "somesecretkey"
|
// "secret": "somesecretkey"
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The resulting config will have the following values:
|
// The resulting config will have the following values:
|
||||||
//
|
//
|
||||||
|
@ -215,13 +252,11 @@ type Viper struct {
|
||||||
aliases map[string]string
|
aliases map[string]string
|
||||||
typeByDefValue bool
|
typeByDefValue bool
|
||||||
|
|
||||||
|
// Store read properties on the object so that we can write back in order with comments.
|
||||||
|
// This will only be used if the configuration read is a properties file.
|
||||||
|
properties *properties.Properties
|
||||||
|
|
||||||
onConfigChange func(fsnotify.Event)
|
onConfigChange func(fsnotify.Event)
|
||||||
|
|
||||||
logger Logger
|
|
||||||
|
|
||||||
// TODO: should probably be protected with a mutex
|
|
||||||
encoderRegistry *encoding.EncoderRegistry
|
|
||||||
decoderRegistry *encoding.DecoderRegistry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns an initialized Viper instance.
|
// New returns an initialized Viper instance.
|
||||||
|
@ -229,7 +264,7 @@ func New() *Viper {
|
||||||
v := new(Viper)
|
v := new(Viper)
|
||||||
v.keyDelim = "."
|
v.keyDelim = "."
|
||||||
v.configName = "config"
|
v.configName = "config"
|
||||||
v.configPermissions = os.FileMode(0o644)
|
v.configPermissions = os.FileMode(0644)
|
||||||
v.fs = afero.NewOsFs()
|
v.fs = afero.NewOsFs()
|
||||||
v.config = make(map[string]interface{})
|
v.config = make(map[string]interface{})
|
||||||
v.override = make(map[string]interface{})
|
v.override = make(map[string]interface{})
|
||||||
|
@ -239,9 +274,6 @@ func New() *Viper {
|
||||||
v.env = make(map[string][]string)
|
v.env = make(map[string][]string)
|
||||||
v.aliases = make(map[string]string)
|
v.aliases = make(map[string]string)
|
||||||
v.typeByDefValue = false
|
v.typeByDefValue = false
|
||||||
v.logger = jwwLogger{}
|
|
||||||
|
|
||||||
v.resetEncoding()
|
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -289,8 +321,6 @@ func NewWithOptions(opts ...Option) *Viper {
|
||||||
opt.apply(v)
|
opt.apply(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.resetEncoding()
|
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,86 +329,8 @@ func NewWithOptions(opts ...Option) *Viper {
|
||||||
// can use it in their testing as well.
|
// can use it in their testing as well.
|
||||||
func Reset() {
|
func Reset() {
|
||||||
v = New()
|
v = New()
|
||||||
SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
|
SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"}
|
||||||
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: make this lazy initialization instead
|
|
||||||
func (v *Viper) resetEncoding() {
|
|
||||||
encoderRegistry := encoding.NewEncoderRegistry()
|
|
||||||
decoderRegistry := encoding.NewDecoderRegistry()
|
|
||||||
|
|
||||||
{
|
|
||||||
codec := yaml.Codec{}
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("yaml", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("yaml", codec)
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("yml", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("yml", codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
codec := json.Codec{}
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("json", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("json", codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
codec := toml.Codec{}
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("toml", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("toml", codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
codec := hcl.Codec{}
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("hcl", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("hcl", codec)
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("tfvars", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("tfvars", codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
codec := ini.Codec{
|
|
||||||
KeyDelimiter: v.keyDelim,
|
|
||||||
LoadOptions: v.iniLoadOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("ini", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("ini", codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
codec := &javaproperties.Codec{
|
|
||||||
KeyDelimiter: v.keyDelim,
|
|
||||||
}
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("properties", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("properties", codec)
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("props", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("props", codec)
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("prop", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("prop", codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
codec := &dotenv.Codec{}
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("dotenv", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("dotenv", codec)
|
|
||||||
|
|
||||||
encoderRegistry.RegisterEncoder("env", codec)
|
|
||||||
decoderRegistry.RegisterDecoder("env", codec)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.encoderRegistry = encoderRegistry
|
|
||||||
v.decoderRegistry = decoderRegistry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type defaultRemoteProvider struct {
|
type defaultRemoteProvider struct {
|
||||||
|
@ -416,10 +368,10 @@ type RemoteProvider interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportedExts are universally supported extensions.
|
// SupportedExts are universally supported extensions.
|
||||||
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
|
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"}
|
||||||
|
|
||||||
// SupportedRemoteProviders are universally supported remote providers.
|
// SupportedRemoteProviders are universally supported remote providers.
|
||||||
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"}
|
||||||
|
|
||||||
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
||||||
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
|
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
|
||||||
|
@ -463,8 +415,9 @@ func (v *Viper) WatchConfig() {
|
||||||
// we only care about the config file with the following cases:
|
// we only care about the config file with the following cases:
|
||||||
// 1 - if the config file was modified or created
|
// 1 - if the config file was modified or created
|
||||||
// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
|
// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
|
||||||
|
const writeOrCreateMask = fsnotify.Write | fsnotify.Create
|
||||||
if (filepath.Clean(event.Name) == configFile &&
|
if (filepath.Clean(event.Name) == configFile &&
|
||||||
(event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) ||
|
event.Op&writeOrCreateMask != 0) ||
|
||||||
(currentConfigFile != "" && currentConfigFile != realConfigFile) {
|
(currentConfigFile != "" && currentConfigFile != realConfigFile) {
|
||||||
realConfigFile = currentConfigFile
|
realConfigFile = currentConfigFile
|
||||||
err := v.ReadInConfig()
|
err := v.ReadInConfig()
|
||||||
|
@ -474,7 +427,8 @@ func (v *Viper) WatchConfig() {
|
||||||
if v.onConfigChange != nil {
|
if v.onConfigChange != nil {
|
||||||
v.onConfigChange(event)
|
v.onConfigChange(event)
|
||||||
}
|
}
|
||||||
} else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) {
|
} else if filepath.Clean(event.Name) == configFile &&
|
||||||
|
event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
|
||||||
eventsWG.Done()
|
eventsWG.Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -560,9 +514,8 @@ func AddConfigPath(in string) { v.AddConfigPath(in) }
|
||||||
|
|
||||||
func (v *Viper) AddConfigPath(in string) {
|
func (v *Viper) AddConfigPath(in string) {
|
||||||
if in != "" {
|
if in != "" {
|
||||||
absin := absPathify(v.logger, in)
|
absin := absPathify(in)
|
||||||
|
jww.INFO.Println("adding", absin, "to paths to search")
|
||||||
v.logger.Info("adding path to search paths", "path", absin)
|
|
||||||
if !stringInSlice(absin, v.configPaths) {
|
if !stringInSlice(absin, v.configPaths) {
|
||||||
v.configPaths = append(v.configPaths, absin)
|
v.configPaths = append(v.configPaths, absin)
|
||||||
}
|
}
|
||||||
|
@ -571,7 +524,7 @@ func (v *Viper) AddConfigPath(in string) {
|
||||||
|
|
||||||
// AddRemoteProvider adds a remote configuration source.
|
// AddRemoteProvider adds a remote configuration source.
|
||||||
// Remote Providers are searched in the order they are added.
|
// Remote Providers are searched in the order they are added.
|
||||||
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
|
// provider is a string value: "etcd", "consul" or "firestore" are currently supported.
|
||||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
||||||
// path is the path in the k/v store to retrieve configuration
|
// path is the path in the k/v store to retrieve configuration
|
||||||
// To retrieve a config file called myapp.json from /configs/myapp.json
|
// To retrieve a config file called myapp.json from /configs/myapp.json
|
||||||
|
@ -586,8 +539,7 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
|
||||||
return UnsupportedRemoteProviderError(provider)
|
return UnsupportedRemoteProviderError(provider)
|
||||||
}
|
}
|
||||||
if provider != "" && endpoint != "" {
|
if provider != "" && endpoint != "" {
|
||||||
v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)
|
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
||||||
|
|
||||||
rp := &defaultRemoteProvider{
|
rp := &defaultRemoteProvider{
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
provider: provider,
|
provider: provider,
|
||||||
|
@ -602,7 +554,7 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
|
||||||
|
|
||||||
// AddSecureRemoteProvider adds a remote configuration source.
|
// AddSecureRemoteProvider adds a remote configuration source.
|
||||||
// Secure Remote Providers are searched in the order they are added.
|
// Secure Remote Providers are searched in the order they are added.
|
||||||
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
|
// provider is a string value: "etcd", "consul" or "firestore" are currently supported.
|
||||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
// 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
|
// 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
|
// path is the path in the k/v store to retrieve configuration
|
||||||
|
@ -619,8 +571,7 @@ func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring
|
||||||
return UnsupportedRemoteProviderError(provider)
|
return UnsupportedRemoteProviderError(provider)
|
||||||
}
|
}
|
||||||
if provider != "" && endpoint != "" {
|
if provider != "" && endpoint != "" {
|
||||||
v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint)
|
jww.INFO.Printf("adding %s:%s to remote provider list", provider, endpoint)
|
||||||
|
|
||||||
rp := &defaultRemoteProvider{
|
rp := &defaultRemoteProvider{
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
provider: provider,
|
provider: provider,
|
||||||
|
@ -783,8 +734,7 @@ func (v *Viper) searchMapWithPathPrefixes(
|
||||||
// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
|
// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
|
||||||
// on its path in the map.
|
// on its path in the map.
|
||||||
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
||||||
//
|
// "foo.bar.baz" in a lower-priority map
|
||||||
// "foo.bar.baz" in a lower-priority map
|
|
||||||
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
|
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
|
||||||
var parentVal interface{}
|
var parentVal interface{}
|
||||||
for i := 1; i < len(path); i++ {
|
for i := 1; i < len(path); i++ {
|
||||||
|
@ -809,8 +759,7 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
|
||||||
// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
|
// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere
|
||||||
// in a sub-path of the map.
|
// in a sub-path of the map.
|
||||||
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
||||||
//
|
// "foo.bar.baz" in a lower-priority map
|
||||||
// "foo.bar.baz" in a lower-priority map
|
|
||||||
func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
|
func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
|
||||||
// unify input map
|
// unify input map
|
||||||
var m map[string]interface{}
|
var m map[string]interface{}
|
||||||
|
@ -835,8 +784,7 @@ func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
|
||||||
// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
|
// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere
|
||||||
// in the environment, when automatic env is on.
|
// in the environment, when automatic env is on.
|
||||||
// e.g., if "foo.bar" has a value in the environment, it “shadows”
|
// e.g., if "foo.bar" has a value in the environment, it “shadows”
|
||||||
//
|
// "foo.bar.baz" in a lower-priority map
|
||||||
// "foo.bar.baz" in a lower-priority map
|
|
||||||
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
||||||
var parentKey string
|
var parentKey string
|
||||||
for i := 1; i < len(path); i++ {
|
for i := 1; i < len(path); i++ {
|
||||||
|
@ -857,11 +805,11 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string {
|
||||||
// would return a string slice for the key if the key's type is inferred by
|
// would return a string slice for the key if the key's type is inferred by
|
||||||
// the default value and the Get function would return:
|
// the default value and the Get function would return:
|
||||||
//
|
//
|
||||||
// []string {"a", "b", "c"}
|
// []string {"a", "b", "c"}
|
||||||
//
|
//
|
||||||
// Otherwise the Get function would return:
|
// Otherwise the Get function would return:
|
||||||
//
|
//
|
||||||
// "a b c"
|
// "a b c"
|
||||||
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
|
func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) }
|
||||||
|
|
||||||
func (v *Viper) SetTypeByDefaultValue(enable bool) {
|
func (v *Viper) SetTypeByDefaultValue(enable bool) {
|
||||||
|
@ -989,13 +937,6 @@ func (v *Viper) GetUint(key string) uint {
|
||||||
return cast.ToUint(v.Get(key))
|
return cast.ToUint(v.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUint16 returns the value associated with the key as an unsigned integer.
|
|
||||||
func GetUint16(key string) uint16 { return v.GetUint16(key) }
|
|
||||||
|
|
||||||
func (v *Viper) GetUint16(key string) uint16 {
|
|
||||||
return cast.ToUint16(v.Get(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUint32 returns the value associated with the key as an unsigned integer.
|
// GetUint32 returns the value associated with the key as an unsigned integer.
|
||||||
func GetUint32(key string) uint32 { return v.GetUint32(key) }
|
func GetUint32(key string) uint32 { return v.GetUint32(key) }
|
||||||
|
|
||||||
|
@ -1145,8 +1086,9 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error {
|
||||||
// BindPFlag binds a specific key to a pflag (as used by cobra).
|
// BindPFlag binds a specific key to a pflag (as used by cobra).
|
||||||
// Example (where serverCmd is a Cobra instance):
|
// Example (where serverCmd is a Cobra instance):
|
||||||
//
|
//
|
||||||
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
|
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
|
||||||
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
|
// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
|
||||||
|
//
|
||||||
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
|
func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) }
|
||||||
|
|
||||||
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
|
func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error {
|
||||||
|
@ -1204,17 +1146,6 @@ func (v *Viper) BindEnv(input ...string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustBindEnv wraps BindEnv in a panic.
|
|
||||||
// If there is an error binding an environment variable, MustBindEnv will
|
|
||||||
// panic.
|
|
||||||
func MustBindEnv(input ...string) { v.MustBindEnv(input...) }
|
|
||||||
|
|
||||||
func (v *Viper) MustBindEnv(input ...string) {
|
|
||||||
if err := v.BindEnv(input...); err != nil {
|
|
||||||
panic(fmt.Sprintf("error while binding environment variable: %v", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a key, find the value.
|
// Given a key, find the value.
|
||||||
//
|
//
|
||||||
// Viper will check to see if an alias exists first.
|
// Viper will check to see if an alias exists first.
|
||||||
|
@ -1456,15 +1387,14 @@ func (v *Viper) registerAlias(alias string, key string) {
|
||||||
v.aliases[alias] = key
|
v.aliases[alias] = key
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
v.logger.Warn("creating circular reference alias", "alias", alias, "key", key, "real_key", v.realKey(key))
|
jww.WARN.Println("Creating circular reference alias", alias, key, v.realKey(key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) realKey(key string) string {
|
func (v *Viper) realKey(key string) string {
|
||||||
newkey, exists := v.aliases[key]
|
newkey, exists := v.aliases[key]
|
||||||
if exists {
|
if exists {
|
||||||
v.logger.Debug("key is an alias", "alias", key, "to", newkey)
|
jww.DEBUG.Println("Alias", key, "to", newkey)
|
||||||
|
|
||||||
return v.realKey(newkey)
|
return v.realKey(newkey)
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
|
@ -1474,13 +1404,11 @@ func (v *Viper) realKey(key string) string {
|
||||||
func InConfig(key string) bool { return v.InConfig(key) }
|
func InConfig(key string) bool { return v.InConfig(key) }
|
||||||
|
|
||||||
func (v *Viper) InConfig(key string) bool {
|
func (v *Viper) InConfig(key string) bool {
|
||||||
lcaseKey := strings.ToLower(key)
|
|
||||||
|
|
||||||
// if the requested key is an alias, then return the proper key
|
// if the requested key is an alias, then return the proper key
|
||||||
lcaseKey = v.realKey(lcaseKey)
|
key = v.realKey(key)
|
||||||
path := strings.Split(lcaseKey, v.keyDelim)
|
|
||||||
|
|
||||||
return v.searchIndexableWithPathPrefixes(v.config, path) != nil
|
_, exists := v.config[key]
|
||||||
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefault sets the default value for this key.
|
// SetDefault sets the default value for this key.
|
||||||
|
@ -1525,7 +1453,7 @@ func (v *Viper) Set(key string, value interface{}) {
|
||||||
func ReadInConfig() error { return v.ReadInConfig() }
|
func ReadInConfig() error { return v.ReadInConfig() }
|
||||||
|
|
||||||
func (v *Viper) ReadInConfig() error {
|
func (v *Viper) ReadInConfig() error {
|
||||||
v.logger.Info("attempting to read in config file")
|
jww.INFO.Println("Attempting to read in config file")
|
||||||
filename, err := v.getConfigFile()
|
filename, err := v.getConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1535,7 +1463,7 @@ func (v *Viper) ReadInConfig() error {
|
||||||
return UnsupportedConfigError(v.getConfigType())
|
return UnsupportedConfigError(v.getConfigType())
|
||||||
}
|
}
|
||||||
|
|
||||||
v.logger.Debug("reading file", "file", filename)
|
jww.DEBUG.Println("Reading file: ", filename)
|
||||||
file, err := afero.ReadFile(v.fs, filename)
|
file, err := afero.ReadFile(v.fs, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1556,7 +1484,7 @@ func (v *Viper) ReadInConfig() error {
|
||||||
func MergeInConfig() error { return v.MergeInConfig() }
|
func MergeInConfig() error { return v.MergeInConfig() }
|
||||||
|
|
||||||
func (v *Viper) MergeInConfig() error {
|
func (v *Viper) MergeInConfig() error {
|
||||||
v.logger.Info("attempting to merge in config file")
|
jww.INFO.Println("Attempting to merge in config file")
|
||||||
filename, err := v.getConfigFile()
|
filename, err := v.getConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1647,12 +1575,11 @@ func (v *Viper) SafeWriteConfigAs(filename string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Viper) writeConfig(filename string, force bool) error {
|
func (v *Viper) writeConfig(filename string, force bool) error {
|
||||||
v.logger.Info("attempting to write configuration to file")
|
jww.INFO.Println("Attempting to write configuration to file.")
|
||||||
|
|
||||||
var configType string
|
var configType string
|
||||||
|
|
||||||
ext := filepath.Ext(filename)
|
ext := filepath.Ext(filename)
|
||||||
if ext != "" && ext != filepath.Base(filename) {
|
if ext != "" {
|
||||||
configType = ext[1:]
|
configType = ext[1:]
|
||||||
} else {
|
} else {
|
||||||
configType = v.configType
|
configType = v.configType
|
||||||
|
@ -1695,11 +1622,53 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||||
buf.ReadFrom(in)
|
buf.ReadFrom(in)
|
||||||
|
|
||||||
switch format := strings.ToLower(v.getConfigType()); format {
|
switch format := strings.ToLower(v.getConfigType()); format {
|
||||||
case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env":
|
case "yaml", "yml", "json", "toml", "hcl":
|
||||||
err := v.decoderRegistry.Decode(format, buf.Bytes(), c)
|
err := decoderRegistry.Decode(format, buf.Bytes(), &c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ConfigParseError{err}
|
return ConfigParseError{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "dotenv", "env":
|
||||||
|
env, err := gotenv.StrictParse(buf)
|
||||||
|
if err != nil {
|
||||||
|
return ConfigParseError{err}
|
||||||
|
}
|
||||||
|
for k, v := range env {
|
||||||
|
c[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
case "properties", "props", "prop":
|
||||||
|
v.properties = properties.NewProperties()
|
||||||
|
var err error
|
||||||
|
if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
|
||||||
|
return ConfigParseError{err}
|
||||||
|
}
|
||||||
|
for _, key := range v.properties.Keys() {
|
||||||
|
value, _ := v.properties.Get(key)
|
||||||
|
// recursively build nested maps
|
||||||
|
path := strings.Split(key, ".")
|
||||||
|
lastKey := strings.ToLower(path[len(path)-1])
|
||||||
|
deepestMap := deepSearch(c, path[0:len(path)-1])
|
||||||
|
// set innermost value
|
||||||
|
deepestMap[lastKey] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
case "ini":
|
||||||
|
cfg := ini.Empty(v.iniLoadOptions)
|
||||||
|
err := cfg.Append(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return ConfigParseError{err}
|
||||||
|
}
|
||||||
|
sections := cfg.Sections()
|
||||||
|
for i := 0; i < len(sections); i++ {
|
||||||
|
section := sections[i]
|
||||||
|
keys := section.Keys()
|
||||||
|
for j := 0; j < len(keys); j++ {
|
||||||
|
key := keys[j]
|
||||||
|
value := cfg.Section(section.Name()).Key(key.Name()).String()
|
||||||
|
c[section.Name()+"."+key.Name()] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insensitiviseMap(c)
|
insensitiviseMap(c)
|
||||||
|
@ -1710,8 +1679,8 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||||
func (v *Viper) marshalWriter(f afero.File, configType string) error {
|
func (v *Viper) marshalWriter(f afero.File, configType string) error {
|
||||||
c := v.AllSettings()
|
c := v.AllSettings()
|
||||||
switch configType {
|
switch configType {
|
||||||
case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "prop", "props", "properties", "dotenv", "env":
|
case "yaml", "yml", "json", "toml", "hcl":
|
||||||
b, err := v.encoderRegistry.Encode(configType, c)
|
b, err := encoderRegistry.Encode(configType, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ConfigMarshalError{err}
|
return ConfigMarshalError{err}
|
||||||
}
|
}
|
||||||
|
@ -1720,6 +1689,50 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ConfigMarshalError{err}
|
return ConfigMarshalError{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "prop", "props", "properties":
|
||||||
|
if v.properties == nil {
|
||||||
|
v.properties = properties.NewProperties()
|
||||||
|
}
|
||||||
|
p := v.properties
|
||||||
|
for _, key := range v.AllKeys() {
|
||||||
|
_, _, err := p.Set(key, v.GetString(key))
|
||||||
|
if err != nil {
|
||||||
|
return ConfigMarshalError{err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := p.WriteComment(f, "#", properties.UTF8)
|
||||||
|
if err != nil {
|
||||||
|
return ConfigMarshalError{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "dotenv", "env":
|
||||||
|
lines := []string{}
|
||||||
|
for _, key := range v.AllKeys() {
|
||||||
|
envName := strings.ToUpper(strings.Replace(key, ".", "_", -1))
|
||||||
|
val := v.Get(key)
|
||||||
|
lines = append(lines, fmt.Sprintf("%v=%v", envName, val))
|
||||||
|
}
|
||||||
|
s := strings.Join(lines, "\n")
|
||||||
|
if _, err := f.WriteString(s); err != nil {
|
||||||
|
return ConfigMarshalError{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "ini":
|
||||||
|
keys := v.AllKeys()
|
||||||
|
cfg := ini.Empty()
|
||||||
|
ini.PrettyFormat = false
|
||||||
|
for i := 0; i < len(keys); i++ {
|
||||||
|
key := keys[i]
|
||||||
|
lastSep := strings.LastIndex(key, ".")
|
||||||
|
sectionName := key[:(lastSep)]
|
||||||
|
keyName := key[(lastSep + 1):]
|
||||||
|
if sectionName == "default" {
|
||||||
|
sectionName = ""
|
||||||
|
}
|
||||||
|
cfg.Section(sectionName).Key(keyName).SetValue(v.GetString(key))
|
||||||
|
}
|
||||||
|
cfg.WriteTo(f)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1736,8 +1749,7 @@ func keyExists(k string, m map[string]interface{}) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func castToMapStringInterface(
|
func castToMapStringInterface(
|
||||||
src map[interface{}]interface{},
|
src map[interface{}]interface{}) map[string]interface{} {
|
||||||
) map[string]interface{} {
|
|
||||||
tgt := map[string]interface{}{}
|
tgt := map[string]interface{}{}
|
||||||
for k, v := range src {
|
for k, v := range src {
|
||||||
tgt[fmt.Sprintf("%v", k)] = v
|
tgt[fmt.Sprintf("%v", k)] = v
|
||||||
|
@ -1775,12 +1787,11 @@ func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{}
|
||||||
// deep. Both map types are supported as there is a go-yaml fork that uses
|
// deep. Both map types are supported as there is a go-yaml fork that uses
|
||||||
// `map[string]interface{}` instead.
|
// `map[string]interface{}` instead.
|
||||||
func mergeMaps(
|
func mergeMaps(
|
||||||
src, tgt map[string]interface{}, itgt map[interface{}]interface{},
|
src, tgt map[string]interface{}, itgt map[interface{}]interface{}) {
|
||||||
) {
|
|
||||||
for sk, sv := range src {
|
for sk, sv := range src {
|
||||||
tk := keyExists(sk, tgt)
|
tk := keyExists(sk, tgt)
|
||||||
if tk == "" {
|
if tk == "" {
|
||||||
v.logger.Trace("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv)
|
jww.TRACE.Printf("tk=\"\", tgt[%s]=%v", sk, sv)
|
||||||
tgt[sk] = sv
|
tgt[sk] = sv
|
||||||
if itgt != nil {
|
if itgt != nil {
|
||||||
itgt[sk] = sv
|
itgt[sk] = sv
|
||||||
|
@ -1790,7 +1801,7 @@ func mergeMaps(
|
||||||
|
|
||||||
tv, ok := tgt[tk]
|
tv, ok := tgt[tk]
|
||||||
if !ok {
|
if !ok {
|
||||||
v.logger.Trace("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv)
|
jww.TRACE.Printf("tgt[%s] != ok, tgt[%s]=%v", tk, sk, sv)
|
||||||
tgt[sk] = sv
|
tgt[sk] = sv
|
||||||
if itgt != nil {
|
if itgt != nil {
|
||||||
itgt[sk] = sv
|
itgt[sk] = sv
|
||||||
|
@ -1800,52 +1811,28 @@ func mergeMaps(
|
||||||
|
|
||||||
svType := reflect.TypeOf(sv)
|
svType := reflect.TypeOf(sv)
|
||||||
tvType := reflect.TypeOf(tv)
|
tvType := reflect.TypeOf(tv)
|
||||||
|
if tvType != nil && svType != tvType { // Allow for the target to be nil
|
||||||
|
jww.ERROR.Printf(
|
||||||
|
"svType != tvType; key=%s, st=%v, tt=%v, sv=%v, tv=%v",
|
||||||
|
sk, svType, tvType, sv, tv)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
v.logger.Trace(
|
jww.TRACE.Printf("processing key=%s, st=%v, tt=%v, sv=%v, tv=%v",
|
||||||
"processing",
|
sk, svType, tvType, sv, tv)
|
||||||
"key", sk,
|
|
||||||
"st", svType,
|
|
||||||
"tt", tvType,
|
|
||||||
"sv", sv,
|
|
||||||
"tv", tv,
|
|
||||||
)
|
|
||||||
|
|
||||||
switch ttv := tv.(type) {
|
switch ttv := tv.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
v.logger.Trace("merging maps (must convert)")
|
jww.TRACE.Printf("merging maps (must convert)")
|
||||||
tsv, ok := sv.(map[interface{}]interface{})
|
tsv := sv.(map[interface{}]interface{})
|
||||||
if !ok {
|
|
||||||
v.logger.Error(
|
|
||||||
"Could not cast sv to map[interface{}]interface{}",
|
|
||||||
"key", sk,
|
|
||||||
"st", svType,
|
|
||||||
"tt", tvType,
|
|
||||||
"sv", sv,
|
|
||||||
"tv", tv,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ssv := castToMapStringInterface(tsv)
|
ssv := castToMapStringInterface(tsv)
|
||||||
stv := castToMapStringInterface(ttv)
|
stv := castToMapStringInterface(ttv)
|
||||||
mergeMaps(ssv, stv, ttv)
|
mergeMaps(ssv, stv, ttv)
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
v.logger.Trace("merging maps")
|
jww.TRACE.Printf("merging maps")
|
||||||
tsv, ok := sv.(map[string]interface{})
|
mergeMaps(sv.(map[string]interface{}), ttv, nil)
|
||||||
if !ok {
|
|
||||||
v.logger.Error(
|
|
||||||
"Could not cast sv to map[string]interface{}",
|
|
||||||
"key", sk,
|
|
||||||
"st", svType,
|
|
||||||
"tt", tvType,
|
|
||||||
"sv", sv,
|
|
||||||
"tv", tv,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mergeMaps(tsv, ttv, nil)
|
|
||||||
default:
|
default:
|
||||||
v.logger.Trace("setting value")
|
jww.TRACE.Printf("setting value")
|
||||||
tgt[tk] = sv
|
tgt[tk] = sv
|
||||||
if itgt != nil {
|
if itgt != nil {
|
||||||
itgt[tk] = sv
|
itgt[tk] = sv
|
||||||
|
@ -1874,17 +1861,13 @@ func (v *Viper) WatchRemoteConfigOnChannel() error {
|
||||||
// Retrieve the first found remote configuration.
|
// Retrieve the first found remote configuration.
|
||||||
func (v *Viper) getKeyValueConfig() error {
|
func (v *Viper) getKeyValueConfig() error {
|
||||||
if RemoteConfig == nil {
|
if RemoteConfig == nil {
|
||||||
return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ git.internal/re/viper/remote'")
|
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 {
|
for _, rp := range v.remoteProviders {
|
||||||
val, err := v.getRemoteConfig(rp)
|
val, err := v.getRemoteConfig(rp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.logger.Error(fmt.Errorf("get remote config: %w", err).Error())
|
jww.ERROR.Printf("get remote config: %s", err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1907,10 +1890,6 @@ func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}
|
||||||
|
|
||||||
// Retrieve the first found remote configuration.
|
// Retrieve the first found remote configuration.
|
||||||
func (v *Viper) watchKeyValueConfigOnChannel() error {
|
func (v *Viper) watchKeyValueConfigOnChannel() error {
|
||||||
if len(v.remoteProviders) == 0 {
|
|
||||||
return RemoteConfigError("No Remote Providers")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rp := range v.remoteProviders {
|
for _, rp := range v.remoteProviders {
|
||||||
respc, _ := RemoteConfig.WatchChannel(rp)
|
respc, _ := RemoteConfig.WatchChannel(rp)
|
||||||
// Todo: Add quit channel
|
// Todo: Add quit channel
|
||||||
|
@ -1928,15 +1907,9 @@ func (v *Viper) watchKeyValueConfigOnChannel() error {
|
||||||
|
|
||||||
// Retrieve the first found remote configuration.
|
// Retrieve the first found remote configuration.
|
||||||
func (v *Viper) watchKeyValueConfig() error {
|
func (v *Viper) watchKeyValueConfig() error {
|
||||||
if len(v.remoteProviders) == 0 {
|
|
||||||
return RemoteConfigError("No Remote Providers")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rp := range v.remoteProviders {
|
for _, rp := range v.remoteProviders {
|
||||||
val, err := v.watchRemoteConfig(rp)
|
val, err := v.watchRemoteConfig(rp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error())
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.kvstore = val
|
v.kvstore = val
|
||||||
|
@ -1979,10 +1952,9 @@ func (v *Viper) AllKeys() []string {
|
||||||
|
|
||||||
// flattenAndMergeMap recursively flattens the given map into a map[string]bool
|
// flattenAndMergeMap recursively flattens the given map into a map[string]bool
|
||||||
// of key paths (used as a set, easier to manipulate than a []string):
|
// of key paths (used as a set, easier to manipulate than a []string):
|
||||||
// - each path is merged into a single key string, delimited with v.keyDelim
|
// - each path is merged into a single key string, delimited with v.keyDelim
|
||||||
// - if a path is shadowed by an earlier value in the initial shadow map,
|
// - if a path is shadowed by an earlier value in the initial shadow map,
|
||||||
// it is skipped.
|
// it is skipped.
|
||||||
//
|
|
||||||
// The resulting set of paths is merged to the given shadow set at the same time.
|
// The resulting set of paths is merged to the given shadow set at the same time.
|
||||||
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
|
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
|
||||||
if shadow != nil && prefix != "" && shadow[prefix] {
|
if shadow != nil && prefix != "" && shadow[prefix] {
|
||||||
|
@ -2131,19 +2103,49 @@ func (v *Viper) getConfigFile() (string, error) {
|
||||||
return v.configFile, nil
|
return v.configFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Viper) searchInPath(in string) (filename string) {
|
||||||
|
jww.DEBUG.Println("Searching for config in ", in)
|
||||||
|
for _, ext := range SupportedExts {
|
||||||
|
jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext))
|
||||||
|
if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b {
|
||||||
|
jww.DEBUG.Println("Found: ", 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 ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search all configPaths for any config file.
|
||||||
|
// Returns the first path that exists (and is a config file).
|
||||||
|
func (v *Viper) findConfigFile() (string, error) {
|
||||||
|
jww.INFO.Println("Searching for config in ", 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)}
|
||||||
|
}
|
||||||
|
|
||||||
// Debug prints all configuration registries for debugging
|
// Debug prints all configuration registries for debugging
|
||||||
// purposes.
|
// purposes.
|
||||||
func Debug() { v.Debug() }
|
func Debug() { v.Debug() }
|
||||||
func DebugTo(w io.Writer) { v.DebugTo(w) }
|
|
||||||
|
|
||||||
func (v *Viper) Debug() { v.DebugTo(os.Stdout) }
|
func (v *Viper) Debug() {
|
||||||
|
fmt.Printf("Aliases:\n%#v\n", v.aliases)
|
||||||
func (v *Viper) DebugTo(w io.Writer) {
|
fmt.Printf("Override:\n%#v\n", v.override)
|
||||||
fmt.Fprintf(w, "Aliases:\n%#v\n", v.aliases)
|
fmt.Printf("PFlags:\n%#v\n", v.pflags)
|
||||||
fmt.Fprintf(w, "Override:\n%#v\n", v.override)
|
fmt.Printf("Env:\n%#v\n", v.env)
|
||||||
fmt.Fprintf(w, "PFlags:\n%#v\n", v.pflags)
|
fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore)
|
||||||
fmt.Fprintf(w, "Env:\n%#v\n", v.env)
|
fmt.Printf("Config:\n%#v\n", v.config)
|
||||||
fmt.Fprintf(w, "Key/Value Store:\n%#v\n", v.kvstore)
|
fmt.Printf("Defaults:\n%#v\n", v.defaults)
|
||||||
fmt.Fprintf(w, "Config:\n%#v\n", v.config)
|
|
||||||
fmt.Fprintf(w, "Defaults:\n%#v\n", v.defaults)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
//go:build !go1.16 || !finder
|
|
||||||
// +build !go1.16 !finder
|
|
||||||
|
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"git.internal/re/afero"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Search all configPaths for any config file.
|
|
||||||
// Returns the first path that exists (and is a config file).
|
|
||||||
func (v *Viper) findConfigFile() (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 ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check 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
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
//go:build go1.16 && finder
|
|
||||||
// +build go1.16,finder
|
|
||||||
|
|
||||||
package viper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.internal/re/afero"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Search all configPaths for any config file.
|
|
||||||
// Returns the first path that exists (and is a config file).
|
|
||||||
func (v *Viper) findConfigFile() (string, error) {
|
|
||||||
finder := finder{
|
|
||||||
paths: v.configPaths,
|
|
||||||
fileNames: []string{v.configName},
|
|
||||||
extensions: SupportedExts,
|
|
||||||
withoutExtension: v.configType != "",
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := finder.Find(afero.NewIOFS(v.fs))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if file == "" {
|
|
||||||
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
|
|
||||||
}
|
|
||||||
|
|
||||||
return file, nil
|
|
||||||
}
|
|
445
viper_test.go
445
viper_test.go
|
@ -9,7 +9,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil" //nolint:staticcheck
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
@ -22,32 +22,32 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.internal/re/afero"
|
|
||||||
"git.internal/re/cast"
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
"github.com/spf13/cast"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"git.internal/re/viper/internal/testutil"
|
"github.com/spf13/viper/internal/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// var yamlExample = []byte(`Hacker: true
|
var yamlExample = []byte(`Hacker: true
|
||||||
// name: steve
|
name: steve
|
||||||
// hobbies:
|
hobbies:
|
||||||
// - skateboarding
|
- skateboarding
|
||||||
// - snowboarding
|
- snowboarding
|
||||||
// - go
|
- go
|
||||||
// clothing:
|
clothing:
|
||||||
// jacket: leather
|
jacket: leather
|
||||||
// trousers: denim
|
trousers: denim
|
||||||
// pants:
|
pants:
|
||||||
// size: large
|
size: large
|
||||||
// age: 35
|
age: 35
|
||||||
// eyes : brown
|
eyes : brown
|
||||||
// beard: true
|
beard: true
|
||||||
// `)
|
`)
|
||||||
|
|
||||||
var yamlExampleWithExtras = []byte(`Existing: true
|
var yamlExampleWithExtras = []byte(`Existing: true
|
||||||
Bogus: true
|
Bogus: true
|
||||||
|
@ -263,13 +263,13 @@ func initDirs(t *testing.T) (string, string, func()) {
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
for _, dir := range testDirs {
|
for _, dir := range testDirs {
|
||||||
err = os.Mkdir(dir, 0o750)
|
err = os.Mkdir(dir, 0750)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
err = ioutil.WriteFile(
|
err = ioutil.WriteFile(
|
||||||
path.Join(dir, config+".toml"),
|
path.Join(dir, config+".toml"),
|
||||||
[]byte("key = \"value is "+dir+"\"\n"),
|
[]byte("key = \"value is "+dir+"\"\n"),
|
||||||
0o640)
|
0640)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,192 +301,65 @@ func (s *stringValue) String() string {
|
||||||
return string(*s)
|
return string(*s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetConfigFile(t *testing.T) {
|
func TestBasics(t *testing.T) {
|
||||||
t.Run("config file set", func(t *testing.T) {
|
SetConfigFile("/tmp/config.yaml")
|
||||||
fs := afero.NewMemMapFs()
|
filename, err := v.getConfigFile()
|
||||||
|
assert.Equal(t, "/tmp/config.yaml", filename)
|
||||||
err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
|
assert.NoError(t, err)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
v := New()
|
|
||||||
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.AddConfigPath("/etc/viper")
|
|
||||||
v.SetConfigFile(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
|
|
||||||
|
|
||||||
filename, err := v.getConfigFile()
|
|
||||||
assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), filename)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("find file", func(t *testing.T) {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
v := New()
|
|
||||||
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.AddConfigPath("/etc/viper")
|
|
||||||
|
|
||||||
filename, err := v.getConfigFile()
|
|
||||||
assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), filename)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("find files only", func(t *testing.T) {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/config"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/etc/config/config.yaml"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
v := New()
|
|
||||||
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.AddConfigPath("/etc")
|
|
||||||
v.AddConfigPath("/etc/config")
|
|
||||||
|
|
||||||
filename, err := v.getConfigFile()
|
|
||||||
assert.Equal(t, testutil.AbsFilePath(t, "/etc/config/config.yaml"), filename)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("precedence", func(t *testing.T) {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
err := fs.Mkdir(testutil.AbsFilePath(t, "/home/viper"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/home/viper/config.zml"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.bml"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = fs.Mkdir(testutil.AbsFilePath(t, "/var/viper"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/var/viper/config.yaml"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
v := New()
|
|
||||||
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.AddConfigPath("/home/viper")
|
|
||||||
v.AddConfigPath("/etc/viper")
|
|
||||||
v.AddConfigPath("/var/viper")
|
|
||||||
|
|
||||||
filename, err := v.getConfigFile()
|
|
||||||
assert.Equal(t, testutil.AbsFilePath(t, "/var/viper/config.yaml"), filename)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("without extension", func(t *testing.T) {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
v := New()
|
|
||||||
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.AddConfigPath("/etc/viper")
|
|
||||||
v.SetConfigName(".dotfilenoext")
|
|
||||||
v.SetConfigType("yaml")
|
|
||||||
|
|
||||||
filename, err := v.getConfigFile()
|
|
||||||
assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"), filename)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("without extension and config type", func(t *testing.T) {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
v := New()
|
|
||||||
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.AddConfigPath("/etc/viper")
|
|
||||||
v.SetConfigName(".dotfilenoext")
|
|
||||||
|
|
||||||
_, err = v.getConfigFile()
|
|
||||||
// unless config type is set, files without extension
|
|
||||||
// are not considered
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadInConfig(t *testing.T) {
|
func TestSearchInPath_WithoutConfigTypeSet(t *testing.T) {
|
||||||
t.Run("config file set", func(t *testing.T) {
|
filename := ".dotfilenoext"
|
||||||
fs := afero.NewMemMapFs()
|
path := "/tmp"
|
||||||
|
file := filepath.Join(path, filename)
|
||||||
|
SetConfigName(filename)
|
||||||
|
AddConfigPath(path)
|
||||||
|
_, createErr := v.fs.Create(file)
|
||||||
|
defer func() {
|
||||||
|
_ = v.fs.Remove(file)
|
||||||
|
}()
|
||||||
|
assert.NoError(t, createErr)
|
||||||
|
_, err := v.getConfigFile()
|
||||||
|
// unless config type is set, files without extension
|
||||||
|
// are not considered
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
err := fs.Mkdir("/etc/viper", 0o777)
|
func TestSearchInPath(t *testing.T) {
|
||||||
require.NoError(t, err)
|
filename := ".dotfilenoext"
|
||||||
|
path := "/tmp"
|
||||||
|
file := filepath.Join(path, filename)
|
||||||
|
SetConfigName(filename)
|
||||||
|
SetConfigType("yaml")
|
||||||
|
AddConfigPath(path)
|
||||||
|
_, createErr := v.fs.Create(file)
|
||||||
|
defer func() {
|
||||||
|
_ = v.fs.Remove(file)
|
||||||
|
}()
|
||||||
|
assert.NoError(t, createErr)
|
||||||
|
filename, err := v.getConfigFile()
|
||||||
|
assert.Equal(t, file, filename)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
|
func TestSearchInPath_FilesOnly(t *testing.T) {
|
||||||
require.NoError(t, err)
|
fs := afero.NewMemMapFs()
|
||||||
|
|
||||||
_, err = file.Write([]byte(`key: value`))
|
err := fs.Mkdir("/tmp/config", 0777)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
file.Close()
|
_, err = fs.Create("/tmp/config/config.yaml")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
v := New()
|
v := New()
|
||||||
|
|
||||||
v.SetFs(fs)
|
v.SetFs(fs)
|
||||||
v.SetConfigFile(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
|
v.AddConfigPath("/tmp")
|
||||||
|
v.AddConfigPath("/tmp/config")
|
||||||
|
|
||||||
err = v.ReadInConfig()
|
filename, err := v.getConfigFile()
|
||||||
require.NoError(t, err)
|
assert.Equal(t, "/tmp/config/config.yaml", filename)
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "value", v.Get("key"))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("find file", func(t *testing.T) {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
|
|
||||||
err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = file.Write([]byte(`key: value`))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
v := New()
|
|
||||||
|
|
||||||
v.SetFs(fs)
|
|
||||||
v.AddConfigPath("/etc/viper")
|
|
||||||
|
|
||||||
err = v.ReadInConfig()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "value", v.Get("key"))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefault(t *testing.T) {
|
func TestDefault(t *testing.T) {
|
||||||
|
@ -509,9 +382,7 @@ func TestUnmarshaling(t *testing.T) {
|
||||||
|
|
||||||
unmarshalReader(r, v.config)
|
unmarshalReader(r, v.config)
|
||||||
assert.True(t, InConfig("name"))
|
assert.True(t, InConfig("name"))
|
||||||
assert.True(t, InConfig("clothing.jacket"))
|
|
||||||
assert.False(t, InConfig("state"))
|
assert.False(t, InConfig("state"))
|
||||||
assert.False(t, InConfig("clothing.hat"))
|
|
||||||
assert.Equal(t, "steve", Get("name"))
|
assert.Equal(t, "steve", Get("name"))
|
||||||
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
|
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
|
||||||
assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
|
assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
|
||||||
|
@ -998,7 +869,7 @@ func TestBindPFlags(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:dupl
|
// nolint: dupl
|
||||||
func TestBindPFlagsStringSlice(t *testing.T) {
|
func TestBindPFlagsStringSlice(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Expected []string
|
Expected []string
|
||||||
|
@ -1046,7 +917,7 @@ func TestBindPFlagsStringSlice(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:dupl
|
// nolint: dupl
|
||||||
func TestBindPFlagsStringArray(t *testing.T) {
|
func TestBindPFlagsStringArray(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Expected []string
|
Expected []string
|
||||||
|
@ -1094,7 +965,7 @@ func TestBindPFlagsStringArray(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:dupl
|
// nolint: dupl
|
||||||
func TestBindPFlagsIntSlice(t *testing.T) {
|
func TestBindPFlagsIntSlice(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Expected []int
|
Expected []int
|
||||||
|
@ -1368,9 +1239,7 @@ func TestReadBufConfig(t *testing.T) {
|
||||||
t.Log(v.AllKeys())
|
t.Log(v.AllKeys())
|
||||||
|
|
||||||
assert.True(t, v.InConfig("name"))
|
assert.True(t, v.InConfig("name"))
|
||||||
assert.True(t, v.InConfig("clothing.jacket"))
|
|
||||||
assert.False(t, v.InConfig("state"))
|
assert.False(t, v.InConfig("state"))
|
||||||
assert.False(t, v.InConfig("clothing.hat"))
|
|
||||||
assert.Equal(t, "steve", v.Get("name"))
|
assert.Equal(t, "steve", v.Get("name"))
|
||||||
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
|
assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
|
||||||
assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
|
assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
|
||||||
|
@ -1558,21 +1427,21 @@ p_ppu = 0.55
|
||||||
p_batters.batter.type = Regular
|
p_batters.batter.type = Regular
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// var yamlWriteExpected = []byte(`age: 35
|
var yamlWriteExpected = []byte(`age: 35
|
||||||
// beard: true
|
beard: true
|
||||||
// clothing:
|
clothing:
|
||||||
// jacket: leather
|
jacket: leather
|
||||||
// pants:
|
pants:
|
||||||
// size: large
|
size: large
|
||||||
// trousers: denim
|
trousers: denim
|
||||||
// eyes: brown
|
eyes: brown
|
||||||
// hacker: true
|
hacker: true
|
||||||
// hobbies:
|
hobbies:
|
||||||
// - skateboarding
|
- skateboarding
|
||||||
// - snowboarding
|
- snowboarding
|
||||||
// - go
|
- go
|
||||||
// name: steve
|
name: steve
|
||||||
// `)
|
`)
|
||||||
|
|
||||||
func TestWriteConfig(t *testing.T) {
|
func TestWriteConfig(t *testing.T) {
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
|
@ -1816,7 +1685,7 @@ func TestSafeWriteConfig(t *testing.T) {
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
|
require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
|
||||||
require.NoError(t, v.SafeWriteConfig())
|
require.NoError(t, v.SafeWriteConfig())
|
||||||
read, err := afero.ReadFile(fs, testutil.AbsFilePath(t, "/test/c.yaml"))
|
read, err := afero.ReadFile(fs, "/test/c.yaml")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, yamlWriteExpected, read)
|
assert.Equal(t, yamlWriteExpected, read)
|
||||||
}
|
}
|
||||||
|
@ -1833,7 +1702,7 @@ func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
|
||||||
func TestSafeWriteConfigWithExistingFile(t *testing.T) {
|
func TestSafeWriteConfigWithExistingFile(t *testing.T) {
|
||||||
v := New()
|
v := New()
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
fs.Create(testutil.AbsFilePath(t, "/test/c.yaml"))
|
fs.Create("/test/c.yaml")
|
||||||
v.SetFs(fs)
|
v.SetFs(fs)
|
||||||
v.AddConfigPath("/test")
|
v.AddConfigPath("/test")
|
||||||
v.SetConfigName("c")
|
v.SetConfigName("c")
|
||||||
|
@ -1869,23 +1738,6 @@ func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
|
||||||
assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
|
assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteHiddenFile(t *testing.T) {
|
|
||||||
v := New()
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
fs.Create(testutil.AbsFilePath(t, "/test/.config"))
|
|
||||||
v.SetFs(fs)
|
|
||||||
|
|
||||||
v.SetConfigName(".config")
|
|
||||||
v.SetConfigType("yaml")
|
|
||||||
v.AddConfigPath("/test")
|
|
||||||
|
|
||||||
err := v.ReadInConfig()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = v.WriteConfig()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var yamlMergeExampleTgt = []byte(`
|
var yamlMergeExampleTgt = []byte(`
|
||||||
hello:
|
hello:
|
||||||
pop: 37890
|
pop: 37890
|
||||||
|
@ -1912,24 +1764,6 @@ hello:
|
||||||
fu: bar
|
fu: bar
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var jsonMergeExampleTgt = []byte(`
|
|
||||||
{
|
|
||||||
"hello": {
|
|
||||||
"foo": null,
|
|
||||||
"pop": 123456
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
var jsonMergeExampleSrc = []byte(`
|
|
||||||
{
|
|
||||||
"hello": {
|
|
||||||
"foo": "foo str",
|
|
||||||
"pop": "pop str"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
func TestMergeConfig(t *testing.T) {
|
func TestMergeConfig(t *testing.T) {
|
||||||
v := New()
|
v := New()
|
||||||
v.SetConfigType("yml")
|
v.SetConfigType("yml")
|
||||||
|
@ -1953,10 +1787,6 @@ func TestMergeConfig(t *testing.T) {
|
||||||
t.Fatalf("uint pop != 37890, = %d", pop)
|
t.Fatalf("uint pop != 37890, = %d", pop)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pop := v.GetUint16("hello.pop"); pop != uint16(37890) {
|
|
||||||
t.Fatalf("uint pop != 37890, = %d", pop)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pop := v.GetUint32("hello.pop"); pop != 37890 {
|
if pop := v.GetUint32("hello.pop"); pop != 37890 {
|
||||||
t.Fatalf("uint32 pop != 37890, = %d", pop)
|
t.Fatalf("uint32 pop != 37890, = %d", pop)
|
||||||
}
|
}
|
||||||
|
@ -2006,26 +1836,6 @@ func TestMergeConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeConfigOverrideType(t *testing.T) {
|
|
||||||
v := New()
|
|
||||||
v.SetConfigType("json")
|
|
||||||
if err := v.ReadConfig(bytes.NewBuffer(jsonMergeExampleTgt)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.MergeConfig(bytes.NewBuffer(jsonMergeExampleSrc)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pop := v.GetString("hello.pop"); pop != "pop str" {
|
|
||||||
t.Fatalf("pop != \"pop str\", = %s", pop)
|
|
||||||
}
|
|
||||||
|
|
||||||
if foo := v.GetString("hello.foo"); foo != "foo str" {
|
|
||||||
t.Fatalf("foo != \"foo str\", = %s", foo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeConfigNoMerge(t *testing.T) {
|
func TestMergeConfigNoMerge(t *testing.T) {
|
||||||
v := New()
|
v := New()
|
||||||
v.SetConfigType("yml")
|
v.SetConfigType("yml")
|
||||||
|
@ -2332,7 +2142,7 @@ func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
|
||||||
watchDir, err := ioutil.TempDir("", "")
|
watchDir, err := ioutil.TempDir("", "")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
configFile := path.Join(watchDir, "config.yaml")
|
configFile := path.Join(watchDir, "config.yaml")
|
||||||
err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0o640)
|
err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
os.RemoveAll(watchDir)
|
os.RemoveAll(watchDir)
|
||||||
|
@ -2349,11 +2159,11 @@ func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func
|
||||||
watchDir, err := ioutil.TempDir("", "")
|
watchDir, err := ioutil.TempDir("", "")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
dataDir1 := path.Join(watchDir, "data1")
|
dataDir1 := path.Join(watchDir, "data1")
|
||||||
err = os.Mkdir(dataDir1, 0o777)
|
err = os.Mkdir(dataDir1, 0777)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
realConfigFile := path.Join(dataDir1, "config.yaml")
|
realConfigFile := path.Join(dataDir1, "config.yaml")
|
||||||
t.Logf("Real config file location: %s\n", realConfigFile)
|
t.Logf("Real config file location: %s\n", realConfigFile)
|
||||||
err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
|
err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
os.RemoveAll(watchDir)
|
os.RemoveAll(watchDir)
|
||||||
|
@ -2388,16 +2198,13 @@ func TestWatchFile(t *testing.T) {
|
||||||
t.Logf("test config file: %s\n", configFile)
|
t.Logf("test config file: %s\n", configFile)
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
var wgDoneOnce sync.Once // OnConfigChange is called twice on Windows
|
|
||||||
v.OnConfigChange(func(in fsnotify.Event) {
|
v.OnConfigChange(func(in fsnotify.Event) {
|
||||||
t.Logf("config file changed")
|
t.Logf("config file changed")
|
||||||
wgDoneOnce.Do(func() {
|
wg.Done()
|
||||||
wg.Done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
v.WatchConfig()
|
v.WatchConfig()
|
||||||
// when overwriting the file and waiting for the custom change notification handler to be triggered
|
// when overwriting the file and waiting for the custom change notification handler to be triggered
|
||||||
err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0o640)
|
err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
// then the config value should have changed
|
// then the config value should have changed
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
@ -2420,10 +2227,10 @@ func TestWatchFile(t *testing.T) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
// when link to another `config.yaml` file
|
// when link to another `config.yaml` file
|
||||||
dataDir2 := path.Join(watchDir, "data2")
|
dataDir2 := path.Join(watchDir, "data2")
|
||||||
err := os.Mkdir(dataDir2, 0o777)
|
err := os.Mkdir(dataDir2, 0777)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
configFile2 := path.Join(dataDir2, "config.yaml")
|
configFile2 := path.Join(dataDir2, "config.yaml")
|
||||||
err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
|
err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
// change the symlink using the `ln -sfn` command
|
// change the symlink using the `ln -sfn` command
|
||||||
err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
|
err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
|
||||||
|
@ -2452,25 +2259,25 @@ func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
|
||||||
assert.Equal(t, "cobra_flag", config.Foo.Bar)
|
assert.Equal(t, "cobra_flag", config.Foo.Bar)
|
||||||
}
|
}
|
||||||
|
|
||||||
// var yamlExampleWithDot = []byte(`Hacker: true
|
var yamlExampleWithDot = []byte(`Hacker: true
|
||||||
// name: steve
|
name: steve
|
||||||
// hobbies:
|
hobbies:
|
||||||
// - skateboarding
|
- skateboarding
|
||||||
// - snowboarding
|
- snowboarding
|
||||||
// - go
|
- go
|
||||||
// clothing:
|
clothing:
|
||||||
// jacket: leather
|
jacket: leather
|
||||||
// trousers: denim
|
trousers: denim
|
||||||
// pants:
|
pants:
|
||||||
// size: large
|
size: large
|
||||||
// age: 35
|
age: 35
|
||||||
// eyes : brown
|
eyes : brown
|
||||||
// beard: true
|
beard: true
|
||||||
// emails:
|
emails:
|
||||||
// steve@hacker.com:
|
steve@hacker.com:
|
||||||
// created: 01/02/03
|
created: 01/02/03
|
||||||
// active: true
|
active: true
|
||||||
// `)
|
`)
|
||||||
|
|
||||||
func TestKeyDelimiter(t *testing.T) {
|
func TestKeyDelimiter(t *testing.T) {
|
||||||
v := NewWithOptions(KeyDelimiter("::"))
|
v := NewWithOptions(KeyDelimiter("::"))
|
||||||
|
@ -2520,10 +2327,7 @@ func TestKeyDelimiter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var yamlDeepNestedSlices = []byte(`TV:
|
var yamlDeepNestedSlices = []byte(`TV:
|
||||||
- title: "The Expanse"
|
- title: "The expanse"
|
||||||
title_i18n:
|
|
||||||
USA: "The Expanse"
|
|
||||||
Japan: "エクスパンス -巨獣めざめる-"
|
|
||||||
seasons:
|
seasons:
|
||||||
- first_released: "December 14, 2015"
|
- first_released: "December 14, 2015"
|
||||||
episodes:
|
episodes:
|
||||||
|
@ -2553,15 +2357,11 @@ func TestSliceIndexAccess(t *testing.T) {
|
||||||
err := v.unmarshalReader(r, v.config)
|
err := v.unmarshalReader(r, v.config)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "The Expanse", v.GetString("tv.0.title"))
|
assert.Equal(t, "The expanse", v.GetString("tv.0.title"))
|
||||||
assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
|
assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
|
||||||
assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
|
assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
|
||||||
assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
|
assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
|
||||||
|
|
||||||
// Test nested keys with capital letters
|
|
||||||
assert.Equal(t, "The Expanse", v.GetString("tv.0.title_i18n.USA"))
|
|
||||||
assert.Equal(t, "エクスパンス -巨獣めざめる-", v.GetString("tv.0.title_i18n.Japan"))
|
|
||||||
|
|
||||||
// Test for index out of bounds
|
// Test for index out of bounds
|
||||||
assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
|
assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
|
||||||
|
|
||||||
|
@ -2605,10 +2405,3 @@ func BenchmarkGetBoolFromMap(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip some tests on Windows that kept failing when Windows was added to the CI as a target.
|
|
||||||
func skipWindows(t *testing.T) {
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
t.Skip("Skip test on Windows")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
//go:build viper_yaml2
|
|
||||||
// +build viper_yaml2
|
|
||||||
|
|
||||||
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
|
|
||||||
`)
|
|
|
@ -1,56 +0,0 @@
|
||||||
//go:build !viper_yaml2
|
|
||||||
// +build !viper_yaml2
|
|
||||||
|
|
||||||
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
|
|
||||||
`)
|
|
3
watch.go
3
watch.go
|
@ -1,5 +1,4 @@
|
||||||
//go:build darwin || dragonfly || freebsd || openbsd || linux || netbsd || solaris || windows
|
// +build !js
|
||||||
// +build darwin dragonfly freebsd openbsd linux netbsd solaris windows
|
|
||||||
|
|
||||||
package viper
|
package viper
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
|
// +build js,wasm
|
||||||
// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
|
|
||||||
|
|
||||||
package viper
|
package viper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newWatcher() (*watcher, error) {
|
|
||||||
return &watcher{}, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
|
|
||||||
}
|
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
Events chan fsnotify.Event
|
Events chan fsnotify.Event
|
||||||
Errors chan error
|
Errors chan error
|
||||||
|
@ -30,3 +24,7 @@ func (*watcher) Add(name string) error {
|
||||||
func (*watcher) Remove(name string) error {
|
func (*watcher) Remove(name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newWatcher() (*watcher, error) {
|
||||||
|
return &watcher{}, errors.New("fsnotify is not supported on WASM")
|
||||||
|
}
|
Loading…
Reference in New Issue