mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Merge branch 'main' into ibm-enable-retries
This commit is contained in:
commit
082cee230f
59 changed files with 2477 additions and 419 deletions
5
.github/PAUL.yaml
vendored
5
.github/PAUL.yaml
vendored
|
@ -47,6 +47,11 @@ pull_requests:
|
||||||
If this is your first time contributing, please make
|
If this is your first time contributing, please make
|
||||||
sure to read the [Developer](https://www.external-secrets.io/contributing-devguide/) and [Contributing Process](https://www.external-secrets.io/contributing-process/) guides.
|
sure to read the [Developer](https://www.external-secrets.io/contributing-devguide/) and [Contributing Process](https://www.external-secrets.io/contributing-process/) guides.
|
||||||
Please also mind and follow our [Code of Conduct](https://www.external-secrets.io/contributing-coc/).
|
Please also mind and follow our [Code of Conduct](https://www.external-secrets.io/contributing-coc/).
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
- `make fmt`: Formats the code
|
||||||
|
- `make check-diff`: Ensures the branch is clean
|
||||||
|
- `make reviewable`: Ensures a PR is ready for review
|
||||||
# Enables the /cat command
|
# Enables the /cat command
|
||||||
cats_enabled: true
|
cats_enabled: true
|
||||||
# enables the /dog command
|
# enables the /dog command
|
||||||
|
|
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
|
@ -11,7 +11,7 @@ on:
|
||||||
env:
|
env:
|
||||||
# Common versions
|
# Common versions
|
||||||
GO_VERSION: '1.16'
|
GO_VERSION: '1.16'
|
||||||
GOLANGCI_VERSION: 'v1.33'
|
GOLANGCI_VERSION: 'v1.42.1'
|
||||||
# list of available versions: https://storage.googleapis.com/kubebuilder-tools
|
# list of available versions: https://storage.googleapis.com/kubebuilder-tools
|
||||||
# TODO: 1.21.2 does not shut down properly with controller-runtime 0.9.2
|
# TODO: 1.21.2 does not shut down properly with controller-runtime 0.9.2
|
||||||
KUBEBUILDER_TOOLS_VERSION: '1.20.2'
|
KUBEBUILDER_TOOLS_VERSION: '1.20.2'
|
||||||
|
@ -61,14 +61,14 @@ jobs:
|
||||||
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
||||||
|
|
||||||
- name: Cache the Go Build Cache
|
- name: Cache the Go Build Cache
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.build-cache }}
|
path: ${{ steps.go.outputs.build-cache }}
|
||||||
key: ${{ runner.os }}-build-lint-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-build-lint-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-build-lint-
|
restore-keys: ${{ runner.os }}-build-lint-
|
||||||
|
|
||||||
- name: Cache Go Dependencies
|
- name: Cache Go Dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.mod-cache }}
|
path: ${{ steps.go.outputs.mod-cache }}
|
||||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||||
|
@ -107,21 +107,25 @@ jobs:
|
||||||
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
||||||
|
|
||||||
- name: Cache the Go Build Cache
|
- name: Cache the Go Build Cache
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.build-cache }}
|
path: ${{ steps.go.outputs.build-cache }}
|
||||||
key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-build-check-diff-
|
restore-keys: ${{ runner.os }}-build-check-diff-
|
||||||
|
|
||||||
- name: Cache Go Dependencies
|
- name: Cache Go Dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.mod-cache }}
|
path: ${{ steps.go.outputs.mod-cache }}
|
||||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-pkg-
|
restore-keys: ${{ runner.os }}-pkg-
|
||||||
|
|
||||||
|
# Check DIff also runs Reviewable which needs golangci-lint installed
|
||||||
- name: Check Diff
|
- name: Check Diff
|
||||||
run: make check-diff
|
run: |
|
||||||
|
wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.42.1
|
||||||
|
export PATH=$PATH:./bin
|
||||||
|
make check-diff
|
||||||
|
|
||||||
unit-tests:
|
unit-tests:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
|
@ -147,14 +151,14 @@ jobs:
|
||||||
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
||||||
|
|
||||||
- name: Cache the Go Build Cache
|
- name: Cache the Go Build Cache
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.build-cache }}
|
path: ${{ steps.go.outputs.build-cache }}
|
||||||
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-build-unit-tests-
|
restore-keys: ${{ runner.os }}-build-unit-tests-
|
||||||
|
|
||||||
- name: Cache Go Dependencies
|
- name: Cache Go Dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.mod-cache }}
|
path: ${{ steps.go.outputs.mod-cache }}
|
||||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||||
|
@ -167,7 +171,7 @@ jobs:
|
||||||
sudo tar -C /usr/local/kubebuilder --strip-components=1 -zvxf envtest-bins.tar.gz
|
sudo tar -C /usr/local/kubebuilder --strip-components=1 -zvxf envtest-bins.tar.gz
|
||||||
|
|
||||||
- name: Cache envtest binaries
|
- name: Cache envtest binaries
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: /usr/local/kubebuilder
|
path: /usr/local/kubebuilder
|
||||||
key: ${{ runner.os }}-kubebuilder-${{env.KUBEBUILDER_TOOLS_VERSION}}
|
key: ${{ runner.os }}-kubebuilder-${{env.KUBEBUILDER_TOOLS_VERSION}}
|
||||||
|
@ -214,14 +218,14 @@ jobs:
|
||||||
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
||||||
|
|
||||||
- name: Cache the Go Build Cache
|
- name: Cache the Go Build Cache
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.build-cache }}
|
path: ${{ steps.go.outputs.build-cache }}
|
||||||
key: ${{ runner.os }}-build-publish-artifacts-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-build-publish-artifacts-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-build-publish-artifacts-
|
restore-keys: ${{ runner.os }}-build-publish-artifacts-
|
||||||
|
|
||||||
- name: Cache Go Dependencies
|
- name: Cache Go Dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.mod-cache }}
|
path: ${{ steps.go.outputs.mod-cache }}
|
||||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||||
|
|
18
.github/workflows/e2e.yml
vendored
18
.github/workflows/e2e.yml
vendored
|
@ -50,14 +50,14 @@ jobs:
|
||||||
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
||||||
|
|
||||||
- name: Cache the Go Build Cache
|
- name: Cache the Go Build Cache
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.build-cache }}
|
path: ${{ steps.go.outputs.build-cache }}
|
||||||
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-build-unit-tests-
|
restore-keys: ${{ runner.os }}-build-unit-tests-
|
||||||
|
|
||||||
- name: Cache Go Dependencies
|
- name: Cache Go Dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.mod-cache }}
|
path: ${{ steps.go.outputs.mod-cache }}
|
||||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||||
|
@ -113,14 +113,14 @@ jobs:
|
||||||
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
echo "::set-output name=mod-cache::$(go env GOMODCACHE)"
|
||||||
|
|
||||||
- name: Cache the Go Build Cache
|
- name: Cache the Go Build Cache
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.build-cache }}
|
path: ${{ steps.go.outputs.build-cache }}
|
||||||
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }}
|
||||||
restore-keys: ${{ runner.os }}-build-unit-tests-
|
restore-keys: ${{ runner.os }}-build-unit-tests-
|
||||||
|
|
||||||
- name: Cache Go Dependencies
|
- name: Cache Go Dependencies
|
||||||
uses: actions/cache@v2.1.6
|
uses: actions/cache@v2.1.7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.go.outputs.mod-cache }}
|
path: ${{ steps.go.outputs.mod-cache }}
|
||||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||||
|
@ -149,7 +149,7 @@ jobs:
|
||||||
make test.e2e
|
make test.e2e
|
||||||
|
|
||||||
# Update check run called "integration-fork"
|
# Update check run called "integration-fork"
|
||||||
- uses: actions/github-script@v5
|
- uses: actions/github-script@v1
|
||||||
id: update-check-run
|
id: update-check-run
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
env:
|
env:
|
||||||
|
@ -165,19 +165,19 @@ jobs:
|
||||||
pull_number: process.env.number
|
pull_number: process.env.number
|
||||||
});
|
});
|
||||||
const ref = pull.head.sha;
|
const ref = pull.head.sha;
|
||||||
|
console.log("\n\nPR sha: " + ref)
|
||||||
const { data: checks } = await github.checks.listForRef({
|
const { data: checks } = await github.checks.listForRef({
|
||||||
...context.repo,
|
...context.repo,
|
||||||
ref
|
ref
|
||||||
});
|
});
|
||||||
|
console.log("\n\nPR CHECKS: " + checks)
|
||||||
const check = checks.check_runs.filter(c => c.name === process.env.job);
|
const check = checks.check_runs.filter(c => c.name === process.env.job);
|
||||||
|
console.log("\n\nPR Filtered CHECK: " + check)
|
||||||
|
console.log(check)
|
||||||
const { data: result } = await github.checks.update({
|
const { data: result } = await github.checks.update({
|
||||||
...context.repo,
|
...context.repo,
|
||||||
check_run_id: check[0].id,
|
check_run_id: check[0].id,
|
||||||
status: 'completed',
|
status: 'completed',
|
||||||
conclusion: process.env.conclusion
|
conclusion: process.env.conclusion
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -32,14 +32,10 @@ linters-settings:
|
||||||
min-complexity: 16
|
min-complexity: 16
|
||||||
goheader:
|
goheader:
|
||||||
template-path: ./hack/boilerplate.go.txt
|
template-path: ./hack/boilerplate.go.txt
|
||||||
golint:
|
|
||||||
min-confidence: 0
|
|
||||||
govet:
|
govet:
|
||||||
check-shadowing: false
|
check-shadowing: false
|
||||||
lll:
|
lll:
|
||||||
line-length: 300
|
line-length: 300
|
||||||
maligned:
|
|
||||||
suggest-new: true
|
|
||||||
misspell:
|
misspell:
|
||||||
locale: US
|
locale: US
|
||||||
|
|
||||||
|
@ -62,7 +58,6 @@ linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
- godot
|
- godot
|
||||||
- gofmt
|
- gofmt
|
||||||
- golint
|
|
||||||
- goprintffuncname
|
- goprintffuncname
|
||||||
- gosec
|
- gosec
|
||||||
- gosimple
|
- gosimple
|
||||||
|
@ -70,13 +65,12 @@ linters:
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- interfacer
|
- interfacer
|
||||||
- lll
|
- lll
|
||||||
- maligned
|
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- prealloc
|
- prealloc
|
||||||
|
- revive
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- scopelint
|
|
||||||
- sqlclosecheck
|
- sqlclosecheck
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- structcheck
|
- structcheck
|
||||||
|
@ -102,7 +96,6 @@ issues:
|
||||||
- errcheck
|
- errcheck
|
||||||
- dupl
|
- dupl
|
||||||
- gosec
|
- gosec
|
||||||
- scopelint
|
|
||||||
- unparam
|
- unparam
|
||||||
- lll
|
- lll
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:3.14.2
|
FROM gcr.io/distroless/static
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
COPY bin/external-secrets-${TARGETOS}-${TARGETARCH} /bin/external-secrets
|
COPY bin/external-secrets-${TARGETOS}-${TARGETARCH} /bin/external-secrets
|
||||||
|
|
47
Makefile
47
Makefile
|
@ -65,12 +65,10 @@ FAIL = (echo ${TIME} ${RED}[FAIL]${CNone} && false)
|
||||||
# ====================================================================================
|
# ====================================================================================
|
||||||
# Conformance
|
# Conformance
|
||||||
|
|
||||||
# Ensure a PR is ready for review.
|
reviewable: generate helm.generate lint ## Ensure a PR is ready for review.
|
||||||
reviewable: generate helm.generate
|
|
||||||
@go mod tidy
|
@go mod tidy
|
||||||
|
|
||||||
# Ensure branch is clean.
|
check-diff: reviewable ## Ensure branch is clean.
|
||||||
check-diff: reviewable
|
|
||||||
@$(INFO) checking that branch is clean
|
@$(INFO) checking that branch is clean
|
||||||
@test -z "$$(git status --porcelain)" || (echo "$$(git status --porcelain)" && $(FAIL))
|
@test -z "$$(git status --porcelain)" || (echo "$$(git status --porcelain)" && $(FAIL))
|
||||||
@$(OK) branch is clean
|
@$(OK) branch is clean
|
||||||
|
@ -91,7 +89,7 @@ test.e2e: generate ## Run e2e tests
|
||||||
@$(OK) go test unit-tests
|
@$(OK) go test unit-tests
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: $(addprefix build-,$(ARCH))
|
build: $(addprefix build-,$(ARCH)) ## Build binary
|
||||||
|
|
||||||
.PHONY: build-%
|
.PHONY: build-%
|
||||||
build-%: generate ## Build binary for the specified arch
|
build-%: generate ## Build binary for the specified arch
|
||||||
|
@ -100,27 +98,26 @@ build-%: generate ## Build binary for the specified arch
|
||||||
go build -o '$(OUTPUT_DIR)/external-secrets-linux-$*' main.go
|
go build -o '$(OUTPUT_DIR)/external-secrets-linux-$*' main.go
|
||||||
@$(OK) go build $*
|
@$(OK) go build $*
|
||||||
|
|
||||||
# Check install of golanci-lint
|
lint.check: ## Check install of golanci-lint
|
||||||
lint.check:
|
|
||||||
@if ! golangci-lint --version > /dev/null 2>&1; then \
|
@if ! golangci-lint --version > /dev/null 2>&1; then \
|
||||||
echo -e "\033[0;33mgolangci-lint is not installed: run \`\033[0;32mmake lint.install\033[0m\033[0;33m\` or install it from https://golangci-lint.run\033[0m"; \
|
echo -e "\033[0;33mgolangci-lint is not installed: run \`\033[0;32mmake lint.install\033[0m\033[0;33m\` or install it from https://golangci-lint.run\033[0m"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# installs golangci-lint to the go bin dir
|
lint.install: ## Install golangci-lint to the go bin dir
|
||||||
lint.install:
|
|
||||||
@if ! golangci-lint --version > /dev/null 2>&1; then \
|
@if ! golangci-lint --version > /dev/null 2>&1; then \
|
||||||
echo "Installing golangci-lint"; \
|
echo "Installing golangci-lint"; \
|
||||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOBIN) v1.33.0; \
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.42.1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
lint: lint.check ## run golangci-lint
|
lint: lint.check ## Run golangci-lint
|
||||||
@if ! golangci-lint run; then \
|
@if ! golangci-lint run; then \
|
||||||
echo -e "\033[0;33mgolangci-lint failed: some checks can be fixed with \`\033[0;32mmake fmt\033[0m\033[0;33m\`\033[0m"; \
|
echo -e "\033[0;33mgolangci-lint failed: some checks can be fixed with \`\033[0;32mmake fmt\033[0m\033[0;33m\`\033[0m"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
@$(OK) Finished linting
|
||||||
|
|
||||||
fmt: lint.check ## ensure consistent code style
|
fmt: lint.check ## Ensure consistent code style
|
||||||
@go mod tidy
|
@go mod tidy
|
||||||
@go fmt ./...
|
@go fmt ./...
|
||||||
@golangci-lint run --fix > /dev/null 2>&1 || true
|
@golangci-lint run --fix > /dev/null 2>&1 || true
|
||||||
|
@ -142,20 +139,17 @@ generate: ## Generate code and crds
|
||||||
|
|
||||||
# This is for running out-of-cluster locally, and is for convenience.
|
# This is for running out-of-cluster locally, and is for convenience.
|
||||||
# For more control, try running the binary directly with different arguments.
|
# For more control, try running the binary directly with different arguments.
|
||||||
run: generate
|
run: generate ## Run app locally (without a k8s cluster)
|
||||||
go run ./main.go
|
go run ./main.go
|
||||||
|
|
||||||
# Generate manifests from helm chart
|
manifests: helm.generate ## Generate manifests from helm chart
|
||||||
manifests: helm.generate
|
|
||||||
mkdir -p $(OUTPUT_DIR)/deploy/manifests
|
mkdir -p $(OUTPUT_DIR)/deploy/manifests
|
||||||
helm template external-secrets $(HELM_DIR) -f deploy/manifests/helm-values.yaml > $(OUTPUT_DIR)/deploy/manifests/external-secrets.yaml
|
helm template external-secrets $(HELM_DIR) -f deploy/manifests/helm-values.yaml > $(OUTPUT_DIR)/deploy/manifests/external-secrets.yaml
|
||||||
|
|
||||||
# Install CRDs into a cluster. This is for convenience.
|
crds.install: generate ## Install CRDs into a cluster. This is for convenience
|
||||||
crds.install: generate
|
|
||||||
kubectl apply -f $(CRD_DIR)
|
kubectl apply -f $(CRD_DIR)
|
||||||
|
|
||||||
# Uninstall CRDs from a cluster. This is for convenience.
|
crds.uninstall: ## Uninstall CRDs from a cluster. This is for convenience
|
||||||
crds.uninstall:
|
|
||||||
kubectl delete -f $(CRD_DIR)
|
kubectl delete -f $(CRD_DIR)
|
||||||
|
|
||||||
# ====================================================================================
|
# ====================================================================================
|
||||||
|
@ -173,8 +167,7 @@ helm.build: helm.generate ## Build helm chart
|
||||||
@mv $(OUTPUT_DIR)/chart/external-secrets-$(HELM_VERSION).tgz $(OUTPUT_DIR)/chart/external-secrets.tgz
|
@mv $(OUTPUT_DIR)/chart/external-secrets-$(HELM_VERSION).tgz $(OUTPUT_DIR)/chart/external-secrets.tgz
|
||||||
@$(OK) helm package
|
@$(OK) helm package
|
||||||
|
|
||||||
# Copy crds to helm chart directory
|
helm.generate: helm.docs ## Copy crds to helm chart directory
|
||||||
helm.generate: helm.docs
|
|
||||||
@cp $(CRD_DIR)/*.yaml $(HELM_DIR)/templates/crds/
|
@cp $(CRD_DIR)/*.yaml $(HELM_DIR)/templates/crds/
|
||||||
# Add helm if statement for controlling the install of CRDs
|
# Add helm if statement for controlling the install of CRDs
|
||||||
@for i in $(HELM_DIR)/templates/crds/*.yaml; do \
|
@for i in $(HELM_DIR)/templates/crds/*.yaml; do \
|
||||||
|
@ -189,24 +182,24 @@ helm.generate: helm.docs
|
||||||
# ====================================================================================
|
# ====================================================================================
|
||||||
# Documentation
|
# Documentation
|
||||||
.PHONY: docs
|
.PHONY: docs
|
||||||
docs: generate
|
docs: generate ## Generate docs
|
||||||
$(MAKE) -C ./hack/api-docs build
|
$(MAKE) -C ./hack/api-docs build
|
||||||
|
|
||||||
.PHONY: serve-docs
|
.PHONY: serve-docs
|
||||||
serve-docs:
|
serve-docs: ## Serve docs
|
||||||
$(MAKE) -C ./hack/api-docs serve
|
$(MAKE) -C ./hack/api-docs serve
|
||||||
|
|
||||||
# ====================================================================================
|
# ====================================================================================
|
||||||
# Build Artifacts
|
# Build Artifacts
|
||||||
|
|
||||||
build.all: docker.build helm.build
|
build.all: docker.build helm.build ## Build all artifacts (docker image, helm chart)
|
||||||
|
|
||||||
docker.build: $(addprefix build-,$(ARCH)) ## Build the docker image
|
docker.build: $(addprefix build-,$(ARCH)) ## Build the docker image
|
||||||
@$(INFO) docker build
|
@$(INFO) docker build
|
||||||
@docker build . $(BUILD_ARGS) -t $(IMAGE_REGISTRY):$(VERSION)
|
@docker build . $(BUILD_ARGS) -t $(IMAGE_REGISTRY):$(VERSION)
|
||||||
@$(OK) docker build
|
@$(OK) docker build
|
||||||
|
|
||||||
docker.push:
|
docker.push: ## Push the docker image to the registry
|
||||||
@$(INFO) docker push
|
@$(INFO) docker push
|
||||||
@docker push $(IMAGE_REGISTRY):$(VERSION)
|
@docker push $(IMAGE_REGISTRY):$(VERSION)
|
||||||
@$(OK) docker push
|
@$(OK) docker push
|
||||||
|
@ -216,7 +209,7 @@ docker.push:
|
||||||
RELEASE_TAG ?= main
|
RELEASE_TAG ?= main
|
||||||
SOURCE_TAG ?= $(VERSION)
|
SOURCE_TAG ?= $(VERSION)
|
||||||
|
|
||||||
docker.promote:
|
docker.promote: ## Promote the docker image to the registry
|
||||||
@$(INFO) promoting $(SOURCE_TAG) to $(RELEASE_TAG)
|
@$(INFO) promoting $(SOURCE_TAG) to $(RELEASE_TAG)
|
||||||
docker manifest inspect $(IMAGE_REGISTRY):$(SOURCE_TAG) > .tagmanifest
|
docker manifest inspect $(IMAGE_REGISTRY):$(SOURCE_TAG) > .tagmanifest
|
||||||
for digest in $$(jq -r '.manifests[].digest' < .tagmanifest); do \
|
for digest in $$(jq -r '.manifests[].digest' < .tagmanifest); do \
|
||||||
|
@ -231,5 +224,5 @@ docker.promote:
|
||||||
# Help
|
# Help
|
||||||
|
|
||||||
# only comments after make target name are shown as help text
|
# only comments after make target name are shown as help text
|
||||||
help: ## displays this help message
|
help: ## Displays this help message
|
||||||
@echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s : | sort)"
|
@echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s : | sort)"
|
||||||
|
|
10
README.md
10
README.md
|
@ -13,6 +13,7 @@ Multiple people and organizations are joining efforts to create a single Externa
|
||||||
|
|
||||||
- [AWS Secrets Manager](https://external-secrets.io/provider-aws-secrets-manager/)
|
- [AWS Secrets Manager](https://external-secrets.io/provider-aws-secrets-manager/)
|
||||||
- [AWS Parameter Store](https://external-secrets.io/provider-aws-parameter-store/)
|
- [AWS Parameter Store](https://external-secrets.io/provider-aws-parameter-store/)
|
||||||
|
- [Akeyless](https://www.akeyless.io/)
|
||||||
- [Hashicorp Vault](https://www.vaultproject.io/)
|
- [Hashicorp Vault](https://www.vaultproject.io/)
|
||||||
- [Google Cloud Secrets Manager](https://external-secrets.io/provider-google-secrets-manager/)
|
- [Google Cloud Secrets Manager](https://external-secrets.io/provider-google-secrets-manager/)
|
||||||
- [Azure Key Vault](https://external-secrets.io/provider-azure-key-vault/)
|
- [Azure Key Vault](https://external-secrets.io/provider-azure-key-vault/)
|
||||||
|
@ -28,10 +29,10 @@ Multiple people and organizations are joining efforts to create a single Externa
|
||||||
|
|
||||||
| Provider | Stability | Contact |
|
| Provider | Stability | Contact |
|
||||||
| ------------------------------------------------------------------------ | :-------: | ---------------------------------------------: |
|
| ------------------------------------------------------------------------ | :-------: | ---------------------------------------------: |
|
||||||
| [AWS SM](https://external-secrets.io/provider-aws-secrets-manager/) | alpha | [ESO Org](https://github.com/external-secrets) |
|
| [AWS SM](https://external-secrets.io/provider-aws-secrets-manager/) | beta | [ESO Org](https://github.com/external-secrets) |
|
||||||
| [AWS PS](https://external-secrets.io/provider-aws-parameter-store/) | alpha | [ESO Org](https://github.com/external-secrets) |
|
| [AWS PS](https://external-secrets.io/provider-aws-parameter-store/) | beta | [ESO Org](https://github.com/external-secrets) |
|
||||||
| [Hashicorp Vault](https://external-secrets.io/provider-hashicorp-vault/) | alpha | [ESO Org](https://github.com/external-secrets) |
|
| [Hashicorp Vault](https://external-secrets.io/provider-hashicorp-vault/) | stable | [ESO Org](https://github.com/external-secrets) |
|
||||||
| [GCP SM](https://external-secrets.io/provider-google-secrets-manager/) | alpha | [ESO Org](https://github.com/external-secrets) |
|
| [GCP SM](https://external-secrets.io/provider-google-secrets-manager/) | beta | [ESO Org](https://github.com/external-secrets) |
|
||||||
|
|
||||||
### Community maintained:
|
### Community maintained:
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ Multiple people and organizations are joining efforts to create a single Externa
|
||||||
| [Gitlab Project Variables](https://external-secrets.io/provider-gitlab-project-variables/) | alpha | [@Jabray5](https://github.com/Jabray5) |
|
| [Gitlab Project Variables](https://external-secrets.io/provider-gitlab-project-variables/) | alpha | [@Jabray5](https://github.com/Jabray5) |
|
||||||
| Alibaba Cloud KMS | alpha | [@ElsaChelala](https://github.com/ElsaChelala) |
|
| Alibaba Cloud KMS | alpha | [@ElsaChelala](https://github.com/ElsaChelala) |
|
||||||
| [Oracle Vault]( https://external-secrets.io/provider-oracle-vault) | alpha | [@KianTigger](https://github.com/KianTigger) |
|
| [Oracle Vault]( https://external-secrets.io/provider-oracle-vault) | alpha | [@KianTigger](https://github.com/KianTigger) |
|
||||||
|
| [Akeyless]( https://external-secrets.io/provider-akeyless) | alpha | [@renanaAkeyless](https://github.com/renanaAkeyless) |
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
42
apis/externalsecrets/v1alpha1/secretstore_akeyless_types.go
Normal file
42
apis/externalsecrets/v1alpha1/secretstore_akeyless_types.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AkeylessProvider Configures an store to sync secrets using Akeyless KV.
|
||||||
|
type AkeylessProvider struct {
|
||||||
|
|
||||||
|
// Akeyless GW API Url from which the secrets to be fetched from.
|
||||||
|
AkeylessGWApiURL *string `json:"akeylessGWApiURL"`
|
||||||
|
|
||||||
|
// Auth configures how the operator authenticates with Akeyless.
|
||||||
|
Auth *AkeylessAuth `json:"authSecretRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AkeylessAuth struct {
|
||||||
|
SecretRef AkeylessAuthSecretRef `json:"secretRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AkeylessAuthSecretRef
|
||||||
|
//AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.
|
||||||
|
type AkeylessAuthSecretRef struct {
|
||||||
|
// The SecretAccessID is used for authentication
|
||||||
|
AccessID esmeta.SecretKeySelector `json:"accessID,omitempty"`
|
||||||
|
AccessType esmeta.SecretKeySelector `json:"accessType,omitempty"`
|
||||||
|
AccessTypeParam esmeta.SecretKeySelector `json:"accessTypeParam,omitempty"`
|
||||||
|
}
|
|
@ -16,14 +16,41 @@ package v1alpha1
|
||||||
|
|
||||||
import smmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
|
import smmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
|
||||||
|
|
||||||
|
// AuthType describes how to authenticate to the Azure Keyvault
|
||||||
|
// Only one of the following auth types may be specified.
|
||||||
|
// If none of the following auth type is specified, the default one
|
||||||
|
// is ServicePrincipal.
|
||||||
|
// +kubebuilder:validation:Enum=ServicePrincipal;ManagedIdentity
|
||||||
|
type AuthType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Using service principal to authenticate, which needs a tenantId, a clientId and a clientSecret.
|
||||||
|
ServicePrincipal AuthType = "ServicePrincipal"
|
||||||
|
|
||||||
|
// Using Managed Identity to authenticate. Used with aad-pod-identity instelled in the clister.
|
||||||
|
ManagedIdentity AuthType = "ManagedIdentity"
|
||||||
|
)
|
||||||
|
|
||||||
// Configures an store to sync secrets using Azure KV.
|
// Configures an store to sync secrets using Azure KV.
|
||||||
type AzureKVProvider struct {
|
type AzureKVProvider struct {
|
||||||
|
// Auth type defines how to authenticate to the keyvault service.
|
||||||
|
// Valid values are:
|
||||||
|
// - "ServicePrincipal" (default): Using a service principal (tenantId, clientId, clientSecret)
|
||||||
|
// - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)
|
||||||
|
// +optional
|
||||||
|
// +kubebuilder:default=ServicePrincipal
|
||||||
|
AuthType *AuthType `json:"authType,omitempty"`
|
||||||
// Vault Url from which the secrets to be fetched from.
|
// Vault Url from which the secrets to be fetched from.
|
||||||
VaultURL *string `json:"vaultUrl"`
|
VaultURL *string `json:"vaultUrl"`
|
||||||
// TenantID configures the Azure Tenant to send requests to.
|
// TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
|
||||||
TenantID *string `json:"tenantId"`
|
// +optional
|
||||||
// Auth configures how the operator authenticates with Azure.
|
TenantID *string `json:"tenantId,omitempty"`
|
||||||
AuthSecretRef *AzureKVAuth `json:"authSecretRef"`
|
// Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type.
|
||||||
|
// +optional
|
||||||
|
AuthSecretRef *AzureKVAuth `json:"authSecretRef,omitempty"`
|
||||||
|
// If multiple Managed Identity is assigned to the pod, you can select the one to be used
|
||||||
|
// +optional
|
||||||
|
IdentityID *string `json:"identityId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration used to authenticate with Azure.
|
// Configuration used to authenticate with Azure.
|
||||||
|
|
|
@ -46,6 +46,10 @@ type SecretStoreProvider struct {
|
||||||
// +optional
|
// +optional
|
||||||
AzureKV *AzureKVProvider `json:"azurekv,omitempty"`
|
AzureKV *AzureKVProvider `json:"azurekv,omitempty"`
|
||||||
|
|
||||||
|
// Akeyless configures this store to sync secrets using Akeyless Vault provider
|
||||||
|
// +optional
|
||||||
|
Akeyless *AkeylessProvider `json:"akeyless,omitempty"`
|
||||||
|
|
||||||
// Vault configures this store to sync secrets using Hashi provider
|
// Vault configures this store to sync secrets using Hashi provider
|
||||||
// +optional
|
// +optional
|
||||||
Vault *VaultProvider `json:"vault,omitempty"`
|
Vault *VaultProvider `json:"vault,omitempty"`
|
||||||
|
|
|
@ -102,6 +102,65 @@ func (in *AWSProvider) DeepCopy() *AWSProvider {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *AkeylessAuth) DeepCopyInto(out *AkeylessAuth) {
|
||||||
|
*out = *in
|
||||||
|
in.SecretRef.DeepCopyInto(&out.SecretRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AkeylessAuth.
|
||||||
|
func (in *AkeylessAuth) DeepCopy() *AkeylessAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(AkeylessAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *AkeylessAuthSecretRef) DeepCopyInto(out *AkeylessAuthSecretRef) {
|
||||||
|
*out = *in
|
||||||
|
in.AccessID.DeepCopyInto(&out.AccessID)
|
||||||
|
in.AccessType.DeepCopyInto(&out.AccessType)
|
||||||
|
in.AccessTypeParam.DeepCopyInto(&out.AccessTypeParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AkeylessAuthSecretRef.
|
||||||
|
func (in *AkeylessAuthSecretRef) DeepCopy() *AkeylessAuthSecretRef {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(AkeylessAuthSecretRef)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *AkeylessProvider) DeepCopyInto(out *AkeylessProvider) {
|
||||||
|
*out = *in
|
||||||
|
if in.AkeylessGWApiURL != nil {
|
||||||
|
in, out := &in.AkeylessGWApiURL, &out.AkeylessGWApiURL
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.Auth != nil {
|
||||||
|
in, out := &in.Auth, &out.Auth
|
||||||
|
*out = new(AkeylessAuth)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AkeylessProvider.
|
||||||
|
func (in *AkeylessProvider) DeepCopy() *AkeylessProvider {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(AkeylessProvider)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *AlibabaAuth) DeepCopyInto(out *AlibabaAuth) {
|
func (in *AlibabaAuth) DeepCopyInto(out *AlibabaAuth) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -183,6 +242,11 @@ func (in *AzureKVAuth) DeepCopy() *AzureKVAuth {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *AzureKVProvider) DeepCopyInto(out *AzureKVProvider) {
|
func (in *AzureKVProvider) DeepCopyInto(out *AzureKVProvider) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.AuthType != nil {
|
||||||
|
in, out := &in.AuthType, &out.AuthType
|
||||||
|
*out = new(AuthType)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
if in.VaultURL != nil {
|
if in.VaultURL != nil {
|
||||||
in, out := &in.VaultURL, &out.VaultURL
|
in, out := &in.VaultURL, &out.VaultURL
|
||||||
*out = new(string)
|
*out = new(string)
|
||||||
|
@ -198,6 +262,11 @@ func (in *AzureKVProvider) DeepCopyInto(out *AzureKVProvider) {
|
||||||
*out = new(AzureKVAuth)
|
*out = new(AzureKVAuth)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.IdentityID != nil {
|
||||||
|
in, out := &in.IdentityID, &out.IdentityID
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKVProvider.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKVProvider.
|
||||||
|
@ -794,6 +863,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
|
||||||
*out = new(AzureKVProvider)
|
*out = new(AzureKVProvider)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.Akeyless != nil {
|
||||||
|
in, out := &in.Akeyless, &out.Akeyless
|
||||||
|
*out = new(AkeylessProvider)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
if in.Vault != nil {
|
if in.Vault != nil {
|
||||||
in, out := &in.Vault, &out.Vault
|
in, out := &in.Vault, &out.Vault
|
||||||
*out = new(VaultProvider)
|
*out = new(VaultProvider)
|
||||||
|
|
|
@ -2,8 +2,8 @@ apiVersion: v2
|
||||||
name: external-secrets
|
name: external-secrets
|
||||||
description: External secret management for Kubernetes
|
description: External secret management for Kubernetes
|
||||||
type: application
|
type: application
|
||||||
version: "0.3.6"
|
version: "0.3.9"
|
||||||
appVersion: "v0.3.6"
|
appVersion: "v0.3.9"
|
||||||
kubeVersion: ">= 1.11.0-0"
|
kubeVersion: ">= 1.11.0-0"
|
||||||
keywords:
|
keywords:
|
||||||
- kubernetes-external-secrets
|
- kubernetes-external-secrets
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
[//]: # (README.md generated by gotmpl. DO NOT EDIT.)
|
[//]: # (README.md generated by gotmpl. DO NOT EDIT.)
|
||||||
|
|
||||||
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.3.6](https://img.shields.io/badge/Version-0.3.6-informational?style=flat-square)
|
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.3.9](https://img.shields.io/badge/Version-0.3.9-informational?style=flat-square)
|
||||||
|
|
||||||
External secret management for Kubernetes
|
External secret management for Kubernetes
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,94 @@ spec:
|
||||||
maxProperties: 1
|
maxProperties: 1
|
||||||
minProperties: 1
|
minProperties: 1
|
||||||
properties:
|
properties:
|
||||||
|
akeyless:
|
||||||
|
description: Akeyless configures this store to sync secrets using
|
||||||
|
Akeyless Vault provider
|
||||||
|
properties:
|
||||||
|
akeylessGWApiURL:
|
||||||
|
description: Akeyless GW API Url from which the secrets to
|
||||||
|
be fetched from.
|
||||||
|
type: string
|
||||||
|
authSecretRef:
|
||||||
|
description: Auth configures how the operator authenticates
|
||||||
|
with Akeyless.
|
||||||
|
properties:
|
||||||
|
secretRef:
|
||||||
|
description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM:
|
||||||
|
AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.'
|
||||||
|
properties:
|
||||||
|
accessID:
|
||||||
|
description: The SecretAccessID is used for authentication
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the entry in the Secret
|
||||||
|
resource's `data` field to be used. Some instances
|
||||||
|
of this field may be defaulted, in others it
|
||||||
|
may be required.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: The name of the Secret resource being
|
||||||
|
referred to.
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: Namespace of the resource being referred
|
||||||
|
to. Ignored if referent is not cluster-scoped.
|
||||||
|
cluster-scoped defaults to the namespace of
|
||||||
|
the referent.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
accessType:
|
||||||
|
description: A reference to a specific 'key' within
|
||||||
|
a Secret resource, In some instances, `key` is a
|
||||||
|
required field.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the entry in the Secret
|
||||||
|
resource's `data` field to be used. Some instances
|
||||||
|
of this field may be defaulted, in others it
|
||||||
|
may be required.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: The name of the Secret resource being
|
||||||
|
referred to.
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: Namespace of the resource being referred
|
||||||
|
to. Ignored if referent is not cluster-scoped.
|
||||||
|
cluster-scoped defaults to the namespace of
|
||||||
|
the referent.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
accessTypeParam:
|
||||||
|
description: A reference to a specific 'key' within
|
||||||
|
a Secret resource, In some instances, `key` is a
|
||||||
|
required field.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the entry in the Secret
|
||||||
|
resource's `data` field to be used. Some instances
|
||||||
|
of this field may be defaulted, in others it
|
||||||
|
may be required.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: The name of the Secret resource being
|
||||||
|
referred to.
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: Namespace of the resource being referred
|
||||||
|
to. Ignored if referent is not cluster-scoped.
|
||||||
|
cluster-scoped defaults to the namespace of
|
||||||
|
the referent.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- secretRef
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- akeylessGWApiURL
|
||||||
|
- authSecretRef
|
||||||
|
type: object
|
||||||
alibaba:
|
alibaba:
|
||||||
description: Alibaba configures this store to sync secrets using
|
description: Alibaba configures this store to sync secrets using
|
||||||
Alibaba Cloud provider
|
Alibaba Cloud provider
|
||||||
|
@ -222,7 +310,7 @@ spec:
|
||||||
properties:
|
properties:
|
||||||
authSecretRef:
|
authSecretRef:
|
||||||
description: Auth configures how the operator authenticates
|
description: Auth configures how the operator authenticates
|
||||||
with Azure.
|
with Azure. Required for ServicePrincipal auth type.
|
||||||
properties:
|
properties:
|
||||||
clientId:
|
clientId:
|
||||||
description: The Azure clientId of the service principle
|
description: The Azure clientId of the service principle
|
||||||
|
@ -266,17 +354,30 @@ spec:
|
||||||
- clientId
|
- clientId
|
||||||
- clientSecret
|
- clientSecret
|
||||||
type: object
|
type: object
|
||||||
|
authType:
|
||||||
|
default: ServicePrincipal
|
||||||
|
description: 'Auth type defines how to authenticate to the
|
||||||
|
keyvault service. Valid values are: - "ServicePrincipal"
|
||||||
|
(default): Using a service principal (tenantId, clientId,
|
||||||
|
clientSecret) - "ManagedIdentity": Using Managed Identity
|
||||||
|
assigned to the pod (see aad-pod-identity)'
|
||||||
|
enum:
|
||||||
|
- ServicePrincipal
|
||||||
|
- ManagedIdentity
|
||||||
|
type: string
|
||||||
|
identityId:
|
||||||
|
description: If multiple Managed Identity is assigned to the
|
||||||
|
pod, you can select the one to be used
|
||||||
|
type: string
|
||||||
tenantId:
|
tenantId:
|
||||||
description: TenantID configures the Azure Tenant to send
|
description: TenantID configures the Azure Tenant to send
|
||||||
requests to.
|
requests to. Required for ServicePrincipal auth type.
|
||||||
type: string
|
type: string
|
||||||
vaultUrl:
|
vaultUrl:
|
||||||
description: Vault Url from which the secrets to be fetched
|
description: Vault Url from which the secrets to be fetched
|
||||||
from.
|
from.
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- authSecretRef
|
|
||||||
- tenantId
|
|
||||||
- vaultUrl
|
- vaultUrl
|
||||||
type: object
|
type: object
|
||||||
gcpsm:
|
gcpsm:
|
||||||
|
|
|
@ -54,6 +54,94 @@ spec:
|
||||||
maxProperties: 1
|
maxProperties: 1
|
||||||
minProperties: 1
|
minProperties: 1
|
||||||
properties:
|
properties:
|
||||||
|
akeyless:
|
||||||
|
description: Akeyless configures this store to sync secrets using
|
||||||
|
Akeyless Vault provider
|
||||||
|
properties:
|
||||||
|
akeylessGWApiURL:
|
||||||
|
description: Akeyless GW API Url from which the secrets to
|
||||||
|
be fetched from.
|
||||||
|
type: string
|
||||||
|
authSecretRef:
|
||||||
|
description: Auth configures how the operator authenticates
|
||||||
|
with Akeyless.
|
||||||
|
properties:
|
||||||
|
secretRef:
|
||||||
|
description: 'AkeylessAuthSecretRef AKEYLESS_ACCESS_TYPE_PARAM:
|
||||||
|
AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.'
|
||||||
|
properties:
|
||||||
|
accessID:
|
||||||
|
description: The SecretAccessID is used for authentication
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the entry in the Secret
|
||||||
|
resource's `data` field to be used. Some instances
|
||||||
|
of this field may be defaulted, in others it
|
||||||
|
may be required.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: The name of the Secret resource being
|
||||||
|
referred to.
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: Namespace of the resource being referred
|
||||||
|
to. Ignored if referent is not cluster-scoped.
|
||||||
|
cluster-scoped defaults to the namespace of
|
||||||
|
the referent.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
accessType:
|
||||||
|
description: A reference to a specific 'key' within
|
||||||
|
a Secret resource, In some instances, `key` is a
|
||||||
|
required field.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the entry in the Secret
|
||||||
|
resource's `data` field to be used. Some instances
|
||||||
|
of this field may be defaulted, in others it
|
||||||
|
may be required.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: The name of the Secret resource being
|
||||||
|
referred to.
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: Namespace of the resource being referred
|
||||||
|
to. Ignored if referent is not cluster-scoped.
|
||||||
|
cluster-scoped defaults to the namespace of
|
||||||
|
the referent.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
accessTypeParam:
|
||||||
|
description: A reference to a specific 'key' within
|
||||||
|
a Secret resource, In some instances, `key` is a
|
||||||
|
required field.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the entry in the Secret
|
||||||
|
resource's `data` field to be used. Some instances
|
||||||
|
of this field may be defaulted, in others it
|
||||||
|
may be required.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: The name of the Secret resource being
|
||||||
|
referred to.
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description: Namespace of the resource being referred
|
||||||
|
to. Ignored if referent is not cluster-scoped.
|
||||||
|
cluster-scoped defaults to the namespace of
|
||||||
|
the referent.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- secretRef
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- akeylessGWApiURL
|
||||||
|
- authSecretRef
|
||||||
|
type: object
|
||||||
alibaba:
|
alibaba:
|
||||||
description: Alibaba configures this store to sync secrets using
|
description: Alibaba configures this store to sync secrets using
|
||||||
Alibaba Cloud provider
|
Alibaba Cloud provider
|
||||||
|
@ -222,7 +310,7 @@ spec:
|
||||||
properties:
|
properties:
|
||||||
authSecretRef:
|
authSecretRef:
|
||||||
description: Auth configures how the operator authenticates
|
description: Auth configures how the operator authenticates
|
||||||
with Azure.
|
with Azure. Required for ServicePrincipal auth type.
|
||||||
properties:
|
properties:
|
||||||
clientId:
|
clientId:
|
||||||
description: The Azure clientId of the service principle
|
description: The Azure clientId of the service principle
|
||||||
|
@ -266,17 +354,30 @@ spec:
|
||||||
- clientId
|
- clientId
|
||||||
- clientSecret
|
- clientSecret
|
||||||
type: object
|
type: object
|
||||||
|
authType:
|
||||||
|
default: ServicePrincipal
|
||||||
|
description: 'Auth type defines how to authenticate to the
|
||||||
|
keyvault service. Valid values are: - "ServicePrincipal"
|
||||||
|
(default): Using a service principal (tenantId, clientId,
|
||||||
|
clientSecret) - "ManagedIdentity": Using Managed Identity
|
||||||
|
assigned to the pod (see aad-pod-identity)'
|
||||||
|
enum:
|
||||||
|
- ServicePrincipal
|
||||||
|
- ManagedIdentity
|
||||||
|
type: string
|
||||||
|
identityId:
|
||||||
|
description: If multiple Managed Identity is assigned to the
|
||||||
|
pod, you can select the one to be used
|
||||||
|
type: string
|
||||||
tenantId:
|
tenantId:
|
||||||
description: TenantID configures the Azure Tenant to send
|
description: TenantID configures the Azure Tenant to send
|
||||||
requests to.
|
requests to. Required for ServicePrincipal auth type.
|
||||||
type: string
|
type: string
|
||||||
vaultUrl:
|
vaultUrl:
|
||||||
description: Vault Url from which the secrets to be fetched
|
description: Vault Url from which the secrets to be fetched
|
||||||
from.
|
from.
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- authSecretRef
|
|
||||||
- tenantId
|
|
||||||
- vaultUrl
|
- vaultUrl
|
||||||
type: object
|
type: object
|
||||||
gcpsm:
|
gcpsm:
|
||||||
|
|
68
docs/provider-akeyless.md
Normal file
68
docs/provider-akeyless.md
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
## Akeyless Vault
|
||||||
|
|
||||||
|
External Secrets Operator integrates with [Akeyless API](https://docs.akeyless.io/reference#v2).
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
The API requires an access-id, access-type and access-Type-param.
|
||||||
|
|
||||||
|
The supported auth-methods and their params are:
|
||||||
|
|
||||||
|
| accessType | accessTypeParam |
|
||||||
|
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `api_key` | The access key. |
|
||||||
|
| `k8s` | The k8s configuration name |
|
||||||
|
| `aws_iam` | - |
|
||||||
|
| `gcp` | The gcp audience |
|
||||||
|
| `azure_ad` | azure object id (optional) |
|
||||||
|
|
||||||
|
form more information about [Akeyless Authentication Methods](https://docs.akeyless.io/docs/access-and-authentication-methods)
|
||||||
|
|
||||||
|
### Akeless credentials secret
|
||||||
|
|
||||||
|
Create a secret containing your credentials:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: akeylss-secret-creds
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
accessId: "p-XXXX"
|
||||||
|
accessType: # k8s/aws_iam/gcp/azure_ad/api_key
|
||||||
|
accessTypeParam: # can be one of the following: k8s-conf-name/gcp-audience/azure-obj-id/access-key
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update secret store
|
||||||
|
Be sure the `akeyless` provider is listed in the `Kind=SecretStore` and the `akeylessGWApiURL` is set (def: "https://api.akeless.io".
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{% include 'akeyless-secret-store.yaml' %}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creating external secret
|
||||||
|
|
||||||
|
To get a secret from Akeyless and secret it on the Kubernetes cluster, a `Kind=ExternalSecret` is needed.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{% include 'akeyless-external-secret.yaml' %}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using DataFrom
|
||||||
|
|
||||||
|
DataFrom can be used to get a secret as a JSON string and attempt to parse it.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{% include 'akeyless-external-secret-json.yaml' %}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting the Kubernetes secret
|
||||||
|
The operator will fetch the secret and inject it as a `Kind=Secret`.
|
||||||
|
```
|
||||||
|
kubectl get secret akeyless-secret-to-create -o jsonpath='{.data.secretKey}' | base64 -d
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl get secret akeyless-secret-to-create-json -o jsonpath='{.data}'
|
||||||
|
```
|
|
@ -7,23 +7,37 @@ External Secrets Operator integrates with [Azure Key vault](https://azure.micros
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
At the moment, we only support [service principals](https://docs.microsoft.com/en-us/azure/key-vault/general/authentication) authentication.
|
We support Service Principals and Managed Identity [authentication](https://docs.microsoft.com/en-us/azure/key-vault/general/authentication).
|
||||||
|
|
||||||
|
To use Managed Identity authentication, you should use [aad-pod-identity](https://azure.github.io/aad-pod-identity/docs/) to assign the identity to external-secrets operator. To add the selector to external-secrets operator, use `podLabels` in your values.yaml in case of Helm installation of external-secrets.
|
||||||
|
|
||||||
#### Service Principal key authentication
|
#### Service Principal key authentication
|
||||||
|
|
||||||
A service Principal client and Secret is created and the JSON keyfile is stored in a `Kind=Secret`. The `ClientID` and `ClientSecret` should be configured for the secret. This service principal should have proper access rights to the keyvault to be managed by the operator
|
A service Principal client and Secret is created and the JSON keyfile is stored in a `Kind=Secret`. The `ClientID` and `ClientSecret` should be configured for the secret. This service principal should have proper access rights to the keyvault to be managed by the operator
|
||||||
|
|
||||||
|
#### Managed Identity authentication
|
||||||
|
|
||||||
|
A Managed Identity should be created in Azure, and that Identity should have proper rights to the keyvault to be managed by the operator.
|
||||||
|
|
||||||
|
If there are multiple Managed Identitites for different keyvaults, the operator should have been assigned all identities via [aad-pod-identity](https://azure.github.io/aad-pod-identity/docs/), then the SecretStore configuration should include the Id of the idenetity to be used via the `identityId` field.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{% include 'azkv-credentials-secret.yaml' %}
|
{% include 'azkv-credentials-secret.yaml' %}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Update secret store
|
### Update secret store
|
||||||
Be sure the `azkv` provider is listed in the `Kind=SecretStore`
|
Be sure the `azurekv` provider is listed in the `Kind=SecretStore`
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{% include 'azkv-secret-store.yaml' %}
|
{% include 'azkv-secret-store.yaml' %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or in case of Managed Idenetity authentication:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{% include 'azkv-secret-store-mi.yaml' %}
|
||||||
|
```
|
||||||
|
|
||||||
### Object Types
|
### Object Types
|
||||||
|
|
||||||
Azure KeyVault manages different [object types](https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#object-types), we support `keys`, `secrets` and `certificates`. Simply prefix the key with `key`, `secret` or `cert` to retrieve the desired type (defaults to secret).
|
Azure KeyVault manages different [object types](https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#object-types), we support `keys`, `secrets` and `certificates`. Simply prefix the key with `key`, `secret` or `cert` to retrieve the desired type (defaults to secret).
|
||||||
|
|
12
docs/snippets/akeyless-credentials-secret.yaml
Normal file
12
docs/snippets/akeyless-credentials-secret.yaml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: akeylss-secret-creds
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
accessId: "p-XXXX"
|
||||||
|
accessType: # k8s/aws_iam/gcp/azure_ad/api_key
|
||||||
|
accessTypeParam: # can be one of the following: k8s-conf-name/gcp-audience/azure-obj-id/access-key
|
||||||
|
|
||||||
|
|
||||||
|
|
18
docs/snippets/akeyless-external-secret-json.yaml
Normal file
18
docs/snippets/akeyless-external-secret-json.yaml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: external-secrets.io/v1alpha1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: akeyless-external-secret-example-json
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
|
||||||
|
secretStoreRef:
|
||||||
|
kind: SecretStore
|
||||||
|
name: akeyless-secret-store # Must match SecretStore on the cluster
|
||||||
|
|
||||||
|
target:
|
||||||
|
name: akeyless-secret-to-create-json # Name for the secret to be created on the cluster
|
||||||
|
creationPolicy: Owner
|
||||||
|
|
||||||
|
# for json formatted secrets: each key in the json will be used as the secret key in the SECRET k8s target object
|
||||||
|
dataFrom:
|
||||||
|
- key: secret-name # Full path of the secret on Akeyless
|
19
docs/snippets/akeyless-external-secret.yaml
Normal file
19
docs/snippets/akeyless-external-secret.yaml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
apiVersion: external-secrets.io/v1alpha1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: akeyless-external-secret-example
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
|
||||||
|
secretStoreRef:
|
||||||
|
kind: SecretStore
|
||||||
|
name: akeyless-secret-store # Must match SecretStore on the cluster
|
||||||
|
|
||||||
|
target:
|
||||||
|
name: akeyless-secret-to-create # Name for the secret to be created on the cluster
|
||||||
|
creationPolicy: Owner
|
||||||
|
|
||||||
|
data:
|
||||||
|
- secretKey: secretKey # Key given to the secret to be created on the cluster
|
||||||
|
remoteRef:
|
||||||
|
key: secret-name # Full path of the secret on Akeyless
|
20
docs/snippets/akeyless-secret-store.yaml
Normal file
20
docs/snippets/akeyless-secret-store.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: external-secrets.io/v1alpha1
|
||||||
|
kind: SecretStore
|
||||||
|
metadata:
|
||||||
|
name: akeyless-secret-store
|
||||||
|
spec:
|
||||||
|
provider:
|
||||||
|
akeyless:
|
||||||
|
# URL of your akeyless API
|
||||||
|
akeylessGWApiURL: "https://api.akeyless.io"
|
||||||
|
authSecretRef:
|
||||||
|
secretRef:
|
||||||
|
accessID:
|
||||||
|
name: akeylss-secret-creds
|
||||||
|
key: accessId
|
||||||
|
accessType:
|
||||||
|
name: akeylss-secret-creds
|
||||||
|
key: accessType
|
||||||
|
accessTypeParam:
|
||||||
|
name: akeylss-secret-creds
|
||||||
|
key: accessTypeParam
|
13
docs/snippets/azkv-secret-store-mi.yaml
Normal file
13
docs/snippets/azkv-secret-store-mi.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
apiVersion: external-secrets.io/v1alpha1
|
||||||
|
kind: SecretStore
|
||||||
|
metadata:
|
||||||
|
name: example-secret-store
|
||||||
|
spec:
|
||||||
|
provider:
|
||||||
|
# provider type: azure keyvault
|
||||||
|
azurekv:
|
||||||
|
authType: ManagedIdentity
|
||||||
|
# Optionally set the Id of the Managed Identity, if multiple identities is assignet to external-secrets operator
|
||||||
|
identityId: "<MI_clientId>"
|
||||||
|
# URL of your vault instance, see: https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates
|
||||||
|
vaultUrl: "https://my-keyvault-name.vault.azure.net"
|
|
@ -1,5 +1,5 @@
|
||||||
{% raw %}
|
{% raw %}
|
||||||
# define your tempalte in a config map
|
# define your template in a config map
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
ARG GO_VERSION=1.16
|
ARG GO_VERSION=1.16
|
||||||
FROM golang:$GO_VERSION-buster as builder
|
FROM golang:$GO_VERSION-buster as builder
|
||||||
|
|
||||||
ENV KUBECTL_VERSION="v1.19.2"
|
ENV KUBECTL_VERSION="v1.21.2"
|
||||||
ENV HELM_VERSION="v3.3.4"
|
ENV HELM_VERSION="v3.7.1"
|
||||||
|
|
||||||
RUN go get -u github.com/onsi/ginkgo/ginkgo
|
RUN go get -u github.com/onsi/ginkgo/ginkgo
|
||||||
RUN wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && \
|
RUN wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && \
|
||||||
|
@ -10,7 +10,7 @@ RUN wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_
|
||||||
wget -q https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz -O - | tar -xzO linux-amd64/helm > /usr/local/bin/helm && \
|
wget -q https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz -O - | tar -xzO linux-amd64/helm > /usr/local/bin/helm && \
|
||||||
chmod +x /usr/local/bin/helm
|
chmod +x /usr/local/bin/helm
|
||||||
|
|
||||||
FROM alpine:3.12
|
FROM alpine:3.15.0
|
||||||
RUN apk add -U --no-cache \
|
RUN apk add -U --no-cache \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
bash \
|
bash \
|
||||||
|
|
|
@ -4,7 +4,7 @@ SHELL := /bin/bash
|
||||||
|
|
||||||
IMG_TAG = test
|
IMG_TAG = test
|
||||||
IMG = local/external-secrets-e2e:$(IMG_TAG)
|
IMG = local/external-secrets-e2e:$(IMG_TAG)
|
||||||
KIND_IMG = "kindest/node:v1.20.7@sha256:cbeaf907fc78ac97ce7b625e4bf0de16e3ea725daf6b04f930bd14c67c671ff9"
|
KIND_IMG = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6"
|
||||||
BUILD_ARGS ?=
|
BUILD_ARGS ?=
|
||||||
export FOCUS := $(FOCUS)
|
export FOCUS := $(FOCUS)
|
||||||
|
|
||||||
|
@ -19,13 +19,14 @@ test: e2e-image ## Run e2e tests against current kube context
|
||||||
$(MAKE) -C ../ docker.build \
|
$(MAKE) -C ../ docker.build \
|
||||||
IMAGE_REGISTRY=local/external-secrets \
|
IMAGE_REGISTRY=local/external-secrets \
|
||||||
VERSION=$(IMG_TAG) \
|
VERSION=$(IMG_TAG) \
|
||||||
ARCH=amd64
|
ARCH=amd64 \
|
||||||
|
BUILD_ARGS="${BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
|
||||||
kind load docker-image --name="external-secrets" local/external-secrets:$(IMG_TAG)
|
kind load docker-image --name="external-secrets" local/external-secrets:$(IMG_TAG)
|
||||||
kind load docker-image --name="external-secrets" $(IMG)
|
kind load docker-image --name="external-secrets" $(IMG)
|
||||||
./run.sh
|
./run.sh
|
||||||
|
|
||||||
e2e-bin:
|
e2e-bin:
|
||||||
CGO_ENABLED=0 ginkgo build .
|
CGO_ENABLED=0 go run github.com/onsi/ginkgo/ginkgo build .
|
||||||
|
|
||||||
e2e-image: e2e-bin
|
e2e-image: e2e-bin
|
||||||
-rm -rf ./k8s/deploy
|
-rm -rf ./k8s/deploy
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
NC='\e[0m'
|
NC='\e[0m'
|
||||||
BGREEN='\e[32m'
|
BGREEN='\e[32m'
|
||||||
|
@ -46,8 +46,8 @@ ginkgo_args=(
|
||||||
kubectl apply -f /k8s/deploy/crds
|
kubectl apply -f /k8s/deploy/crds
|
||||||
|
|
||||||
echo -e "${BGREEN}Running e2e test suite (FOCUS=${FOCUS})...${NC}"
|
echo -e "${BGREEN}Running e2e test suite (FOCUS=${FOCUS})...${NC}"
|
||||||
ginkgo "${ginkgo_args[@]}" \
|
ACK_GINKGO_RC=true ginkgo "${ginkgo_args[@]}" \
|
||||||
-focus="${FOCUS}" \
|
-focus="${FOCUS}" \
|
||||||
-skip="\[Serial\]|\[MemoryLeak\]" \
|
-skip="\[Serial\]|\[MemoryLeak\]" \
|
||||||
-nodes="${E2E_NODES}" \
|
-nodes="${E2E_NODES}" \
|
||||||
/e2e.test
|
/e2e.test
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
vault "github.com/hashicorp/vault/api"
|
vault "github.com/hashicorp/vault/api"
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
|
@ -286,6 +286,7 @@ func (l *Vault) Setup(cfg *Config) error {
|
||||||
return l.chart.Setup(cfg)
|
return l.chart.Setup(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gocritic
|
||||||
func genVaultCertificates(namespace string) ([]byte, []byte, []byte, []byte, []byte, []byte, error) {
|
func genVaultCertificates(namespace string) ([]byte, []byte, []byte, []byte, []byte, []byte, error) {
|
||||||
// gen server ca + certs
|
// gen server ca + certs
|
||||||
serverRootCert, serverRootPem, serverRootKey, err := genCARoot()
|
serverRootCert, serverRootPem, serverRootKey, err := genCARoot()
|
||||||
|
|
|
@ -13,9 +13,7 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
set -o errexit
|
set -euo pipefail
|
||||||
set -o nounset
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
if ! command -v kind --version &> /dev/null; then
|
if ! command -v kind --version &> /dev/null; then
|
||||||
echo "kind is not installed. Use the package manager or visit the official site https://kind.sigs.k8s.io/"
|
echo "kind is not installed. Use the package manager or visit the official site https://kind.sigs.k8s.io/"
|
||||||
|
@ -56,6 +54,9 @@ kubectl run --rm \
|
||||||
--env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \
|
--env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \
|
||||||
--env="AZURE_CLIENT_ID=${AZURE_CLIENT_ID:-}" \
|
--env="AZURE_CLIENT_ID=${AZURE_CLIENT_ID:-}" \
|
||||||
--env="AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET:-}" \
|
--env="AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET:-}" \
|
||||||
|
--env="AKEYLESS_ACCESS_ID=${AKEYLESS_ACCESS_ID:-}" \
|
||||||
|
--env="AKEYLESS_ACCESS_TYPE=${AKEYLESS_ACCESS_TYPE:-}" \
|
||||||
|
--env="AKEYLESS_ACCESS_TYPE_PARAM=${AKEYLESS_ACCESS_TYPE_PARAM:-}" \
|
||||||
--env="TENANT_ID=${TENANT_ID:-}" \
|
--env="TENANT_ID=${TENANT_ID:-}" \
|
||||||
--env="VAULT_URL=${VAULT_URL:-}" \
|
--env="VAULT_URL=${VAULT_URL:-}" \
|
||||||
--env="GITLAB_TOKEN=${GITLAB_TOKEN:-}" \
|
--env="GITLAB_TOKEN=${GITLAB_TOKEN:-}" \
|
||||||
|
|
49
e2e/suite/akeyless/akeyless.go
Normal file
49
e2e/suite/akeyless/akeyless.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akeyless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
// nolint
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
// nolint
|
||||||
|
. "github.com/onsi/ginkgo/extensions/table"
|
||||||
|
|
||||||
|
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||||
|
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("[akeyless] ", func() {
|
||||||
|
f := framework.New("eso-akeyless")
|
||||||
|
accessID := os.Getenv("AKEYLESS_ACCESS_ID")
|
||||||
|
accessType := os.Getenv("AKEYLESS_ACCESS_TYPE")
|
||||||
|
accessTypeParam := os.Getenv("AKEYLESS_ACCESS_TYPE_PARAM")
|
||||||
|
prov := newAkeylessProvider(f, accessID, accessType, accessTypeParam)
|
||||||
|
|
||||||
|
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
||||||
|
Entry(common.SimpleDataSync(f)),
|
||||||
|
Entry(common.NestedJSONWithGJSON(f)),
|
||||||
|
Entry(common.JSONDataFromSync(f)),
|
||||||
|
Entry(common.JSONDataWithProperty(f)),
|
||||||
|
Entry(common.JSONDataWithTemplate(f)),
|
||||||
|
Entry(common.DockerJSONConfig(f)),
|
||||||
|
Entry(common.DataPropertyDockerconfigJSON(f)),
|
||||||
|
Entry(common.SSHKeySync(f)),
|
||||||
|
Entry(common.SSHKeySyncDataProperty(f)),
|
||||||
|
Entry(common.SyncWithoutTargetName(f)),
|
||||||
|
Entry(common.JSONDataWithoutTargetName(f)),
|
||||||
|
)
|
||||||
|
})
|
227
e2e/suite/akeyless/provider.go
Normal file
227
e2e/suite/akeyless/provider.go
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akeyless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
aws_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/aws"
|
||||||
|
azure_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/azure"
|
||||||
|
gcp_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/gcp"
|
||||||
|
"github.com/akeylesslabs/akeyless-go/v2"
|
||||||
|
|
||||||
|
//nolint
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
|
||||||
|
//nolint
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||||
|
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
|
||||||
|
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
type akeylessProvider struct {
|
||||||
|
accessID string
|
||||||
|
accessType string
|
||||||
|
accessTypeParam string
|
||||||
|
framework *framework.Framework
|
||||||
|
restAPIClient *akeyless.V2ApiService
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiErr akeyless.GenericOpenAPIError
|
||||||
|
|
||||||
|
const DefServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||||
|
|
||||||
|
func newAkeylessProvider(f *framework.Framework, accessID, accessType, accessTypeParam string) *akeylessProvider {
|
||||||
|
prov := &akeylessProvider{
|
||||||
|
accessID: accessID,
|
||||||
|
accessType: accessType,
|
||||||
|
accessTypeParam: accessTypeParam,
|
||||||
|
framework: f,
|
||||||
|
}
|
||||||
|
|
||||||
|
restAPIClient := akeyless.NewAPIClient(&akeyless.Configuration{
|
||||||
|
Servers: []akeyless.ServerConfiguration{
|
||||||
|
{
|
||||||
|
URL: "https://api.akeyless.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).V2Api
|
||||||
|
|
||||||
|
prov.restAPIClient = restAPIClient
|
||||||
|
|
||||||
|
BeforeEach(prov.BeforeEach)
|
||||||
|
return prov
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSecret creates a secret.
|
||||||
|
func (a *akeylessProvider) CreateSecret(key, val string) {
|
||||||
|
token, err := a.GetToken()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
gsvBody := akeyless.CreateSecret{
|
||||||
|
Name: key,
|
||||||
|
Value: val,
|
||||||
|
Token: &token,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = a.restAPIClient.CreateSecret(ctx).Body(gsvBody).Execute()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessProvider) DeleteSecret(key string) {
|
||||||
|
token, err := a.GetToken()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
gsvBody := akeyless.DeleteItem{
|
||||||
|
Name: key,
|
||||||
|
Token: &token,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = a.restAPIClient.DeleteItem(ctx).Body(gsvBody).Execute()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessProvider) BeforeEach() {
|
||||||
|
// Creating an Akeyless secret
|
||||||
|
akeylessCreds := &v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "provider-secret",
|
||||||
|
Namespace: a.framework.Namespace.Name,
|
||||||
|
},
|
||||||
|
StringData: map[string]string{
|
||||||
|
"access-id": a.accessID,
|
||||||
|
"access-type": a.accessType,
|
||||||
|
"access-type-param": a.accessTypeParam,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := a.framework.CRClient.Create(context.Background(), akeylessCreds)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
// Creating Akeyless secret store
|
||||||
|
secretStore := &esv1alpha1.SecretStore{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: a.framework.Namespace.Name,
|
||||||
|
Namespace: a.framework.Namespace.Name,
|
||||||
|
},
|
||||||
|
Spec: esv1alpha1.SecretStoreSpec{
|
||||||
|
Provider: &esv1alpha1.SecretStoreProvider{
|
||||||
|
Akeyless: &esv1alpha1.AkeylessProvider{
|
||||||
|
Auth: &esv1alpha1.AkeylessAuth{
|
||||||
|
SecretRef: esv1alpha1.AkeylessAuthSecretRef{
|
||||||
|
AccessID: esmeta.SecretKeySelector{
|
||||||
|
Name: "access-id-secret",
|
||||||
|
Key: "access-id",
|
||||||
|
},
|
||||||
|
AccessType: esmeta.SecretKeySelector{
|
||||||
|
Name: "access-type-secret",
|
||||||
|
Key: "access-type",
|
||||||
|
},
|
||||||
|
AccessTypeParam: esmeta.SecretKeySelector{
|
||||||
|
Name: "access-type-param-secert",
|
||||||
|
Key: "access-type-param",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = a.framework.CRClient.Create(context.Background(), secretStore)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessProvider) GetToken() (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
authBody := akeyless.NewAuthWithDefaults()
|
||||||
|
authBody.AccessId = akeyless.PtrString(a.accessID)
|
||||||
|
|
||||||
|
if a.accessType == "api_key" {
|
||||||
|
authBody.AccessKey = akeyless.PtrString(a.accessTypeParam)
|
||||||
|
} else if a.accessType == "k8s" {
|
||||||
|
jwtString, err := readK8SServiceAccountJWT()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read JWT with Kubernetes Auth from %v. error: %w", DefServiceAccountFile, err)
|
||||||
|
}
|
||||||
|
K8SAuthConfigName := a.accessTypeParam
|
||||||
|
authBody.AccessType = akeyless.PtrString(a.accessType)
|
||||||
|
authBody.K8sServiceAccountToken = akeyless.PtrString(jwtString)
|
||||||
|
authBody.K8sAuthConfigName = akeyless.PtrString(K8SAuthConfigName)
|
||||||
|
} else {
|
||||||
|
cloudID, err := a.getCloudID(a.accessType, a.accessTypeParam)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Require Cloud ID " + err.Error())
|
||||||
|
}
|
||||||
|
authBody.AccessType = akeyless.PtrString(a.accessType)
|
||||||
|
authBody.CloudId = akeyless.PtrString(cloudID)
|
||||||
|
}
|
||||||
|
|
||||||
|
authOut, _, err := a.restAPIClient.Auth(ctx).Body(*authBody).Execute()
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &apiErr) {
|
||||||
|
return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("authentication failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
token := authOut.GetToken()
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessProvider) getCloudID(provider, accTypeParam string) (string, error) {
|
||||||
|
var cloudID string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch provider {
|
||||||
|
case "azure_ad":
|
||||||
|
cloudID, err = azure_cloud_id.GetCloudId(accTypeParam)
|
||||||
|
case "aws_iam":
|
||||||
|
cloudID, err = aws_cloud_id.GetCloudId()
|
||||||
|
case "gcp":
|
||||||
|
cloudID, err = gcp_cloud_id.GetCloudID(accTypeParam)
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unable to determine provider: %s", provider)
|
||||||
|
}
|
||||||
|
return cloudID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// readK8SServiceAccountJWT reads the JWT data for the Agent to submit to Akeyless Gateway.
|
||||||
|
func readK8SServiceAccountJWT() (string, error) {
|
||||||
|
data, err := os.Open(DefServiceAccountFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer data.Close()
|
||||||
|
|
||||||
|
contentBytes, err := ioutil.ReadAll(data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := strings.TrimSpace(string(contentBytes))
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(a)), nil
|
||||||
|
}
|
|
@ -31,7 +31,11 @@ var _ = Describe("[alibaba] ", func() {
|
||||||
accessKeyID := os.Getenv("ACCESS_KEY_ID")
|
accessKeyID := os.Getenv("ACCESS_KEY_ID")
|
||||||
accessKeySecret := os.Getenv("ACCESS_KEY_SECRET")
|
accessKeySecret := os.Getenv("ACCESS_KEY_SECRET")
|
||||||
regionID := os.Getenv("REGION_ID")
|
regionID := os.Getenv("REGION_ID")
|
||||||
prov := newAlibabaProvider(f, accessKeyID, accessKeySecret, regionID)
|
prov := &alibabaProvider{}
|
||||||
|
|
||||||
|
if accessKeyID != "" && accessKeySecret != "" && regionID != "" {
|
||||||
|
prov = newAlibabaProvider(f, accessKeyID, accessKeySecret, regionID)
|
||||||
|
}
|
||||||
|
|
||||||
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
||||||
Entry(common.SimpleDataSync(f)),
|
Entry(common.SimpleDataSync(f)),
|
||||||
|
|
|
@ -30,7 +30,11 @@ var _ = Describe("[azure] ", func() {
|
||||||
tenantID := os.Getenv("TENANT_ID")
|
tenantID := os.Getenv("TENANT_ID")
|
||||||
clientID := os.Getenv("AZURE_CLIENT_ID")
|
clientID := os.Getenv("AZURE_CLIENT_ID")
|
||||||
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
|
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
|
||||||
prov := newazureProvider(f, clientID, clientSecret, tenantID, vaultURL)
|
prov := &azureProvider{}
|
||||||
|
|
||||||
|
if vaultURL != "" && tenantID != "" && clientID != "" && clientSecret != "" {
|
||||||
|
prov = newazureProvider(f, clientID, clientSecret, tenantID, vaultURL)
|
||||||
|
}
|
||||||
|
|
||||||
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
||||||
Entry(common.SimpleDataSync(f)),
|
Entry(common.SimpleDataSync(f)),
|
||||||
|
|
|
@ -22,6 +22,14 @@ import (
|
||||||
"github.com/external-secrets/external-secrets/e2e/framework"
|
"github.com/external-secrets/external-secrets/e2e/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Constants.
|
||||||
|
dockerConfigExampleName = "docker-config-example"
|
||||||
|
dockerConfigJSONKey = ".dockerconfigjson"
|
||||||
|
mysecretToStringTemplating = "{{ .mysecret | toString }}"
|
||||||
|
sshPrivateKey = "ssh-privatekey"
|
||||||
|
)
|
||||||
|
|
||||||
// This case creates multiple secrets with simple key/value pairs and syncs them using multiple .Spec.Data blocks.
|
// This case creates multiple secrets with simple key/value pairs and syncs them using multiple .Spec.Data blocks.
|
||||||
// Not supported by: vault.
|
// Not supported by: vault.
|
||||||
func SimpleDataSync(f *framework.Framework) (string, func(*framework.TestCase)) {
|
func SimpleDataSync(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||||
|
@ -289,7 +297,7 @@ func NestedJSONWithGJSON(f *framework.Framework) (string, func(*framework.TestCa
|
||||||
// not supported by: vault.
|
// not supported by: vault.
|
||||||
func DockerJSONConfig(f *framework.Framework) (string, func(*framework.TestCase)) {
|
func DockerJSONConfig(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||||
return "[common] should sync docker configurated json secrets with template simple", func(tc *framework.TestCase) {
|
return "[common] should sync docker configurated json secrets with template simple", func(tc *framework.TestCase) {
|
||||||
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, "docker-config-example")
|
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, dockerConfigExampleName)
|
||||||
dockerconfig := `{"auths":{"https://index.docker.io/v1/": {"auth": "c3R...zE2"}}}`
|
dockerconfig := `{"auths":{"https://index.docker.io/v1/": {"auth": "c3R...zE2"}}}`
|
||||||
cloudSecretValue := fmt.Sprintf(`{"dockerconfig": %s}`, dockerconfig)
|
cloudSecretValue := fmt.Sprintf(`{"dockerconfig": %s}`, dockerconfig)
|
||||||
tc.Secrets = map[string]string{
|
tc.Secrets = map[string]string{
|
||||||
|
@ -299,7 +307,7 @@ func DockerJSONConfig(f *framework.Framework) (string, func(*framework.TestCase)
|
||||||
tc.ExpectedSecret = &v1.Secret{
|
tc.ExpectedSecret = &v1.Secret{
|
||||||
Type: v1.SecretTypeOpaque,
|
Type: v1.SecretTypeOpaque,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
".dockerconfigjson": []byte(dockerconfig),
|
dockerConfigJSONKey: []byte(dockerconfig),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,7 +323,7 @@ func DockerJSONConfig(f *framework.Framework) (string, func(*framework.TestCase)
|
||||||
|
|
||||||
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
".dockerconfigjson": "{{ .mysecret | toString }}",
|
dockerConfigJSONKey: mysecretToStringTemplating,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,7 +334,7 @@ func DockerJSONConfig(f *framework.Framework) (string, func(*framework.TestCase)
|
||||||
// Need to have a key holding dockerconfig to be supported by vault.
|
// Need to have a key holding dockerconfig to be supported by vault.
|
||||||
func DataPropertyDockerconfigJSON(f *framework.Framework) (string, func(*framework.TestCase)) {
|
func DataPropertyDockerconfigJSON(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||||
return "[common] should sync docker configurated json secrets with template", func(tc *framework.TestCase) {
|
return "[common] should sync docker configurated json secrets with template", func(tc *framework.TestCase) {
|
||||||
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, "docker-config-example")
|
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, dockerConfigExampleName)
|
||||||
dockerconfigString := `"{\"auths\":{\"https://index.docker.io/v1/\": {\"auth\": \"c3R...zE2\"}}}"`
|
dockerconfigString := `"{\"auths\":{\"https://index.docker.io/v1/\": {\"auth\": \"c3R...zE2\"}}}"`
|
||||||
dockerconfig := `{"auths":{"https://index.docker.io/v1/": {"auth": "c3R...zE2"}}}`
|
dockerconfig := `{"auths":{"https://index.docker.io/v1/": {"auth": "c3R...zE2"}}}`
|
||||||
cloudSecretValue := fmt.Sprintf(`{"dockerconfig": %s}`, dockerconfigString)
|
cloudSecretValue := fmt.Sprintf(`{"dockerconfig": %s}`, dockerconfigString)
|
||||||
|
@ -337,7 +345,7 @@ func DataPropertyDockerconfigJSON(f *framework.Framework) (string, func(*framewo
|
||||||
tc.ExpectedSecret = &v1.Secret{
|
tc.ExpectedSecret = &v1.Secret{
|
||||||
Type: v1.SecretTypeDockerConfigJson,
|
Type: v1.SecretTypeDockerConfigJson,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
".dockerconfigjson": []byte(dockerconfig),
|
dockerConfigJSONKey: []byte(dockerconfig),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,7 +362,7 @@ func DataPropertyDockerconfigJSON(f *framework.Framework) (string, func(*framewo
|
||||||
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
||||||
Type: v1.SecretTypeDockerConfigJson,
|
Type: v1.SecretTypeDockerConfigJson,
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
".dockerconfigjson": "{{ .mysecret | toString }}",
|
dockerConfigJSONKey: mysecretToStringTemplating,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,7 +419,7 @@ func SSHKeySync(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||||
tc.ExpectedSecret = &v1.Secret{
|
tc.ExpectedSecret = &v1.Secret{
|
||||||
Type: v1.SecretTypeSSHAuth,
|
Type: v1.SecretTypeSSHAuth,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"ssh-privatekey": []byte(sshSecretValue),
|
sshPrivateKey: []byte(sshSecretValue),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,7 +435,7 @@ func SSHKeySync(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||||
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
||||||
Type: v1.SecretTypeSSHAuth,
|
Type: v1.SecretTypeSSHAuth,
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"ssh-privatekey": "{{ .mysecret | toString }}",
|
sshPrivateKey: mysecretToStringTemplating,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,7 +444,7 @@ func SSHKeySync(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||||
// This case adds an ssh private key secret and syncs it.
|
// This case adds an ssh private key secret and syncs it.
|
||||||
func SSHKeySyncDataProperty(f *framework.Framework) (string, func(*framework.TestCase)) {
|
func SSHKeySyncDataProperty(f *framework.Framework) (string, func(*framework.TestCase)) {
|
||||||
return "[common] should sync ssh key with provider.", func(tc *framework.TestCase) {
|
return "[common] should sync ssh key with provider.", func(tc *framework.TestCase) {
|
||||||
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, "docker-config-example")
|
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, dockerConfigExampleName)
|
||||||
SSHKey := `-----BEGIN OPENSSH PRIVATE KEY-----
|
SSHKey := `-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
|
||||||
NhAAAAAwEAAQAAAYEAsARoZUqo6L5dd0WRjZ2QPq/kKlbjtUY1njzJ01UtdC1u1eSJFUnV
|
NhAAAAAwEAAQAAAYEAsARoZUqo6L5dd0WRjZ2QPq/kKlbjtUY1njzJ01UtdC1u1eSJFUnV
|
||||||
|
@ -483,7 +491,7 @@ func SSHKeySyncDataProperty(f *framework.Framework) (string, func(*framework.Tes
|
||||||
tc.ExpectedSecret = &v1.Secret{
|
tc.ExpectedSecret = &v1.Secret{
|
||||||
Type: v1.SecretTypeSSHAuth,
|
Type: v1.SecretTypeSSHAuth,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"ssh-privatekey": []byte(SSHKey),
|
sshPrivateKey: []byte(SSHKey),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,7 +508,7 @@ func SSHKeySyncDataProperty(f *framework.Framework) (string, func(*framework.Tes
|
||||||
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
|
||||||
Type: v1.SecretTypeSSHAuth,
|
Type: v1.SecretTypeSSHAuth,
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"ssh-privatekey": "{{ .mysecret | toString }}",
|
sshPrivateKey: mysecretToStringTemplating,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,11 @@ var _ = Describe("[gcp] ", func() {
|
||||||
f := framework.New("eso-gcp")
|
f := framework.New("eso-gcp")
|
||||||
credentials := os.Getenv("GCP_SM_SA_JSON")
|
credentials := os.Getenv("GCP_SM_SA_JSON")
|
||||||
projectID := os.Getenv("GCP_PROJECT_ID")
|
projectID := os.Getenv("GCP_PROJECT_ID")
|
||||||
prov := newgcpProvider(f, credentials, projectID)
|
prov := &gcpProvider{}
|
||||||
|
|
||||||
|
if credentials != "" && projectID != "" {
|
||||||
|
prov = newgcpProvider(f, credentials, projectID)
|
||||||
|
}
|
||||||
|
|
||||||
// P12Cert case creates a secret with a p12 cert containing a privkey and cert bundled together.
|
// P12Cert case creates a secret with a p12 cert containing a privkey and cert bundled together.
|
||||||
// It uses templating to generate a k8s secret of type tls with pem values
|
// It uses templating to generate a k8s secret of type tls with pem values
|
||||||
|
|
|
@ -33,7 +33,12 @@ var _ = Describe("[gitlab] ", func() {
|
||||||
f := framework.New("esogitlab")
|
f := framework.New("esogitlab")
|
||||||
credentials := os.Getenv("GITLAB_TOKEN")
|
credentials := os.Getenv("GITLAB_TOKEN")
|
||||||
projectID := os.Getenv("GITLAB_PROJECT_ID")
|
projectID := os.Getenv("GITLAB_PROJECT_ID")
|
||||||
prov := newGitlabProvider(f, credentials, projectID)
|
|
||||||
|
prov := &gitlabProvider{}
|
||||||
|
|
||||||
|
if credentials != "" && projectID != "" {
|
||||||
|
prov = newGitlabProvider(f, credentials, projectID)
|
||||||
|
}
|
||||||
|
|
||||||
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
DescribeTable("sync secrets", framework.TableFunc(f, prov),
|
||||||
Entry(common.SimpleDataSync(f)),
|
Entry(common.SimpleDataSync(f)),
|
||||||
|
|
|
@ -23,6 +23,15 @@ import (
|
||||||
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
"github.com/external-secrets/external-secrets/e2e/suite/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
withTokenAuth = "with token auth"
|
||||||
|
withCertAuth = "with cert auth"
|
||||||
|
withApprole = "with approle auth"
|
||||||
|
withV1 = "with v1 provider"
|
||||||
|
withJWT = "with jwt provider"
|
||||||
|
withK8s = "with kubernetes provider"
|
||||||
|
)
|
||||||
|
|
||||||
var _ = Describe("[vault] ", func() {
|
var _ = Describe("[vault] ", func() {
|
||||||
f := framework.New("eso-vault")
|
f := framework.New("eso-vault")
|
||||||
|
|
||||||
|
@ -30,41 +39,41 @@ var _ = Describe("[vault] ", func() {
|
||||||
framework.TableFunc(f,
|
framework.TableFunc(f,
|
||||||
newVaultProvider(f)),
|
newVaultProvider(f)),
|
||||||
// uses token auth
|
// uses token auth
|
||||||
compose("with token auth", f, common.JSONDataFromSync, useTokenAuth),
|
compose(withTokenAuth, f, common.JSONDataFromSync, useTokenAuth),
|
||||||
compose("with token auth", f, common.JSONDataWithProperty, useTokenAuth),
|
compose(withTokenAuth, f, common.JSONDataWithProperty, useTokenAuth),
|
||||||
compose("with token auth", f, common.JSONDataWithTemplate, useTokenAuth),
|
compose(withTokenAuth, f, common.JSONDataWithTemplate, useTokenAuth),
|
||||||
compose("with token auth", f, common.DataPropertyDockerconfigJSON, useTokenAuth),
|
compose(withTokenAuth, f, common.DataPropertyDockerconfigJSON, useTokenAuth),
|
||||||
compose("with token auth", f, common.JSONDataWithoutTargetName, useTokenAuth),
|
compose(withTokenAuth, f, common.JSONDataWithoutTargetName, useTokenAuth),
|
||||||
// use cert auth
|
// use cert auth
|
||||||
compose("with cert auth", f, common.JSONDataFromSync, useCertAuth),
|
compose(withCertAuth, f, common.JSONDataFromSync, useCertAuth),
|
||||||
compose("with cert auth", f, common.JSONDataWithProperty, useCertAuth),
|
compose(withCertAuth, f, common.JSONDataWithProperty, useCertAuth),
|
||||||
compose("with cert auth", f, common.JSONDataWithTemplate, useCertAuth),
|
compose(withCertAuth, f, common.JSONDataWithTemplate, useCertAuth),
|
||||||
compose("with cert auth", f, common.DataPropertyDockerconfigJSON, useCertAuth),
|
compose(withCertAuth, f, common.DataPropertyDockerconfigJSON, useCertAuth),
|
||||||
compose("with cert auth", f, common.JSONDataWithoutTargetName, useTokenAuth),
|
compose(withCertAuth, f, common.JSONDataWithoutTargetName, useTokenAuth),
|
||||||
// use approle auth
|
// use approle auth
|
||||||
compose("with appRole auth", f, common.JSONDataFromSync, useApproleAuth),
|
compose(withApprole, f, common.JSONDataFromSync, useApproleAuth),
|
||||||
compose("with appRole auth", f, common.JSONDataWithProperty, useApproleAuth),
|
compose(withApprole, f, common.JSONDataWithProperty, useApproleAuth),
|
||||||
compose("with appRole auth", f, common.JSONDataWithTemplate, useApproleAuth),
|
compose(withApprole, f, common.JSONDataWithTemplate, useApproleAuth),
|
||||||
compose("with appRole auth", f, common.DataPropertyDockerconfigJSON, useApproleAuth),
|
compose(withApprole, f, common.DataPropertyDockerconfigJSON, useApproleAuth),
|
||||||
compose("with appRole auth", f, common.JSONDataWithoutTargetName, useTokenAuth),
|
compose(withApprole, f, common.JSONDataWithoutTargetName, useTokenAuth),
|
||||||
// use v1 provider
|
// use v1 provider
|
||||||
compose("with v1 kv provider", f, common.JSONDataFromSync, useV1Provider),
|
compose(withV1, f, common.JSONDataFromSync, useV1Provider),
|
||||||
compose("with v1 kv provider", f, common.JSONDataWithProperty, useV1Provider),
|
compose(withV1, f, common.JSONDataWithProperty, useV1Provider),
|
||||||
compose("with v1 kv provider", f, common.JSONDataWithTemplate, useV1Provider),
|
compose(withV1, f, common.JSONDataWithTemplate, useV1Provider),
|
||||||
compose("with v1 kv provider", f, common.DataPropertyDockerconfigJSON, useV1Provider),
|
compose(withV1, f, common.DataPropertyDockerconfigJSON, useV1Provider),
|
||||||
compose("with v1 kv provider", f, common.JSONDataWithoutTargetName, useTokenAuth),
|
compose(withV1, f, common.JSONDataWithoutTargetName, useTokenAuth),
|
||||||
// use jwt provider
|
// use jwt provider
|
||||||
compose("with jwt provider", f, common.JSONDataFromSync, useJWTProvider),
|
compose(withJWT, f, common.JSONDataFromSync, useJWTProvider),
|
||||||
compose("with jwt provider", f, common.JSONDataWithProperty, useJWTProvider),
|
compose(withJWT, f, common.JSONDataWithProperty, useJWTProvider),
|
||||||
compose("with jwt provider", f, common.JSONDataWithTemplate, useJWTProvider),
|
compose(withJWT, f, common.JSONDataWithTemplate, useJWTProvider),
|
||||||
compose("with jwt provider", f, common.DataPropertyDockerconfigJSON, useJWTProvider),
|
compose(withJWT, f, common.DataPropertyDockerconfigJSON, useJWTProvider),
|
||||||
compose("with jwt provider", f, common.JSONDataWithoutTargetName, useTokenAuth),
|
compose(withJWT, f, common.JSONDataWithoutTargetName, useTokenAuth),
|
||||||
// use kubernetes provider
|
// use kubernetes provider
|
||||||
compose("with kubernetes provider", f, common.JSONDataFromSync, useKubernetesProvider),
|
compose(withK8s, f, common.JSONDataFromSync, useKubernetesProvider),
|
||||||
compose("with kubernetes provider", f, common.JSONDataWithProperty, useKubernetesProvider),
|
compose(withK8s, f, common.JSONDataWithProperty, useKubernetesProvider),
|
||||||
compose("with kubernetes provider", f, common.JSONDataWithTemplate, useKubernetesProvider),
|
compose(withK8s, f, common.JSONDataWithTemplate, useKubernetesProvider),
|
||||||
compose("with kubernetes provider", f, common.DataPropertyDockerconfigJSON, useKubernetesProvider),
|
compose(withK8s, f, common.DataPropertyDockerconfigJSON, useKubernetesProvider),
|
||||||
compose("with kubernetes provider", f, common.JSONDataWithoutTargetName, useTokenAuth),
|
compose(withK8s, f, common.JSONDataWithoutTargetName, useTokenAuth),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
22
go.mod
22
go.mod
|
@ -33,20 +33,23 @@ replace (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.65.0
|
cloud.google.com/go v0.81.0
|
||||||
github.com/Azure/azure-sdk-for-go v54.1.0+incompatible
|
github.com/Azure/azure-sdk-for-go v54.1.0+incompatible
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.7
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.7
|
||||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||||
github.com/IBM/go-sdk-core/v5 v5.5.0
|
github.com/IBM/go-sdk-core/v5 v5.5.0
|
||||||
github.com/IBM/secrets-manager-go-sdk v1.0.23
|
github.com/IBM/secrets-manager-go-sdk v1.0.23
|
||||||
|
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2
|
||||||
|
github.com/akeylesslabs/akeyless-go/v2 v2.5.11
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1192
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1192
|
||||||
github.com/aws/aws-sdk-go v1.38.6
|
github.com/aws/aws-sdk-go v1.38.6
|
||||||
github.com/crossplane/crossplane-runtime v0.13.0
|
github.com/crossplane/crossplane-runtime v0.13.0
|
||||||
github.com/fatih/color v1.10.0 // indirect
|
github.com/fatih/color v1.10.0 // indirect
|
||||||
github.com/frankban/quicktest v1.10.0 // indirect
|
github.com/frankban/quicktest v1.10.0 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||||
github.com/go-logr/logr v0.4.0
|
github.com/go-logr/logr v0.4.0
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||||
github.com/google/go-cmp v0.5.5
|
github.com/google/go-cmp v0.5.5
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
|
@ -57,7 +60,7 @@ require (
|
||||||
github.com/hashicorp/vault/api v1.0.5-0.20210224012239-b540be4b7ec4
|
github.com/hashicorp/vault/api v1.0.5-0.20210224012239-b540be4b7ec4
|
||||||
github.com/kr/pretty v0.2.1 // indirect
|
github.com/kr/pretty v0.2.1 // indirect
|
||||||
github.com/lestrrat-go/jwx v1.2.1
|
github.com/lestrrat-go/jwx v1.2.1
|
||||||
github.com/onsi/ginkgo v1.16.4
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/gomega v1.16.0
|
github.com/onsi/gomega v1.16.0
|
||||||
github.com/oracle/oci-go-sdk/v45 v45.2.0
|
github.com/oracle/oci-go-sdk/v45 v45.2.0
|
||||||
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
|
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
|
||||||
|
@ -65,7 +68,7 @@ require (
|
||||||
github.com/prometheus/client_model v0.2.0
|
github.com/prometheus/client_model v0.2.0
|
||||||
github.com/spf13/cobra v1.1.3 // indirect
|
github.com/spf13/cobra v1.1.3 // indirect
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/tidwall/gjson v1.7.5
|
github.com/tidwall/gjson v1.12.1
|
||||||
github.com/xanzy/go-gitlab v0.50.1
|
github.com/xanzy/go-gitlab v0.50.1
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20210809082946-a97da516c588
|
github.com/yandex-cloud/go-genproto v0.0.0-20210809082946-a97da516c588
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20210809100642-c13c40a429fa
|
github.com/yandex-cloud/go-sdk v0.0.0-20210809100642-c13c40a429fa
|
||||||
|
@ -73,11 +76,12 @@ require (
|
||||||
go.uber.org/zap v1.17.0
|
go.uber.org/zap v1.17.0
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 // indirect
|
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c
|
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78
|
||||||
golang.org/x/tools v0.1.2-0.20210512205948-8287d5da45e4 // indirect
|
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c // indirect
|
||||||
google.golang.org/api v0.30.0
|
golang.org/x/tools v0.1.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a
|
google.golang.org/api v0.45.0
|
||||||
google.golang.org/grpc v1.31.0
|
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3
|
||||||
|
google.golang.org/grpc v1.37.0
|
||||||
honnef.co/go/tools v0.1.4 // indirect
|
honnef.co/go/tools v0.1.4 // indirect
|
||||||
k8s.io/api v0.21.3
|
k8s.io/api v0.21.3
|
||||||
k8s.io/apimachinery v0.21.3
|
k8s.io/apimachinery v0.21.3
|
||||||
|
|
119
go.sum
119
go.sum
|
@ -11,8 +11,13 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
|
||||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
|
|
||||||
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.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||||
|
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=
|
||||||
|
@ -72,6 +77,10 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2 h1:1h4udX3Y5KgSG0m4Th2bHfaYxZB9fbngiij9PrKEp6c=
|
||||||
|
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.2/go.mod h1:ionnWiARf5TYoFGuHS7syh51/7lYosZejpZbnECFJcU=
|
||||||
|
github.com/akeylesslabs/akeyless-go/v2 v2.5.11 h1:Z7xJAUPk1h4/YGS7yr/nx9RSMaSESzPwClqe21WFYHo=
|
||||||
|
github.com/akeylesslabs/akeyless-go/v2 v2.5.11/go.mod h1:uOdXD49NCCe4rexeSc2aBU5Qv4KZgJE6YlbtYalvb+I=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
@ -92,6 +101,8 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
|
||||||
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
||||||
github.com/aws/aws-sdk-go v1.38.6 h1:h0AKIaz/A1kEJ50HxCv7tL1GW+KbxYbp75+lZ/nvFOI=
|
github.com/aws/aws-sdk-go v1.38.6 h1:h0AKIaz/A1kEJ50HxCv7tL1GW+KbxYbp75+lZ/nvFOI=
|
||||||
github.com/aws/aws-sdk-go v1.38.6/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.38.6/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v0.23.0 h1:+E1q1LLSfHSDn/DzOtdJOX+pLZE2HiNV2yO5AjZINwM=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v0.23.0/go.mod h1:2LhT7UgHOXK3UXONKI5OMgIyoQL6zTAw/jwIeX6yqzw=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
@ -168,8 +179,9 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
|
||||||
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
|
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
|
||||||
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||||
|
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
|
@ -210,6 +222,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||||
|
@ -248,8 +261,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
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-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
@ -265,6 +278,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=
|
||||||
|
@ -280,6 +294,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||||
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.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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
|
@ -294,6 +309,7 @@ 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.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
@ -305,6 +321,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.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/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
@ -312,6 +329,10 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/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-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-20201203190320-1bf35d6f28c2/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.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
@ -394,6 +415,7 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:W
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
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/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||||
|
@ -533,8 +555,9 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
|
||||||
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
@ -645,13 +668,13 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tidwall/gjson v1.7.5 h1:zmAN/xmX7OtpAkv4Ovfso60r/BiCi5IErCDYGNJu+uc=
|
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
||||||
github.com/tidwall/gjson v1.7.5/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||||
|
@ -676,7 +699,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||||
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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
|
@ -687,8 +710,10 @@ 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 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
|
|
||||||
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.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||||
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
@ -747,8 +772,9 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
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 h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
|
||||||
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 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/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=
|
||||||
|
@ -758,6 +784,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
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.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/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.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
@ -800,19 +827,30 @@ 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-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/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-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-20201110031124-69a78807bb2b/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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
||||||
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
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-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/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=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c h1:HiAZXo96zOhVhtFHchj/ojzoxCFiPrp9/j0GtS38V3g=
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c/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-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-20210413134643-5e61552d6c78 h1:rPRtHfUb0UKZeZ6GH4K4Nt4YRbE9V1u+QZX5upZXqJQ=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/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=
|
||||||
|
@ -875,17 +913,26 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
|
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c h1:QOfDMdrf/UwlVR0UBq2Mpr58UzNtvgJRXA4BgPfFACs=
|
||||||
|
golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
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-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||||
|
@ -963,13 +1010,18 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
||||||
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=
|
||||||
|
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
|
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/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.2-0.20210512205948-8287d5da45e4 h1:cYSqdOzmV9wJ7lWurRAws06Dmif0Wv6UL4gQLlz+im0=
|
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||||
golang.org/x/tools v0.1.2-0.20210512205948-8287d5da45e4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
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=
|
||||||
|
@ -993,8 +1045,14 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
|
||||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w=
|
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
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.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.45.0 h1:pqMffJFLBVUDIoYsHcqtxgQVTsmxMDpYLOc5MT4Jrww=
|
||||||
|
google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA=
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.3.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.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=
|
||||||
|
@ -1033,9 +1091,20 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
|
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/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-20201214200347-8c77b98c765d/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-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-20210413151531-c14fb6ef47c3 h1:K+7Ig5hjiLVA/i1UFUUbCGimWz5/Ey0lAQjT3QiLaPY=
|
||||||
|
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
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=
|
||||||
|
|
|
@ -45,6 +45,7 @@ nav:
|
||||||
- Secrets Manager: provider-google-secrets-manager.md
|
- Secrets Manager: provider-google-secrets-manager.md
|
||||||
- IBM:
|
- IBM:
|
||||||
- Secrets Manager: provider-ibm-secrets-manager.md
|
- Secrets Manager: provider-ibm-secrets-manager.md
|
||||||
|
- Akeyless: provider-akeyless.md
|
||||||
- HashiCorp Vault: provider-hashicorp-vault.md
|
- HashiCorp Vault: provider-hashicorp-vault.md
|
||||||
- Yandex:
|
- Yandex:
|
||||||
- Lockbox: provider-yandex-lockbox.md
|
- Lockbox: provider-yandex-lockbox.md
|
||||||
|
|
|
@ -238,11 +238,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||||
}
|
}
|
||||||
|
|
||||||
conditionSynced := NewExternalSecretCondition(esv1alpha1.ExternalSecretReady, v1.ConditionTrue, esv1alpha1.ConditionReasonSecretSynced, "Secret was synced")
|
conditionSynced := NewExternalSecretCondition(esv1alpha1.ExternalSecretReady, v1.ConditionTrue, esv1alpha1.ConditionReasonSecretSynced, "Secret was synced")
|
||||||
|
currCond := GetExternalSecretCondition(externalSecret.Status, esv1alpha1.ExternalSecretReady)
|
||||||
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||||
externalSecret.Status.RefreshTime = metav1.NewTime(time.Now())
|
externalSecret.Status.RefreshTime = metav1.NewTime(time.Now())
|
||||||
externalSecret.Status.SyncedResourceVersion = getResourceVersion(externalSecret)
|
externalSecret.Status.SyncedResourceVersion = getResourceVersion(externalSecret)
|
||||||
syncCallsTotal.With(syncCallsMetricLabels).Inc()
|
syncCallsTotal.With(syncCallsMetricLabels).Inc()
|
||||||
log.V(1).Info("reconciled secret")
|
if currCond == nil || currCond.Status != conditionSynced.Status {
|
||||||
|
log.Info("reconciled secret") // Log once if on success in any verbosity
|
||||||
|
} else {
|
||||||
|
log.V(1).Info("reconciled secret") // Log all reconciliation cycles if higher verbosity applied
|
||||||
|
}
|
||||||
|
|
||||||
return ctrl.Result{
|
return ctrl.Result{
|
||||||
RequeueAfter: refreshInt,
|
RequeueAfter: refreshInt,
|
||||||
|
|
|
@ -134,6 +134,11 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
ExternalSecretName = "test-es"
|
ExternalSecretName = "test-es"
|
||||||
ExternalSecretStore = "test-store"
|
ExternalSecretStore = "test-store"
|
||||||
ExternalSecretTargetSecretName = "test-secret"
|
ExternalSecretTargetSecretName = "test-secret"
|
||||||
|
FakeManager = "fake.manager"
|
||||||
|
expectedSecretVal = "SOMEVALUE was templated"
|
||||||
|
targetPropObj = "{{ .targetProperty | toString | upper }} was templated"
|
||||||
|
FooValue = "map-foo-value"
|
||||||
|
BarValue = "map-bar-value"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ExternalSecretNamespace string
|
var ExternalSecretNamespace string
|
||||||
|
@ -283,13 +288,13 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
// create secret beforehand
|
// create secret beforehand
|
||||||
Expect(k8sClient.Create(context.Background(), &v1.Secret{
|
Expect(k8sClient.Create(context.Background(), &v1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-secret",
|
Name: ExternalSecretTargetSecretName,
|
||||||
Namespace: ExternalSecretNamespace,
|
Namespace: ExternalSecretNamespace,
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
existingKey: []byte(existingVal),
|
existingKey: []byte(existingVal),
|
||||||
},
|
},
|
||||||
}, client.FieldOwner("fake.manager"))).To(Succeed())
|
}, client.FieldOwner(FakeManager))).To(Succeed())
|
||||||
|
|
||||||
fakeProvider.WithGetSecret([]byte(secretVal), nil)
|
fakeProvider.WithGetSecret([]byte(secretVal), nil)
|
||||||
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
||||||
|
@ -309,7 +314,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
"external-secrets",
|
"external-secrets",
|
||||||
fmt.Sprintf("{\"f:data\":{\"f:targetProperty\":{}},\"f:immutable\":{},\"f:metadata\":{\"f:annotations\":{\"f:%s\":{}}}}", esv1alpha1.AnnotationDataHash)),
|
fmt.Sprintf("{\"f:data\":{\"f:targetProperty\":{}},\"f:immutable\":{},\"f:metadata\":{\"f:annotations\":{\"f:%s\":{}}}}", esv1alpha1.AnnotationDataHash)),
|
||||||
).To(BeTrue())
|
).To(BeTrue())
|
||||||
Expect(hasFieldOwnership(secret.ObjectMeta, "fake.manager", "{\"f:data\":{\".\":{},\"f:pre-existing-key\":{}},\"f:type\":{}}")).To(BeTrue())
|
Expect(hasFieldOwnership(secret.ObjectMeta, FakeManager, "{\"f:data\":{\".\":{},\"f:pre-existing-key\":{}},\"f:type\":{}}")).To(BeTrue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,13 +353,13 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
// create secret beforehand
|
// create secret beforehand
|
||||||
Expect(k8sClient.Create(context.Background(), &v1.Secret{
|
Expect(k8sClient.Create(context.Background(), &v1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-secret",
|
Name: ExternalSecretTargetSecretName,
|
||||||
Namespace: ExternalSecretNamespace,
|
Namespace: ExternalSecretNamespace,
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
existingKey: []byte(existingVal),
|
existingKey: []byte(existingVal),
|
||||||
},
|
},
|
||||||
}, client.FieldOwner("fake.manager"))).To(Succeed())
|
}, client.FieldOwner(FakeManager))).To(Succeed())
|
||||||
fakeProvider.WithGetSecret([]byte(secretVal), nil)
|
fakeProvider.WithGetSecret([]byte(secretVal), nil)
|
||||||
|
|
||||||
tc.checkCondition = func(es *esv1alpha1.ExternalSecret) bool {
|
tc.checkCondition = func(es *esv1alpha1.ExternalSecret) bool {
|
||||||
|
@ -373,7 +378,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
// check owner/managedFields
|
// check owner/managedFields
|
||||||
Expect(hasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretName)).To(BeFalse())
|
Expect(hasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretName)).To(BeFalse())
|
||||||
Expect(secret.ObjectMeta.ManagedFields).To(HaveLen(1))
|
Expect(secret.ObjectMeta.ManagedFields).To(HaveLen(1))
|
||||||
Expect(hasFieldOwnership(secret.ObjectMeta, "fake.manager", "{\"f:data\":{\".\":{},\"f:targetProperty\":{}},\"f:type\":{}}")).To(BeTrue())
|
Expect(hasFieldOwnership(secret.ObjectMeta, FakeManager, "{\"f:data\":{\".\":{},\"f:targetProperty\":{}},\"f:type\":{}}")).To(BeTrue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +386,6 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
// to construct a new secret: labels, annotations and type
|
// to construct a new secret: labels, annotations and type
|
||||||
syncWithTemplate := func(tc *testCase) {
|
syncWithTemplate := func(tc *testCase) {
|
||||||
const secretVal = "someValue"
|
const secretVal = "someValue"
|
||||||
const expectedSecretVal = "SOMEVALUE was templated"
|
|
||||||
const tplStaticKey = "tplstatickey"
|
const tplStaticKey = "tplstatickey"
|
||||||
const tplStaticVal = "tplstaticvalue"
|
const tplStaticVal = "tplstaticvalue"
|
||||||
tc.externalSecret.ObjectMeta.Labels = map[string]string{
|
tc.externalSecret.ObjectMeta.Labels = map[string]string{
|
||||||
|
@ -401,7 +405,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
},
|
},
|
||||||
Type: v1.SecretTypeOpaque,
|
Type: v1.SecretTypeOpaque,
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
targetProp: "{{ .targetProperty | toString | upper }} was templated",
|
targetProp: targetPropObj,
|
||||||
tplStaticKey: tplStaticVal,
|
tplStaticKey: tplStaticVal,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -426,7 +430,6 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
// * dataFrom
|
// * dataFrom
|
||||||
syncWithTemplatePrecedence := func(tc *testCase) {
|
syncWithTemplatePrecedence := func(tc *testCase) {
|
||||||
const secretVal = "someValue"
|
const secretVal = "someValue"
|
||||||
const expectedSecretVal = "SOMEVALUE was templated"
|
|
||||||
const tplStaticKey = "tplstatickey"
|
const tplStaticKey = "tplstatickey"
|
||||||
const tplStaticVal = "tplstaticvalue"
|
const tplStaticVal = "tplstaticvalue"
|
||||||
const tplFromCMName = "template-cm"
|
const tplFromCMName = "template-cm"
|
||||||
|
@ -480,7 +483,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
},
|
},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
// this should be the data value, not dataFrom
|
// this should be the data value, not dataFrom
|
||||||
targetProp: "{{ .targetProperty | toString | upper }} was templated",
|
targetProp: targetPropObj,
|
||||||
// this should use the value from the map
|
// this should use the value from the map
|
||||||
"bar": "value from map: {{ .bar | toString }}",
|
"bar": "value from map: {{ .bar | toString }}",
|
||||||
// just a static value
|
// just a static value
|
||||||
|
@ -494,8 +497,8 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
}
|
}
|
||||||
fakeProvider.WithGetSecret([]byte(secretVal), nil)
|
fakeProvider.WithGetSecret([]byte(secretVal), nil)
|
||||||
fakeProvider.WithGetSecretMap(map[string][]byte{
|
fakeProvider.WithGetSecretMap(map[string][]byte{
|
||||||
"targetProperty": []byte("map-foo-value"),
|
"targetProperty": []byte(FooValue),
|
||||||
"bar": []byte("map-bar-value"),
|
"bar": []byte(BarValue),
|
||||||
}, nil)
|
}, nil)
|
||||||
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
||||||
// check values
|
// check values
|
||||||
|
@ -509,7 +512,6 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
|
|
||||||
refreshWithTemplate := func(tc *testCase) {
|
refreshWithTemplate := func(tc *testCase) {
|
||||||
const secretVal = "someValue"
|
const secretVal = "someValue"
|
||||||
const expectedSecretVal = "SOMEVALUE was templated"
|
|
||||||
const tplStaticKey = "tplstatickey"
|
const tplStaticKey = "tplstatickey"
|
||||||
const tplStaticVal = "tplstaticvalue"
|
const tplStaticVal = "tplstaticvalue"
|
||||||
tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
|
tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
|
||||||
|
@ -520,7 +522,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
},
|
},
|
||||||
Type: v1.SecretTypeOpaque,
|
Type: v1.SecretTypeOpaque,
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
targetProp: "{{ .targetProperty | toString | upper }} was templated",
|
targetProp: targetPropObj,
|
||||||
tplStaticKey: tplStaticVal,
|
tplStaticKey: tplStaticVal,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -660,13 +662,13 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fakeProvider.WithGetSecretMap(map[string][]byte{
|
fakeProvider.WithGetSecretMap(map[string][]byte{
|
||||||
"foo": []byte("map-foo-value"),
|
"foo": []byte(FooValue),
|
||||||
"bar": []byte("map-bar-value"),
|
"bar": []byte(BarValue),
|
||||||
}, nil)
|
}, nil)
|
||||||
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
||||||
// check values
|
// check values
|
||||||
Expect(string(secret.Data["foo"])).To(Equal("map-foo-value"))
|
Expect(string(secret.Data["foo"])).To(Equal(FooValue))
|
||||||
Expect(string(secret.Data["bar"])).To(Equal("map-bar-value"))
|
Expect(string(secret.Data["bar"])).To(Equal(BarValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,14 +689,14 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fakeProvider.WithGetSecretMap(map[string][]byte{
|
fakeProvider.WithGetSecretMap(map[string][]byte{
|
||||||
"tls.crt": []byte("map-foo-value"),
|
"tls.crt": []byte(FooValue),
|
||||||
"tls.key": []byte("map-bar-value"),
|
"tls.key": []byte(BarValue),
|
||||||
}, nil)
|
}, nil)
|
||||||
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
tc.checkSecret = func(es *esv1alpha1.ExternalSecret, secret *v1.Secret) {
|
||||||
Expect(secret.Type).To(Equal(v1.SecretTypeTLS))
|
Expect(secret.Type).To(Equal(v1.SecretTypeTLS))
|
||||||
// check values
|
// check values
|
||||||
Expect(string(secret.Data["tls.crt"])).To(Equal("map-foo-value"))
|
Expect(string(secret.Data["tls.crt"])).To(Equal(FooValue))
|
||||||
Expect(string(secret.Data["tls.key"])).To(Equal("map-bar-value"))
|
Expect(string(secret.Data["tls.key"])).To(Equal(BarValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -851,7 +853,7 @@ var _ = Describe("ExternalSecret controller", func() {
|
||||||
// When we amend the created kind=secret, refresh operation should be run again regardless of refresh interval
|
// When we amend the created kind=secret, refresh operation should be run again regardless of refresh interval
|
||||||
checkSecretDataHashAnnotationChange := func(tc *testCase) {
|
checkSecretDataHashAnnotationChange := func(tc *testCase) {
|
||||||
fakeData := map[string][]byte{
|
fakeData := map[string][]byte{
|
||||||
"targetProperty": []byte("map-foo-value"),
|
"targetProperty": []byte(FooValue),
|
||||||
}
|
}
|
||||||
fakeProvider.WithGetSecretMap(fakeData, nil)
|
fakeProvider.WithGetSecretMap(fakeData, nil)
|
||||||
tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Minute * 10}
|
tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Minute * 10}
|
||||||
|
|
155
pkg/provider/akeyless/akeyless.go
Normal file
155
pkg/provider/akeyless/akeyless.go
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akeyless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/akeylesslabs/akeyless-go/v2"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||||
|
"github.com/external-secrets/external-secrets/pkg/provider"
|
||||||
|
"github.com/external-secrets/external-secrets/pkg/provider/schema"
|
||||||
|
"github.com/external-secrets/external-secrets/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultAPIUrl = "https://api.akeyless.io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider satisfies the provider interface.
|
||||||
|
type Provider struct{}
|
||||||
|
|
||||||
|
// akeylessBase satisfies the provider.SecretsClient interface.
|
||||||
|
type akeylessBase struct {
|
||||||
|
kube client.Client
|
||||||
|
store esv1alpha1.GenericStore
|
||||||
|
namespace string
|
||||||
|
|
||||||
|
akeylessGwAPIURL string
|
||||||
|
RestAPI *akeyless.V2ApiService
|
||||||
|
}
|
||||||
|
|
||||||
|
type Akeyless struct {
|
||||||
|
Client akeylessVaultInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
type akeylessVaultInterface interface {
|
||||||
|
GetSecretByType(secretName, token string, version int32) (string, error)
|
||||||
|
TokenFromSecretRef(ctx context.Context) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
schema.Register(&Provider{}, &esv1alpha1.SecretStoreProvider{
|
||||||
|
Akeyless: &esv1alpha1.AkeylessProvider{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient constructs a new secrets client based on the provided store.
|
||||||
|
func (p *Provider) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
|
||||||
|
return newClient(ctx, store, kube, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClient(_ context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
|
||||||
|
akl := &akeylessBase{
|
||||||
|
kube: kube,
|
||||||
|
store: store,
|
||||||
|
namespace: namespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
spec, err := GetAKeylessProvider(store)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
akeylessGwAPIURL := defaultAPIUrl
|
||||||
|
if spec != nil && spec.AkeylessGWApiURL != nil && *spec.AkeylessGWApiURL != "" {
|
||||||
|
akeylessGwAPIURL = getV2Url(*spec.AkeylessGWApiURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.Auth == nil {
|
||||||
|
return nil, fmt.Errorf("missing Auth in store config")
|
||||||
|
}
|
||||||
|
|
||||||
|
RestAPIClient := akeyless.NewAPIClient(&akeyless.Configuration{
|
||||||
|
Servers: []akeyless.ServerConfiguration{
|
||||||
|
{
|
||||||
|
URL: akeylessGwAPIURL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).V2Api
|
||||||
|
|
||||||
|
akl.akeylessGwAPIURL = akeylessGwAPIURL
|
||||||
|
akl.RestAPI = RestAPIClient
|
||||||
|
return &Akeyless{Client: akl}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Akeyless) Close(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements store.Client.GetSecret Interface.
|
||||||
|
// Retrieves a secret with the secret name defined in ref.Name.
|
||||||
|
func (a *Akeyless) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||||
|
if utils.IsNil(a.Client) {
|
||||||
|
return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := a.Client.TokenFromSecretRef(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
version := int32(0)
|
||||||
|
if ref.Version != "" {
|
||||||
|
i, err := strconv.ParseInt(ref.Version, 10, 32)
|
||||||
|
if err == nil {
|
||||||
|
version = int32(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value, err := a.Client.GetSecretByType(ref.Key, token, version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(value), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements store.Client.GetSecretMap Interface.
|
||||||
|
// New version of GetSecretMap.
|
||||||
|
func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
|
||||||
|
if utils.IsNil(a.Client) {
|
||||||
|
return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := a.GetSecret(ctx, ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Maps the json data to a string:string map
|
||||||
|
kv := make(map[string]string)
|
||||||
|
err = json.Unmarshal(val, &kv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts values in K:V pairs into bytes, while leaving keys as strings
|
||||||
|
secretData := make(map[string][]byte)
|
||||||
|
for k, v := range kv {
|
||||||
|
secretData[k] = []byte(v)
|
||||||
|
}
|
||||||
|
return secretData, nil
|
||||||
|
}
|
250
pkg/provider/akeyless/akeyless_api.go
Normal file
250
pkg/provider/akeyless/akeyless_api.go
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akeyless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
aws_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/aws"
|
||||||
|
azure_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/azure"
|
||||||
|
gcp_cloud_id "github.com/akeylesslabs/akeyless-go-cloud-id/cloudprovider/gcp"
|
||||||
|
"github.com/akeylesslabs/akeyless-go/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var apiErr akeyless.GenericOpenAPIError
|
||||||
|
|
||||||
|
const DefServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||||
|
|
||||||
|
func (a *akeylessBase) GetToken(accessID, accType, accTypeParam string) (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
authBody := akeyless.NewAuthWithDefaults()
|
||||||
|
authBody.AccessId = akeyless.PtrString(accessID)
|
||||||
|
if accType == "api_key" || accType == "access_key" {
|
||||||
|
authBody.AccessKey = akeyless.PtrString(accTypeParam)
|
||||||
|
} else if accType == "k8s" {
|
||||||
|
jwtString, err := readK8SServiceAccountJWT()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read JWT with Kubernetes Auth from %v. error: %w", DefServiceAccountFile, err)
|
||||||
|
}
|
||||||
|
K8SAuthConfigName := accTypeParam
|
||||||
|
authBody.AccessType = akeyless.PtrString(accType)
|
||||||
|
authBody.K8sServiceAccountToken = akeyless.PtrString(jwtString)
|
||||||
|
authBody.K8sAuthConfigName = akeyless.PtrString(K8SAuthConfigName)
|
||||||
|
} else {
|
||||||
|
cloudID, err := a.getCloudID(accType, accTypeParam)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Require Cloud ID " + err.Error())
|
||||||
|
}
|
||||||
|
authBody.AccessType = akeyless.PtrString(accType)
|
||||||
|
authBody.CloudId = akeyless.PtrString(cloudID)
|
||||||
|
}
|
||||||
|
|
||||||
|
authOut, _, err := a.RestAPI.Auth(ctx).Body(*authBody).Execute()
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &apiErr) {
|
||||||
|
return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("authentication failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
token := authOut.GetToken()
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessBase) GetSecretByType(secretName, token string, version int32) (string, error) {
|
||||||
|
item, err := a.DescribeItem(secretName, token)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
secretType := item.GetItemType()
|
||||||
|
|
||||||
|
switch secretType {
|
||||||
|
case "STATIC_SECRET":
|
||||||
|
return a.GetStaticSecret(secretName, token, version)
|
||||||
|
case "DYNAMIC_SECRET":
|
||||||
|
return a.GetDynamicSecrets(secretName, token)
|
||||||
|
case "ROTATED_SECRET":
|
||||||
|
return a.GetRotatedSecrets(secretName, token, version)
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("invalid item type: %v", secretType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessBase) DescribeItem(itemName, token string) (*akeyless.Item, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
body := akeyless.DescribeItem{
|
||||||
|
Name: itemName,
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(token, "u-") {
|
||||||
|
body.UidToken = &token
|
||||||
|
} else {
|
||||||
|
body.Token = &token
|
||||||
|
}
|
||||||
|
gsvOut, _, err := a.RestAPI.DescribeItem(ctx).Body(body).Execute()
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &apiErr) {
|
||||||
|
return nil, fmt.Errorf("can't describe item: %v", string(apiErr.Body()))
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("can't describe item: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gsvOut, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessBase) GetRotatedSecrets(secretName, token string, version int32) (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
body := akeyless.GetRotatedSecretValue{
|
||||||
|
Names: secretName,
|
||||||
|
Version: &version,
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(token, "u-") {
|
||||||
|
body.UidToken = &token
|
||||||
|
} else {
|
||||||
|
body.Token = &token
|
||||||
|
}
|
||||||
|
|
||||||
|
gsvOut, _, err := a.RestAPI.GetRotatedSecretValue(ctx).Body(body).Execute()
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &apiErr) {
|
||||||
|
return "", fmt.Errorf("can't get rotated secret value: %v", string(apiErr.Body()))
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("can't get rotated secret value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok := gsvOut["value"]
|
||||||
|
if ok {
|
||||||
|
if _, ok := val["payload"]; ok {
|
||||||
|
return fmt.Sprintf("%v", val["payload"]), nil
|
||||||
|
} else if _, ok := val["target_value"]; ok {
|
||||||
|
out, err := json.Marshal(val["target_value"])
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
|
||||||
|
}
|
||||||
|
return string(out), nil
|
||||||
|
} else {
|
||||||
|
out, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
|
||||||
|
}
|
||||||
|
return string(out), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out, err := json.Marshal(gsvOut)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
|
||||||
|
}
|
||||||
|
return string(out), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessBase) GetDynamicSecrets(secretName, token string) (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
body := akeyless.GetDynamicSecretValue{
|
||||||
|
Name: secretName,
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(token, "u-") {
|
||||||
|
body.UidToken = &token
|
||||||
|
} else {
|
||||||
|
body.Token = &token
|
||||||
|
}
|
||||||
|
|
||||||
|
gsvOut, _, err := a.RestAPI.GetDynamicSecretValue(ctx).Body(body).Execute()
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &apiErr) {
|
||||||
|
return "", fmt.Errorf("can't get dynamic secret value: %v", string(apiErr.Body()))
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("can't get dynamic secret value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := json.Marshal(gsvOut)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("can't marshal dynamic secret value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(out), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessBase) GetStaticSecret(secretName, token string, version int32) (string, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
gsvBody := akeyless.GetSecretValue{
|
||||||
|
Names: []string{secretName},
|
||||||
|
Version: &version,
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(token, "u-") {
|
||||||
|
gsvBody.UidToken = &token
|
||||||
|
} else {
|
||||||
|
gsvBody.Token = &token
|
||||||
|
}
|
||||||
|
|
||||||
|
gsvOut, _, err := a.RestAPI.GetSecretValue(ctx).Body(gsvBody).Execute()
|
||||||
|
if err != nil {
|
||||||
|
if errors.As(err, &apiErr) {
|
||||||
|
return "", fmt.Errorf("can't get secret value: %v", string(apiErr.Body()))
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("can't get secret value: %w", err)
|
||||||
|
}
|
||||||
|
val, ok := gsvOut[secretName]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("can't get secret: %v", secretName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *akeylessBase) getCloudID(provider, accTypeParam string) (string, error) {
|
||||||
|
var cloudID string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch provider {
|
||||||
|
case "azure_ad":
|
||||||
|
cloudID, err = azure_cloud_id.GetCloudId(accTypeParam)
|
||||||
|
case "aws_iam":
|
||||||
|
cloudID, err = aws_cloud_id.GetCloudId()
|
||||||
|
case "gcp":
|
||||||
|
cloudID, err = gcp_cloud_id.GetCloudID(accTypeParam)
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unable to determine provider: %s", provider)
|
||||||
|
}
|
||||||
|
return cloudID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// readK8SServiceAccountJWT reads the JWT data for the Agent to submit to Akeyless Gateway.
|
||||||
|
func readK8SServiceAccountJWT() (string, error) {
|
||||||
|
data, err := os.Open(DefServiceAccountFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer data.Close()
|
||||||
|
|
||||||
|
contentBytes, err := ioutil.ReadAll(data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := strings.TrimSpace(string(contentBytes))
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(a)), nil
|
||||||
|
}
|
168
pkg/provider/akeyless/akeyless_test.go
Normal file
168
pkg/provider/akeyless/akeyless_test.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package akeyless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||||
|
fakeakeyless "github.com/external-secrets/external-secrets/pkg/provider/akeyless/fake"
|
||||||
|
)
|
||||||
|
|
||||||
|
type akeylessTestCase struct {
|
||||||
|
mockClient *fakeakeyless.AkeylessMockClient
|
||||||
|
apiInput *fakeakeyless.Input
|
||||||
|
apiOutput *fakeakeyless.Output
|
||||||
|
ref *esv1alpha1.ExternalSecretDataRemoteRef
|
||||||
|
expectError string
|
||||||
|
expectedSecret string
|
||||||
|
// for testing secretmap
|
||||||
|
expectedData map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValidAkeylessTestCase() *akeylessTestCase {
|
||||||
|
smtc := akeylessTestCase{
|
||||||
|
mockClient: &fakeakeyless.AkeylessMockClient{},
|
||||||
|
apiInput: makeValidInput(),
|
||||||
|
ref: makeValidRef(),
|
||||||
|
apiOutput: makeValidOutput(),
|
||||||
|
expectError: "",
|
||||||
|
expectedSecret: "",
|
||||||
|
expectedData: map[string][]byte{},
|
||||||
|
}
|
||||||
|
smtc.mockClient.WithValue(smtc.apiInput, smtc.apiOutput)
|
||||||
|
return &smtc
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValidRef() *esv1alpha1.ExternalSecretDataRemoteRef {
|
||||||
|
return &esv1alpha1.ExternalSecretDataRemoteRef{
|
||||||
|
Key: "test-secret",
|
||||||
|
Version: "1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValidInput() *fakeakeyless.Input {
|
||||||
|
return &fakeakeyless.Input{
|
||||||
|
SecretName: "name",
|
||||||
|
Version: 0,
|
||||||
|
Token: "token",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValidOutput() *fakeakeyless.Output {
|
||||||
|
return &fakeakeyless.Output{
|
||||||
|
Value: "secret-val",
|
||||||
|
Err: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValidAkeylessTestCaseCustom(tweaks ...func(smtc *akeylessTestCase)) *akeylessTestCase {
|
||||||
|
smtc := makeValidAkeylessTestCase()
|
||||||
|
for _, fn := range tweaks {
|
||||||
|
fn(smtc)
|
||||||
|
}
|
||||||
|
smtc.mockClient.WithValue(smtc.apiInput, smtc.apiOutput)
|
||||||
|
return smtc
|
||||||
|
}
|
||||||
|
|
||||||
|
// This case can be shared by both GetSecret and GetSecretMap tests.
|
||||||
|
// bad case: set apiErr.
|
||||||
|
var setAPIErr = func(smtc *akeylessTestCase) {
|
||||||
|
smtc.apiOutput.Err = fmt.Errorf("oh no")
|
||||||
|
smtc.expectError = "oh no"
|
||||||
|
}
|
||||||
|
|
||||||
|
var setNilMockClient = func(smtc *akeylessTestCase) {
|
||||||
|
smtc.mockClient = nil
|
||||||
|
smtc.expectError = errUninitalizedAkeylessProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAkeylessGetSecret(t *testing.T) {
|
||||||
|
secretValue := "changedvalue"
|
||||||
|
// good case: default version is set
|
||||||
|
// key is passed in, output is sent back
|
||||||
|
setSecretString := func(smtc *akeylessTestCase) {
|
||||||
|
smtc.apiOutput = &fakeakeyless.Output{
|
||||||
|
Value: secretValue,
|
||||||
|
Err: nil,
|
||||||
|
}
|
||||||
|
smtc.expectedSecret = secretValue
|
||||||
|
}
|
||||||
|
|
||||||
|
successCases := []*akeylessTestCase{
|
||||||
|
makeValidAkeylessTestCaseCustom(setAPIErr),
|
||||||
|
makeValidAkeylessTestCaseCustom(setSecretString),
|
||||||
|
makeValidAkeylessTestCaseCustom(setNilMockClient),
|
||||||
|
}
|
||||||
|
|
||||||
|
sm := Akeyless{}
|
||||||
|
for k, v := range successCases {
|
||||||
|
sm.Client = v.mockClient
|
||||||
|
fmt.Println(*v.ref)
|
||||||
|
out, err := sm.GetSecret(context.Background(), *v.ref)
|
||||||
|
if !ErrorContains(err, v.expectError) {
|
||||||
|
t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
|
||||||
|
}
|
||||||
|
if string(out) != v.expectedSecret {
|
||||||
|
t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSecretMap(t *testing.T) {
|
||||||
|
// good case: default version & deserialization
|
||||||
|
setDeserialization := func(smtc *akeylessTestCase) {
|
||||||
|
smtc.apiOutput.Value = `{"foo":"bar"}`
|
||||||
|
smtc.expectedData["foo"] = []byte("bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad case: invalid json
|
||||||
|
setInvalidJSON := func(smtc *akeylessTestCase) {
|
||||||
|
smtc.apiOutput.Value = `-----------------`
|
||||||
|
smtc.expectError = "unable to unmarshal secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
successCases := []*akeylessTestCase{
|
||||||
|
makeValidAkeylessTestCaseCustom(setDeserialization),
|
||||||
|
makeValidAkeylessTestCaseCustom(setInvalidJSON),
|
||||||
|
makeValidAkeylessTestCaseCustom(setAPIErr),
|
||||||
|
makeValidAkeylessTestCaseCustom(setNilMockClient),
|
||||||
|
}
|
||||||
|
|
||||||
|
sm := Akeyless{}
|
||||||
|
for k, v := range successCases {
|
||||||
|
sm.Client = v.mockClient
|
||||||
|
out, err := sm.GetSecretMap(context.Background(), *v.ref)
|
||||||
|
if !ErrorContains(err, v.expectError) {
|
||||||
|
t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
|
||||||
|
}
|
||||||
|
if err == nil && !reflect.DeepEqual(out, v.expectedData) {
|
||||||
|
t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorContains(out error, want string) bool {
|
||||||
|
if out == nil {
|
||||||
|
return want == ""
|
||||||
|
}
|
||||||
|
if want == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.Contains(out.Error(), want)
|
||||||
|
}
|
103
pkg/provider/akeyless/auth.go
Normal file
103
pkg/provider/akeyless/auth.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akeyless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterSecretStore: missing Akeyless AccessID Namespace"
|
||||||
|
errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing Akeyless AccessType Namespace"
|
||||||
|
errFetchAKIDSecret = "could not fetch accessID secret: %w"
|
||||||
|
errFetchSAKSecret = "could not fetch AccessType secret: %w"
|
||||||
|
errMissingSAK = "missing SecretAccessKey"
|
||||||
|
errMissingAKID = "missing AccessKeyID"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *akeylessBase) TokenFromSecretRef(ctx context.Context) (string, error) {
|
||||||
|
prov, err := GetAKeylessProvider(a.store)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ke := client.ObjectKey{
|
||||||
|
Name: prov.Auth.SecretRef.AccessID.Name,
|
||||||
|
Namespace: a.namespace, // default to ExternalSecret namespace
|
||||||
|
}
|
||||||
|
// only ClusterStore is allowed to set namespace (and then it's required)
|
||||||
|
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
|
||||||
|
if prov.Auth.SecretRef.AccessID.Namespace == nil {
|
||||||
|
return "", fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace)
|
||||||
|
}
|
||||||
|
ke.Namespace = *prov.Auth.SecretRef.AccessID.Namespace
|
||||||
|
}
|
||||||
|
accessIDSecret := v1.Secret{}
|
||||||
|
err = a.kube.Get(ctx, ke, &accessIDSecret)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(errFetchAKIDSecret, err)
|
||||||
|
}
|
||||||
|
ke = client.ObjectKey{
|
||||||
|
Name: prov.Auth.SecretRef.AccessType.Name,
|
||||||
|
Namespace: a.namespace, // default to ExternalSecret namespace
|
||||||
|
}
|
||||||
|
// only ClusterStore is allowed to set namespace (and then it's required)
|
||||||
|
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
|
||||||
|
if prov.Auth.SecretRef.AccessType.Namespace == nil {
|
||||||
|
return "", fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
|
||||||
|
}
|
||||||
|
ke.Namespace = *prov.Auth.SecretRef.AccessType.Namespace
|
||||||
|
}
|
||||||
|
accessTypeSecret := v1.Secret{}
|
||||||
|
err = a.kube.Get(ctx, ke, &accessTypeSecret)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(errFetchSAKSecret, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ke = client.ObjectKey{
|
||||||
|
Name: prov.Auth.SecretRef.AccessTypeParam.Name,
|
||||||
|
Namespace: a.namespace, // default to ExternalSecret namespace
|
||||||
|
}
|
||||||
|
// only ClusterStore is allowed to set namespace (and then it's required)
|
||||||
|
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
|
||||||
|
if prov.Auth.SecretRef.AccessType.Namespace == nil {
|
||||||
|
return "", fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
|
||||||
|
}
|
||||||
|
ke.Namespace = *prov.Auth.SecretRef.AccessType.Namespace
|
||||||
|
}
|
||||||
|
accessTypeParamSecret := v1.Secret{}
|
||||||
|
err = a.kube.Get(ctx, ke, &accessTypeParamSecret)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(errFetchSAKSecret, err)
|
||||||
|
}
|
||||||
|
accessID := string(accessIDSecret.Data[prov.Auth.SecretRef.AccessID.Key])
|
||||||
|
accessType := string(accessTypeSecret.Data[prov.Auth.SecretRef.AccessType.Key])
|
||||||
|
accessTypeParam := string(accessTypeSecret.Data[prov.Auth.SecretRef.AccessTypeParam.Key])
|
||||||
|
|
||||||
|
if accessID == "" {
|
||||||
|
return "", fmt.Errorf(errMissingSAK)
|
||||||
|
}
|
||||||
|
if accessType == "" {
|
||||||
|
return "", fmt.Errorf(errMissingAKID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.GetToken(accessID, accessType, accessTypeParam)
|
||||||
|
}
|
49
pkg/provider/akeyless/fake/fake.go
Normal file
49
pkg/provider/akeyless/fake/fake.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AkeylessMockClient struct {
|
||||||
|
getSecret func(secretName, token string, version int32) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *AkeylessMockClient) TokenFromSecretRef(ctx context.Context) (string, error) {
|
||||||
|
return "newToken", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *AkeylessMockClient) GetSecretByType(secretName, token string, version int32) (string, error) {
|
||||||
|
return mc.getSecret(secretName, token, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *AkeylessMockClient) WithValue(in *Input, out *Output) {
|
||||||
|
if mc != nil {
|
||||||
|
mc.getSecret = func(secretName, token string, version int32) (string, error) {
|
||||||
|
return out.Value, out.Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Input struct {
|
||||||
|
SecretName string
|
||||||
|
Token string
|
||||||
|
Version int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Output struct {
|
||||||
|
Value string
|
||||||
|
Err error
|
||||||
|
}
|
99
pkg/provider/akeyless/utils.go
Normal file
99
pkg/provider/akeyless/utils.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package akeyless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errNilStore = "found nil store"
|
||||||
|
errMissingStoreSpec = "store is missing spec"
|
||||||
|
errMissingProvider = "storeSpec is missing provider"
|
||||||
|
errInvalidProvider = "invalid provider spec. Missing Akeyless field in store %s"
|
||||||
|
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
|
||||||
|
errUninitalizedAkeylessProvider = "provider akeyless is not initialized"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetAKeylessProvider does the necessary nil checks and returns the akeyless provider or an error.
|
||||||
|
func GetAKeylessProvider(store esv1alpha1.GenericStore) (*esv1alpha1.AkeylessProvider, error) {
|
||||||
|
if store == nil {
|
||||||
|
return nil, fmt.Errorf(errNilStore)
|
||||||
|
}
|
||||||
|
spc := store.GetSpec()
|
||||||
|
if spc == nil {
|
||||||
|
return nil, fmt.Errorf(errMissingStoreSpec)
|
||||||
|
}
|
||||||
|
if spc.Provider == nil {
|
||||||
|
return nil, fmt.Errorf(errMissingProvider)
|
||||||
|
}
|
||||||
|
prov := spc.Provider.Akeyless
|
||||||
|
if prov == nil {
|
||||||
|
return nil, fmt.Errorf(errInvalidProvider, store.GetObjectMeta().String())
|
||||||
|
}
|
||||||
|
return prov, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getV2Url(path string) string {
|
||||||
|
// add check if not v2
|
||||||
|
rebody := sendReq(path)
|
||||||
|
if strings.Contains(rebody, "unknown command") {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(path, "/v2") {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
url, err := url.Parse(path)
|
||||||
|
if err != nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(url.Host, "/v2") {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
url.Host += "/v2"
|
||||||
|
p := url.Scheme + "://" + url.Host
|
||||||
|
if url.Port() != "" {
|
||||||
|
p = p + ":" + url.Port()
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendReq(url string) string {
|
||||||
|
req, err := http.NewRequest("POST", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return string(body)
|
||||||
|
}
|
|
@ -160,6 +160,9 @@ func sessionFromSecretRef(ctx context.Context, prov *esv1alpha1.AWSProvider, sto
|
||||||
|
|
||||||
func sessionFromServiceAccount(ctx context.Context, prov *esv1alpha1.AWSProvider, store esv1alpha1.GenericStore, kube client.Client, namespace string, jwtProvider jwtProviderFactory) (*credentials.Credentials, error) {
|
func sessionFromServiceAccount(ctx context.Context, prov *esv1alpha1.AWSProvider, store esv1alpha1.GenericStore, kube client.Client, namespace string, jwtProvider jwtProviderFactory) (*credentials.Credentials, error) {
|
||||||
if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
|
if store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
|
||||||
|
if prov.Auth.JWTAuth.ServiceAccountRef.Namespace == nil {
|
||||||
|
return nil, fmt.Errorf("serviceAccountRef has no Namespace field (mandatory for ClusterSecretStore specs)")
|
||||||
|
}
|
||||||
namespace = *prov.Auth.JWTAuth.ServiceAccountRef.Namespace
|
namespace = *prov.Auth.JWTAuth.ServiceAccountRef.Namespace
|
||||||
}
|
}
|
||||||
name := prov.Auth.JWTAuth.ServiceAccountRef.Name
|
name := prov.Auth.JWTAuth.ServiceAccountRef.Name
|
||||||
|
|
|
@ -38,16 +38,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
myServiceAcc = "my-service-account"
|
esNamespaceKey = "es-namespace"
|
||||||
otherNs = "other-ns"
|
platformTeamNsKey = "platform-team-ns"
|
||||||
|
myServiceAccountKey = "my-service-account"
|
||||||
|
otherNsName = "other-ns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewSession(t *testing.T) {
|
func TestNewSession(t *testing.T) {
|
||||||
const (
|
|
||||||
esNamespace = "es-namespace"
|
|
||||||
platformTeamNs = "platform-team-ns"
|
|
||||||
)
|
|
||||||
|
|
||||||
rows := []TestSessionRow{
|
rows := []TestSessionRow{
|
||||||
{
|
{
|
||||||
name: "nil store",
|
name: "nil store",
|
||||||
|
@ -271,7 +268,7 @@ func TestNewSession(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ClusterStore should use credentials from a specific namespace",
|
name: "ClusterStore should use credentials from a specific namespace",
|
||||||
namespace: esNamespace,
|
namespace: esNamespaceKey,
|
||||||
store: &esv1alpha1.ClusterSecretStore{
|
store: &esv1alpha1.ClusterSecretStore{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
|
APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
|
||||||
|
@ -284,12 +281,12 @@ func TestNewSession(t *testing.T) {
|
||||||
SecretRef: &esv1alpha1.AWSAuthSecretRef{
|
SecretRef: &esv1alpha1.AWSAuthSecretRef{
|
||||||
AccessKeyID: esmeta.SecretKeySelector{
|
AccessKeyID: esmeta.SecretKeySelector{
|
||||||
Name: "onesecret",
|
Name: "onesecret",
|
||||||
Namespace: aws.String(platformTeamNs),
|
Namespace: aws.String(platformTeamNsKey),
|
||||||
Key: "one",
|
Key: "one",
|
||||||
},
|
},
|
||||||
SecretAccessKey: esmeta.SecretKeySelector{
|
SecretAccessKey: esmeta.SecretKeySelector{
|
||||||
Name: "onesecret",
|
Name: "onesecret",
|
||||||
Namespace: aws.String(platformTeamNs),
|
Namespace: aws.String(platformTeamNsKey),
|
||||||
Key: "two",
|
Key: "two",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -302,7 +299,7 @@ func TestNewSession(t *testing.T) {
|
||||||
{
|
{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "onesecret",
|
Name: "onesecret",
|
||||||
Namespace: platformTeamNs,
|
Namespace: platformTeamNsKey,
|
||||||
},
|
},
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
"one": []byte("1111"),
|
"one": []byte("1111"),
|
||||||
|
@ -316,7 +313,7 @@ func TestNewSession(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "namespace is mandatory when using ClusterStore with SecretKeySelector",
|
name: "namespace is mandatory when using ClusterStore with SecretKeySelector",
|
||||||
namespace: esNamespace,
|
namespace: esNamespaceKey,
|
||||||
store: &esv1alpha1.ClusterSecretStore{
|
store: &esv1alpha1.ClusterSecretStore{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
|
APIVersion: esv1alpha1.ClusterSecretStoreKindAPIVersion,
|
||||||
|
@ -345,19 +342,19 @@ func TestNewSession(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jwt auth via cluster secret store",
|
name: "jwt auth via cluster secret store",
|
||||||
namespace: esNamespace,
|
namespace: esNamespaceKey,
|
||||||
sa: &v1.ServiceAccount{
|
sa: &v1.ServiceAccount{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: myServiceAcc,
|
Name: myServiceAccountKey,
|
||||||
Namespace: otherNs,
|
Namespace: otherNsName,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
roleARNAnnotation: "my-sa-role",
|
roleARNAnnotation: "my-sa-role",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jwtProvider: func(name, namespace, roleArn, region string) (credentials.Provider, error) {
|
jwtProvider: func(name, namespace, roleArn, region string) (credentials.Provider, error) {
|
||||||
assert.Equal(t, myServiceAcc, name)
|
assert.Equal(t, myServiceAccountKey, name)
|
||||||
assert.Equal(t, otherNs, namespace)
|
assert.Equal(t, otherNsName, namespace)
|
||||||
assert.Equal(t, "my-sa-role", roleArn)
|
assert.Equal(t, "my-sa-role", roleArn)
|
||||||
return fakesess.CredentialsProvider{
|
return fakesess.CredentialsProvider{
|
||||||
RetrieveFunc: func() (credentials.Value, error) {
|
RetrieveFunc: func() (credentials.Value, error) {
|
||||||
|
@ -382,8 +379,8 @@ func TestNewSession(t *testing.T) {
|
||||||
Auth: esv1alpha1.AWSAuth{
|
Auth: esv1alpha1.AWSAuth{
|
||||||
JWTAuth: &esv1alpha1.AWSJWTAuth{
|
JWTAuth: &esv1alpha1.AWSJWTAuth{
|
||||||
ServiceAccountRef: &esmeta.ServiceAccountSelector{
|
ServiceAccountRef: &esmeta.ServiceAccountSelector{
|
||||||
Name: myServiceAcc,
|
Name: myServiceAccountKey,
|
||||||
Namespace: aws.String(otherNs),
|
Namespace: aws.String(otherNsName),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -434,8 +431,8 @@ func testRow(t *testing.T, row TestSessionRow) {
|
||||||
}
|
}
|
||||||
err := kc.Create(context.Background(), &authv1.TokenRequest{
|
err := kc.Create(context.Background(), &authv1.TokenRequest{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: myServiceAcc,
|
Name: myServiceAccountKey,
|
||||||
Namespace: otherNs,
|
Namespace: otherNsName,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultObjType = "secret"
|
defaultObjType = "secret"
|
||||||
|
vaultResource = "https://vault.azure.net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Provider satisfies the provider interface.
|
// Provider satisfies the provider interface.
|
||||||
|
@ -74,15 +75,18 @@ func newClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.C
|
||||||
store: store,
|
store: store,
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
}
|
}
|
||||||
azClient, vaultURL, err := anAzure.newAzureClient(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
clientSet, err := anAzure.setAzureClientWithManagedIdentity()
|
||||||
return nil, err
|
if clientSet {
|
||||||
|
return anAzure, err
|
||||||
}
|
}
|
||||||
|
|
||||||
anAzure.baseClient = azClient
|
clientSet, err = anAzure.setAzureClientWithServicePrincipal(ctx)
|
||||||
anAzure.vaultURL = vaultURL
|
if clientSet {
|
||||||
return anAzure, nil
|
return anAzure, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("cannot initialize Azure Client: no valid authType was specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements store.Client.GetSecret Interface.
|
// Implements store.Client.GetSecret Interface.
|
||||||
|
@ -168,42 +172,75 @@ func (a *Azure) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretD
|
||||||
return nil, fmt.Errorf("unknown Azure Keyvault object Type for %s", secretName)
|
return nil, fmt.Errorf("unknown Azure Keyvault object Type for %s", secretName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Azure) newAzureClient(ctx context.Context) (*keyvault.BaseClient, string, error) {
|
func (a *Azure) setAzureClientWithManagedIdentity() (bool, error) {
|
||||||
spec := *a.store.GetSpec().Provider.AzureKV
|
spec := *a.store.GetSpec().Provider.AzureKV
|
||||||
tenantID := *spec.TenantID
|
|
||||||
vaultURL := *spec.VaultURL
|
|
||||||
|
|
||||||
if spec.AuthSecretRef == nil {
|
if *spec.AuthType != esv1alpha1.ManagedIdentity {
|
||||||
return nil, "", fmt.Errorf("missing clientID/clientSecret in store config")
|
return false, nil
|
||||||
}
|
|
||||||
clusterScoped := false
|
|
||||||
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
|
|
||||||
clusterScoped = true
|
|
||||||
}
|
|
||||||
if spec.AuthSecretRef.ClientID == nil || spec.AuthSecretRef.ClientSecret == nil {
|
|
||||||
return nil, "", fmt.Errorf("missing accessKeyID/secretAccessKey in store config")
|
|
||||||
}
|
|
||||||
cid, err := a.secretKeyRef(ctx, a.store.GetNamespace(), *spec.AuthSecretRef.ClientID, clusterScoped)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
csec, err := a.secretKeyRef(ctx, a.store.GetNamespace(), *spec.AuthSecretRef.ClientSecret, clusterScoped)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clientCredentialsConfig := kvauth.NewClientCredentialsConfig(cid, csec, tenantID)
|
msiConfig := kvauth.NewMSIConfig()
|
||||||
// the default resource api is the management URL and not the vault URL which we need for keyvault operations
|
msiConfig.Resource = vaultResource
|
||||||
clientCredentialsConfig.Resource = "https://vault.azure.net"
|
if spec.IdentityID != nil {
|
||||||
authorizer, err := clientCredentialsConfig.Authorizer()
|
msiConfig.ClientID = *spec.IdentityID
|
||||||
|
}
|
||||||
|
authorizer, err := msiConfig.Authorizer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
basicClient := keyvault.New()
|
basicClient := keyvault.New()
|
||||||
basicClient.Authorizer = authorizer
|
basicClient.Authorizer = authorizer
|
||||||
|
|
||||||
return &basicClient, vaultURL, nil
|
a.baseClient = basicClient
|
||||||
|
a.vaultURL = *spec.VaultURL
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Azure) setAzureClientWithServicePrincipal(ctx context.Context) (bool, error) {
|
||||||
|
spec := *a.store.GetSpec().Provider.AzureKV
|
||||||
|
|
||||||
|
if *spec.AuthType != esv1alpha1.ServicePrincipal {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.TenantID == nil {
|
||||||
|
return true, fmt.Errorf("missing tenantID in store config")
|
||||||
|
}
|
||||||
|
if spec.AuthSecretRef == nil {
|
||||||
|
return true, fmt.Errorf("missing clientID/clientSecret in store config")
|
||||||
|
}
|
||||||
|
if spec.AuthSecretRef.ClientID == nil || spec.AuthSecretRef.ClientSecret == nil {
|
||||||
|
return true, fmt.Errorf("missing accessKeyID/secretAccessKey in store config")
|
||||||
|
}
|
||||||
|
clusterScoped := false
|
||||||
|
if a.store.GetObjectKind().GroupVersionKind().Kind == esv1alpha1.ClusterSecretStoreKind {
|
||||||
|
clusterScoped = true
|
||||||
|
}
|
||||||
|
cid, err := a.secretKeyRef(ctx, a.store.GetNamespace(), *spec.AuthSecretRef.ClientID, clusterScoped)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
csec, err := a.secretKeyRef(ctx, a.store.GetNamespace(), *spec.AuthSecretRef.ClientSecret, clusterScoped)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCredentialsConfig := kvauth.NewClientCredentialsConfig(cid, csec, *spec.TenantID)
|
||||||
|
clientCredentialsConfig.Resource = vaultResource
|
||||||
|
authorizer, err := clientCredentialsConfig.Authorizer()
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
basicClient := keyvault.New()
|
||||||
|
basicClient.Authorizer = authorizer
|
||||||
|
|
||||||
|
a.baseClient = &basicClient
|
||||||
|
a.vaultURL = *spec.VaultURL
|
||||||
|
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Azure) secretKeyRef(ctx context.Context, namespace string, secretRef smmeta.SecretKeySelector, clusterScoped bool) (string, error) {
|
func (a *Azure) secretKeyRef(ctx context.Context, namespace string, secretRef smmeta.SecretKeySelector, clusterScoped bool) (string, error) {
|
||||||
|
|
|
@ -39,15 +39,46 @@ func newAzure() (Azure, *fake.AzureMock) {
|
||||||
return testAzure, azureMock
|
return testAzure, azureMock
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewClientNoCreds(t *testing.T) {
|
func TestNewClientManagedIdentityNoNeedForCredentials(t *testing.T) {
|
||||||
namespace := "internal"
|
namespace := "internal"
|
||||||
vaultURL := "https://local.vault.url"
|
vaultURL := "https://local.vault.url"
|
||||||
tenantID := "1234"
|
identityID := "1234"
|
||||||
|
authType := esv1alpha1.ManagedIdentity
|
||||||
store := esv1alpha1.SecretStore{
|
store := esv1alpha1.SecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
},
|
},
|
||||||
Spec: esv1alpha1.SecretStoreSpec{Provider: &esv1alpha1.SecretStoreProvider{AzureKV: &esv1alpha1.AzureKVProvider{
|
Spec: esv1alpha1.SecretStoreSpec{Provider: &esv1alpha1.SecretStoreProvider{AzureKV: &esv1alpha1.AzureKVProvider{
|
||||||
|
AuthType: &authType,
|
||||||
|
IdentityID: &identityID,
|
||||||
|
VaultURL: &vaultURL,
|
||||||
|
}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := schema.GetProvider(&store)
|
||||||
|
tassert.Nil(t, err, "the return err should be nil")
|
||||||
|
k8sClient := clientfake.NewClientBuilder().Build()
|
||||||
|
secretClient, err := provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
||||||
|
if err != nil {
|
||||||
|
// On non Azure environment, MSI auth not available, so this error should be returned
|
||||||
|
tassert.EqualError(t, err, "failed to get oauth token from MSI: MSI not available")
|
||||||
|
} else {
|
||||||
|
// On Azure (where GitHub Actions are running) a secretClient is returned, as only an Authorizer is configured, but no token is requested for MI
|
||||||
|
tassert.NotNil(t, secretClient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewClientNoCreds(t *testing.T) {
|
||||||
|
namespace := "internal"
|
||||||
|
vaultURL := "https://local.vault.url"
|
||||||
|
tenantID := "1234"
|
||||||
|
authType := esv1alpha1.ServicePrincipal
|
||||||
|
store := esv1alpha1.SecretStore{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: namespace,
|
||||||
|
},
|
||||||
|
Spec: esv1alpha1.SecretStoreSpec{Provider: &esv1alpha1.SecretStoreProvider{AzureKV: &esv1alpha1.AzureKVProvider{
|
||||||
|
AuthType: &authType,
|
||||||
VaultURL: &vaultURL,
|
VaultURL: &vaultURL,
|
||||||
TenantID: &tenantID,
|
TenantID: &tenantID,
|
||||||
}}},
|
}}},
|
||||||
|
@ -55,32 +86,27 @@ func TestNewClientNoCreds(t *testing.T) {
|
||||||
provider, err := schema.GetProvider(&store)
|
provider, err := schema.GetProvider(&store)
|
||||||
tassert.Nil(t, err, "the return err should be nil")
|
tassert.Nil(t, err, "the return err should be nil")
|
||||||
k8sClient := clientfake.NewClientBuilder().Build()
|
k8sClient := clientfake.NewClientBuilder().Build()
|
||||||
secretClient, err := provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
_, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "missing clientID/clientSecret in store config")
|
tassert.EqualError(t, err, "missing clientID/clientSecret in store config")
|
||||||
tassert.Nil(t, secretClient)
|
|
||||||
|
|
||||||
store.Spec.Provider.AzureKV.AuthSecretRef = &esv1alpha1.AzureKVAuth{}
|
store.Spec.Provider.AzureKV.AuthSecretRef = &esv1alpha1.AzureKVAuth{}
|
||||||
secretClient, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
_, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "missing accessKeyID/secretAccessKey in store config")
|
tassert.EqualError(t, err, "missing accessKeyID/secretAccessKey in store config")
|
||||||
tassert.Nil(t, secretClient)
|
|
||||||
|
|
||||||
store.Spec.Provider.AzureKV.AuthSecretRef.ClientID = &v1.SecretKeySelector{Name: "user"}
|
store.Spec.Provider.AzureKV.AuthSecretRef.ClientID = &v1.SecretKeySelector{Name: "user"}
|
||||||
secretClient, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
_, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "missing accessKeyID/secretAccessKey in store config")
|
tassert.EqualError(t, err, "missing accessKeyID/secretAccessKey in store config")
|
||||||
tassert.Nil(t, secretClient)
|
|
||||||
|
|
||||||
store.Spec.Provider.AzureKV.AuthSecretRef.ClientSecret = &v1.SecretKeySelector{Name: "password"}
|
store.Spec.Provider.AzureKV.AuthSecretRef.ClientSecret = &v1.SecretKeySelector{Name: "password"}
|
||||||
secretClient, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
_, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "could not find secret internal/user: secrets \"user\" not found")
|
tassert.EqualError(t, err, "could not find secret internal/user: secrets \"user\" not found")
|
||||||
tassert.Nil(t, secretClient)
|
|
||||||
store.TypeMeta.Kind = esv1alpha1.ClusterSecretStoreKind
|
store.TypeMeta.Kind = esv1alpha1.ClusterSecretStoreKind
|
||||||
store.TypeMeta.APIVersion = esv1alpha1.ClusterSecretStoreKindAPIVersion
|
store.TypeMeta.APIVersion = esv1alpha1.ClusterSecretStoreKindAPIVersion
|
||||||
ns := "default"
|
ns := "default"
|
||||||
store.Spec.Provider.AzureKV.AuthSecretRef.ClientID.Namespace = &ns
|
store.Spec.Provider.AzureKV.AuthSecretRef.ClientID.Namespace = &ns
|
||||||
store.Spec.Provider.AzureKV.AuthSecretRef.ClientSecret.Namespace = &ns
|
store.Spec.Provider.AzureKV.AuthSecretRef.ClientSecret.Namespace = &ns
|
||||||
secretClient, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
_, err = provider.NewClient(context.Background(), &store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "could not find secret default/user: secrets \"user\" not found")
|
tassert.EqualError(t, err, "could not find secret default/user: secrets \"user\" not found")
|
||||||
tassert.Nil(t, secretClient)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -109,82 +109,102 @@ func (ibm *providerIBM) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSe
|
||||||
|
|
||||||
switch secretType {
|
switch secretType {
|
||||||
case sm.GetSecretOptionsSecretTypeArbitraryConst:
|
case sm.GetSecretOptionsSecretTypeArbitraryConst:
|
||||||
response, _, err := ibm.IBMClient.GetSecret(
|
|
||||||
&sm.GetSecretOptions{
|
|
||||||
SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
|
|
||||||
ID: &secretName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
secret := response.Resources[0].(*sm.SecretResource)
|
return getArbitrarySecret(ibm, &secretName)
|
||||||
secretData := secret.SecretData.(map[string]interface{})
|
|
||||||
arbitrarySecretPayload := secretData["payload"].(string)
|
|
||||||
return []byte(arbitrarySecretPayload), nil
|
|
||||||
|
|
||||||
case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
|
case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
|
||||||
|
|
||||||
if ref.Property == "" {
|
if ref.Property == "" {
|
||||||
return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
|
return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
|
||||||
}
|
}
|
||||||
response, _, err := ibm.IBMClient.GetSecret(
|
return getUsernamePasswordSecret(ibm, &secretName, ref)
|
||||||
&sm.GetSecretOptions{
|
|
||||||
SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
|
|
||||||
ID: &secretName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
secret := response.Resources[0].(*sm.SecretResource)
|
|
||||||
secretData := secret.SecretData.(map[string]interface{})
|
|
||||||
|
|
||||||
if val, ok := secretData[ref.Property]; ok {
|
|
||||||
return []byte(val.(string)), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
|
||||||
|
|
||||||
case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
|
case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
|
||||||
response, _, err := ibm.IBMClient.GetSecret(
|
|
||||||
&sm.GetSecretOptions{
|
|
||||||
SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
|
|
||||||
ID: &secretName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
secret := response.Resources[0].(*sm.SecretResource)
|
return getIamCredentialsSecret(ibm, &secretName)
|
||||||
secretData := *secret.APIKey
|
|
||||||
|
|
||||||
return []byte(secretData), nil
|
|
||||||
|
|
||||||
case sm.CreateSecretOptionsSecretTypeImportedCertConst:
|
case sm.CreateSecretOptionsSecretTypeImportedCertConst:
|
||||||
|
|
||||||
if ref.Property == "" {
|
if ref.Property == "" {
|
||||||
return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
|
return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
|
||||||
}
|
}
|
||||||
response, _, err := ibm.IBMClient.GetSecret(
|
|
||||||
&sm.GetSecretOptions{
|
|
||||||
SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
|
|
||||||
ID: &secretName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
secret := response.Resources[0].(*sm.SecretResource)
|
|
||||||
secretData := secret.SecretData.(map[string]interface{})
|
|
||||||
|
|
||||||
if val, ok := secretData[ref.Property]; ok {
|
|
||||||
return []byte(val.(string)), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
|
||||||
|
|
||||||
|
return getImportCertSecret(ibm, &secretName, ref)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown secret type %s", secretType)
|
return nil, fmt.Errorf("unknown secret type %s", secretType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getArbitrarySecret(ibm *providerIBM, secretName *string) ([]byte, error) {
|
||||||
|
response, _, err := ibm.IBMClient.GetSecret(
|
||||||
|
&sm.GetSecretOptions{
|
||||||
|
SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
|
||||||
|
ID: secretName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := response.Resources[0].(*sm.SecretResource)
|
||||||
|
secretData := secret.SecretData.(map[string]interface{})
|
||||||
|
arbitrarySecretPayload := secretData["payload"].(string)
|
||||||
|
return []byte(arbitrarySecretPayload), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||||
|
response, _, err := ibm.IBMClient.GetSecret(
|
||||||
|
&sm.GetSecretOptions{
|
||||||
|
SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
|
||||||
|
ID: secretName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := response.Resources[0].(*sm.SecretResource)
|
||||||
|
secretData := secret.SecretData.(map[string]interface{})
|
||||||
|
|
||||||
|
if val, ok := secretData[ref.Property]; ok {
|
||||||
|
return []byte(val.(string)), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIamCredentialsSecret(ibm *providerIBM, secretName *string) ([]byte, error) {
|
||||||
|
response, _, err := ibm.IBMClient.GetSecret(
|
||||||
|
&sm.GetSecretOptions{
|
||||||
|
SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
|
||||||
|
ID: secretName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := response.Resources[0].(*sm.SecretResource)
|
||||||
|
secretData := *secret.APIKey
|
||||||
|
|
||||||
|
return []byte(secretData), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
||||||
|
response, _, err := ibm.IBMClient.GetSecret(
|
||||||
|
&sm.GetSecretOptions{
|
||||||
|
SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
|
||||||
|
ID: secretName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := response.Resources[0].(*sm.SecretResource)
|
||||||
|
secretData := secret.SecretData.(map[string]interface{})
|
||||||
|
|
||||||
|
if val, ok := secretData[ref.Property]; ok {
|
||||||
|
return []byte(val.(string)), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
||||||
|
}
|
||||||
|
|
||||||
func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
|
func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
|
||||||
if utils.IsNil(ibm.IBMClient) {
|
if utils.IsNil(ibm.IBMClient) {
|
||||||
return nil, fmt.Errorf(errUninitalizedIBMProvider)
|
return nil, fmt.Errorf(errUninitalizedIBMProvider)
|
||||||
|
@ -214,16 +234,13 @@ func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.Externa
|
||||||
secretData := secret.SecretData.(map[string]interface{})
|
secretData := secret.SecretData.(map[string]interface{})
|
||||||
arbitrarySecretPayload := secretData["payload"].(string)
|
arbitrarySecretPayload := secretData["payload"].(string)
|
||||||
|
|
||||||
kv := make(map[string]string)
|
kv := make(map[string]interface{})
|
||||||
err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv)
|
err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
|
return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secretMap := make(map[string][]byte)
|
secretMap := byteArrayMap(kv)
|
||||||
for k, v := range kv {
|
|
||||||
secretMap[k] = []byte(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return secretMap, nil
|
return secretMap, nil
|
||||||
|
|
||||||
|
@ -240,10 +257,7 @@ func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.Externa
|
||||||
secret := response.Resources[0].(*sm.SecretResource)
|
secret := response.Resources[0].(*sm.SecretResource)
|
||||||
secretData := secret.SecretData.(map[string]interface{})
|
secretData := secret.SecretData.(map[string]interface{})
|
||||||
|
|
||||||
secretMap := make(map[string][]byte)
|
secretMap := byteArrayMap(secretData)
|
||||||
for k, v := range secretData {
|
|
||||||
secretMap[k] = []byte(v.(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
return secretMap, nil
|
return secretMap, nil
|
||||||
|
|
||||||
|
@ -278,10 +292,7 @@ func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.Externa
|
||||||
secret := response.Resources[0].(*sm.SecretResource)
|
secret := response.Resources[0].(*sm.SecretResource)
|
||||||
secretData := secret.SecretData.(map[string]interface{})
|
secretData := secret.SecretData.(map[string]interface{})
|
||||||
|
|
||||||
secretMap := make(map[string][]byte)
|
secretMap := byteArrayMap(secretData)
|
||||||
for k, v := range secretData {
|
|
||||||
secretMap[k] = []byte(v.(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
return secretMap, nil
|
return secretMap, nil
|
||||||
|
|
||||||
|
@ -290,6 +301,14 @@ func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.Externa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func byteArrayMap(secretData map[string]interface{}) map[string][]byte {
|
||||||
|
secretMap := make(map[string][]byte)
|
||||||
|
for k, v := range secretData {
|
||||||
|
secretMap[k] = []byte(v.(string))
|
||||||
|
}
|
||||||
|
return secretMap
|
||||||
|
}
|
||||||
|
|
||||||
func (ibm *providerIBM) Close(ctx context.Context) error {
|
func (ibm *providerIBM) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,9 @@ limitations under the License.
|
||||||
package register
|
package register
|
||||||
|
|
||||||
// packages imported here are registered to the controller schema.
|
// packages imported here are registered to the controller schema.
|
||||||
// nolint:golint
|
// nolint:revive
|
||||||
import (
|
import (
|
||||||
|
_ "github.com/external-secrets/external-secrets/pkg/provider/akeyless"
|
||||||
_ "github.com/external-secrets/external-secrets/pkg/provider/alibaba"
|
_ "github.com/external-secrets/external-secrets/pkg/provider/alibaba"
|
||||||
_ "github.com/external-secrets/external-secrets/pkg/provider/aws"
|
_ "github.com/external-secrets/external-secrets/pkg/provider/aws"
|
||||||
_ "github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault"
|
_ "github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault"
|
||||||
|
|
|
@ -224,6 +224,8 @@ func (v *client) readSecret(ctx context.Context, path, version string) (map[stri
|
||||||
byteMap[k] = []byte(t)
|
byteMap[k] = []byte(t)
|
||||||
case []byte:
|
case []byte:
|
||||||
byteMap[k] = t
|
byteMap[k] = t
|
||||||
|
case nil:
|
||||||
|
byteMap[k] = []byte(nil)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(errSecretFormat)
|
return nil, errors.New(errSecretFormat)
|
||||||
}
|
}
|
||||||
|
@ -316,67 +318,115 @@ func getCertFromConfigMap(v *client) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error {
|
func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error {
|
||||||
|
tokenExists, err := setSecretKeyToken(ctx, v, client)
|
||||||
|
if tokenExists {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenExists, err = setAppRoleToken(ctx, v, client)
|
||||||
|
if tokenExists {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenExists, err = setKubernetesAuthToken(ctx, v, client)
|
||||||
|
if tokenExists {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenExists, err = setLdapAuthToken(ctx, v, client)
|
||||||
|
if tokenExists {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenExists, err = setJwtAuthToken(ctx, v, client)
|
||||||
|
if tokenExists {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenExists, err = setCertAuthToken(ctx, v, client, cfg)
|
||||||
|
if tokenExists {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(errAuthFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAppRoleToken(ctx context.Context, v *client, client Client) (bool, error) {
|
||||||
tokenRef := v.store.Auth.TokenSecretRef
|
tokenRef := v.store.Auth.TokenSecretRef
|
||||||
if tokenRef != nil {
|
if tokenRef != nil {
|
||||||
token, err := v.secretKeyRef(ctx, tokenRef)
|
token, err := v.secretKeyRef(ctx, tokenRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return true, err
|
||||||
}
|
}
|
||||||
client.SetToken(token)
|
client.SetToken(token)
|
||||||
return nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSecretKeyToken(ctx context.Context, v *client, client Client) (bool, error) {
|
||||||
appRole := v.store.Auth.AppRole
|
appRole := v.store.Auth.AppRole
|
||||||
if appRole != nil {
|
if appRole != nil {
|
||||||
token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole)
|
token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return true, err
|
||||||
}
|
}
|
||||||
client.SetToken(token)
|
client.SetToken(token)
|
||||||
return nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setKubernetesAuthToken(ctx context.Context, v *client, client Client) (bool, error) {
|
||||||
kubernetesAuth := v.store.Auth.Kubernetes
|
kubernetesAuth := v.store.Auth.Kubernetes
|
||||||
if kubernetesAuth != nil {
|
if kubernetesAuth != nil {
|
||||||
token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth)
|
token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return true, err
|
||||||
}
|
}
|
||||||
client.SetToken(token)
|
client.SetToken(token)
|
||||||
return nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLdapAuthToken(ctx context.Context, v *client, client Client) (bool, error) {
|
||||||
ldapAuth := v.store.Auth.Ldap
|
ldapAuth := v.store.Auth.Ldap
|
||||||
if ldapAuth != nil {
|
if ldapAuth != nil {
|
||||||
token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
|
token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return true, err
|
||||||
}
|
}
|
||||||
client.SetToken(token)
|
client.SetToken(token)
|
||||||
return nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setJwtAuthToken(ctx context.Context, v *client, client Client) (bool, error) {
|
||||||
jwtAuth := v.store.Auth.Jwt
|
jwtAuth := v.store.Auth.Jwt
|
||||||
if jwtAuth != nil {
|
if jwtAuth != nil {
|
||||||
token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
|
token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return true, err
|
||||||
}
|
}
|
||||||
client.SetToken(token)
|
client.SetToken(token)
|
||||||
return nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCertAuthToken(ctx context.Context, v *client, client Client, cfg *vault.Config) (bool, error) {
|
||||||
certAuth := v.store.Auth.Cert
|
certAuth := v.store.Auth.Cert
|
||||||
if certAuth != nil {
|
if certAuth != nil {
|
||||||
token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg)
|
token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return true, err
|
||||||
}
|
}
|
||||||
client.SetToken(token)
|
client.SetToken(token)
|
||||||
return nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
return errors.New(errAuthFormat)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
|
func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
|
||||||
|
@ -493,43 +543,16 @@ func kubeParameters(role, jwt string) map[string]string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
|
func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
|
||||||
jwtString := ""
|
jwtString, err := getJwtString(ctx, v, kubernetesAuth)
|
||||||
if kubernetesAuth.ServiceAccountRef != nil {
|
if err != nil {
|
||||||
jwt, err := v.secretKeyRefForServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
|
return "", err
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
jwtString = jwt
|
|
||||||
} else if kubernetesAuth.SecretRef != nil {
|
|
||||||
tokenRef := kubernetesAuth.SecretRef
|
|
||||||
if tokenRef.Key == "" {
|
|
||||||
tokenRef = kubernetesAuth.SecretRef.DeepCopy()
|
|
||||||
tokenRef.Key = "token"
|
|
||||||
}
|
|
||||||
jwt, err := v.secretKeyRef(ctx, tokenRef)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
jwtString = jwt
|
|
||||||
} else {
|
|
||||||
// Kubernetes authentication is specified, but without a referenced
|
|
||||||
// Kubernetes secret. We check if the file path for in-cluster service account
|
|
||||||
// exists and attempt to use the token for Vault Kubernetes auth.
|
|
||||||
if _, err := os.Stat(serviceAccTokenPath); err != nil {
|
|
||||||
return "", fmt.Errorf(errServiceAccount, err)
|
|
||||||
}
|
|
||||||
jwtByte, err := ioutil.ReadFile(serviceAccTokenPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf(errServiceAccount, err)
|
|
||||||
}
|
|
||||||
jwtString = string(jwtByte)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters := kubeParameters(kubernetesAuth.Role, jwtString)
|
parameters := kubeParameters(kubernetesAuth.Role, jwtString)
|
||||||
url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
|
url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
|
||||||
request := client.NewRequest("POST", url)
|
request := client.NewRequest("POST", url)
|
||||||
|
|
||||||
err := request.SetJSONBody(parameters)
|
err = request.SetJSONBody(parameters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf(errVaultReqParams, err)
|
return "", fmt.Errorf(errVaultReqParams, err)
|
||||||
}
|
}
|
||||||
|
@ -554,6 +577,39 @@ func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Clie
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
|
||||||
|
if kubernetesAuth.ServiceAccountRef != nil {
|
||||||
|
jwt, err := v.secretKeyRefForServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return jwt, nil
|
||||||
|
} else if kubernetesAuth.SecretRef != nil {
|
||||||
|
tokenRef := kubernetesAuth.SecretRef
|
||||||
|
if tokenRef.Key == "" {
|
||||||
|
tokenRef = kubernetesAuth.SecretRef.DeepCopy()
|
||||||
|
tokenRef.Key = "token"
|
||||||
|
}
|
||||||
|
jwt, err := v.secretKeyRef(ctx, tokenRef)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return jwt, nil
|
||||||
|
} else {
|
||||||
|
// Kubernetes authentication is specified, but without a referenced
|
||||||
|
// Kubernetes secret. We check if the file path for in-cluster service account
|
||||||
|
// exists and attempt to use the token for Vault Kubernetes auth.
|
||||||
|
if _, err := os.Stat(serviceAccTokenPath); err != nil {
|
||||||
|
return "", fmt.Errorf(errServiceAccount, err)
|
||||||
|
}
|
||||||
|
jwtByte, err := ioutil.ReadFile(serviceAccTokenPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(errServiceAccount, err)
|
||||||
|
}
|
||||||
|
return string(jwtByte), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1alpha1.VaultLdapAuth) (string, error) {
|
func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1alpha1.VaultLdapAuth) (string, error) {
|
||||||
username := strings.TrimSpace(ldapAuth.Username)
|
username := strings.TrimSpace(ldapAuth.Username)
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ const (
|
||||||
secretDataString = "some-creds"
|
secretDataString = "some-creds"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeValidSecretStore() *esv1alpha1.SecretStore {
|
func makeValidSecretStoreWithVersion(v esv1alpha1.VaultKVStoreVersion) *esv1alpha1.SecretStore {
|
||||||
return &esv1alpha1.SecretStore{
|
return &esv1alpha1.SecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "vault-store",
|
Name: "vault-store",
|
||||||
|
@ -52,7 +52,7 @@ func makeValidSecretStore() *esv1alpha1.SecretStore {
|
||||||
Vault: &esv1alpha1.VaultProvider{
|
Vault: &esv1alpha1.VaultProvider{
|
||||||
Server: "vault.example.com",
|
Server: "vault.example.com",
|
||||||
Path: "secret",
|
Path: "secret",
|
||||||
Version: esv1alpha1.VaultKVStoreV2,
|
Version: v,
|
||||||
Auth: esv1alpha1.VaultAuth{
|
Auth: esv1alpha1.VaultAuth{
|
||||||
Kubernetes: &esv1alpha1.VaultKubernetesAuth{
|
Kubernetes: &esv1alpha1.VaultKubernetesAuth{
|
||||||
Path: "kubernetes",
|
Path: "kubernetes",
|
||||||
|
@ -68,6 +68,10 @@ func makeValidSecretStore() *esv1alpha1.SecretStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeValidSecretStore() *esv1alpha1.SecretStore {
|
||||||
|
return makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV2)
|
||||||
|
}
|
||||||
|
|
||||||
func makeValidSecretStoreWithCerts() *esv1alpha1.SecretStore {
|
func makeValidSecretStoreWithCerts() *esv1alpha1.SecretStore {
|
||||||
return &esv1alpha1.SecretStore{
|
return &esv1alpha1.SecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
@ -136,14 +140,35 @@ func newVaultResponse(data *vault.Secret) *vault.Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newVaultTokenIDResponse(token string) *vault.Response {
|
func newVaultResponseWithData(data map[string]interface{}) *vault.Response {
|
||||||
return newVaultResponse(&vault.Secret{
|
return newVaultResponse(&vault.Secret{
|
||||||
Data: map[string]interface{}{
|
Data: data,
|
||||||
"id": token,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newVaultTokenIDResponse(token string) *vault.Response {
|
||||||
|
return newVaultResponseWithData(map[string]interface{}{
|
||||||
|
"id": token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
newClientFunc func(c *vault.Config) (Client, error)
|
||||||
|
store esv1alpha1.GenericStore
|
||||||
|
kube kclient.Client
|
||||||
|
ns string
|
||||||
|
}
|
||||||
|
|
||||||
|
type want struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
reason string
|
||||||
|
args args
|
||||||
|
want want
|
||||||
|
}
|
||||||
|
|
||||||
func clientWithLoginMock(c *vault.Config) (Client, error) {
|
func clientWithLoginMock(c *vault.Config) (Client, error) {
|
||||||
return &fake.VaultClient{
|
return &fake.VaultClient{
|
||||||
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
|
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
|
||||||
|
@ -182,22 +207,7 @@ MIICsTCCAZkCFEJJ4daz5sxkFlzq9n1djLEuG7bmMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCHZh
|
||||||
`)
|
`)
|
||||||
secretData := []byte(secretDataString)
|
secretData := []byte(secretDataString)
|
||||||
|
|
||||||
type args struct {
|
cases := map[string]testCase{
|
||||||
newClientFunc func(c *vault.Config) (Client, error)
|
|
||||||
store esv1alpha1.GenericStore
|
|
||||||
kube kclient.Client
|
|
||||||
ns string
|
|
||||||
}
|
|
||||||
|
|
||||||
type want struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
cases := map[string]struct {
|
|
||||||
reason string
|
|
||||||
args args
|
|
||||||
want want
|
|
||||||
}{
|
|
||||||
"InvalidVaultStore": {
|
"InvalidVaultStore": {
|
||||||
reason: "Should return error if given an invalid vault store.",
|
reason: "Should return error if given an invalid vault store.",
|
||||||
args: args{
|
args: args{
|
||||||
|
@ -472,22 +482,35 @@ MIICsTCCAZkCFEJJ4daz5sxkFlzq9n1djLEuG7bmMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCHZh
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
conn := &connector{
|
vaultTest(t, name, tc)
|
||||||
newVaultClient: tc.args.newClientFunc,
|
|
||||||
}
|
|
||||||
if tc.args.newClientFunc == nil {
|
|
||||||
conn.newVaultClient = newVaultClient
|
|
||||||
}
|
|
||||||
_, err := conn.NewClient(context.Background(), tc.args.store, tc.args.kube, tc.args.ns)
|
|
||||||
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
|
|
||||||
t.Errorf("\n%s\nvault.New(...): -want error, +got error:\n%s", tc.reason, diff)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vaultTest(t *testing.T, name string, tc testCase) {
|
||||||
|
conn := &connector{
|
||||||
|
newVaultClient: tc.args.newClientFunc,
|
||||||
|
}
|
||||||
|
if tc.args.newClientFunc == nil {
|
||||||
|
conn.newVaultClient = newVaultClient
|
||||||
|
}
|
||||||
|
_, err := conn.NewClient(context.Background(), tc.args.store, tc.args.kube, tc.args.ns)
|
||||||
|
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
|
||||||
|
t.Errorf("\n%s\nvault.New(...): -want error, +got error:\n%s", tc.reason, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetSecretMap(t *testing.T) {
|
func TestGetSecretMap(t *testing.T) {
|
||||||
errBoom := errors.New("boom")
|
errBoom := errors.New("boom")
|
||||||
|
secret := map[string]interface{}{
|
||||||
|
"access_key": "access_key",
|
||||||
|
"access_secret": "access_secret",
|
||||||
|
}
|
||||||
|
secretWithNilVal := map[string]interface{}{
|
||||||
|
"access_key": "access_key",
|
||||||
|
"access_secret": "access_secret",
|
||||||
|
"token": nil,
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
store *esv1alpha1.VaultProvider
|
store *esv1alpha1.VaultProvider
|
||||||
|
@ -506,6 +529,74 @@ func TestGetSecretMap(t *testing.T) {
|
||||||
args args
|
args args
|
||||||
want want
|
want want
|
||||||
}{
|
}{
|
||||||
|
"ReadSecretKV1": {
|
||||||
|
reason: "Should map the secret even if it has a nil value",
|
||||||
|
args: args{
|
||||||
|
store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV1).Spec.Provider.Vault,
|
||||||
|
vClient: &fake.VaultClient{
|
||||||
|
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
|
||||||
|
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
|
||||||
|
newVaultResponseWithData(secret), nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ReadSecretKV2": {
|
||||||
|
reason: "Should map the secret even if it has a nil value",
|
||||||
|
args: args{
|
||||||
|
store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV2).Spec.Provider.Vault,
|
||||||
|
vClient: &fake.VaultClient{
|
||||||
|
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
|
||||||
|
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
|
||||||
|
newVaultResponseWithData(
|
||||||
|
map[string]interface{}{
|
||||||
|
"data": secret,
|
||||||
|
},
|
||||||
|
), nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ReadSecretWithNilValueKV1": {
|
||||||
|
reason: "Should map the secret even if it has a nil value",
|
||||||
|
args: args{
|
||||||
|
store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV1).Spec.Provider.Vault,
|
||||||
|
vClient: &fake.VaultClient{
|
||||||
|
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
|
||||||
|
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
|
||||||
|
newVaultResponseWithData(secretWithNilVal), nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ReadSecretWithNilValueKV2": {
|
||||||
|
reason: "Should map the secret even if it has a nil value",
|
||||||
|
args: args{
|
||||||
|
store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV2).Spec.Provider.Vault,
|
||||||
|
vClient: &fake.VaultClient{
|
||||||
|
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
|
||||||
|
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
|
||||||
|
newVaultResponseWithData(
|
||||||
|
map[string]interface{}{
|
||||||
|
"data": secretWithNilVal,
|
||||||
|
},
|
||||||
|
), nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
"ReadSecretError": {
|
"ReadSecretError": {
|
||||||
reason: "Should return error if vault client fails to read secret.",
|
reason: "Should return error if vault client fails to read secret.",
|
||||||
args: args{
|
args: args{
|
||||||
|
|
|
@ -35,6 +35,12 @@ import (
|
||||||
"github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client/fake"
|
"github.com/external-secrets/external-secrets/pkg/provider/yandex/lockbox/client/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errMissingKey = "invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name"
|
||||||
|
errSecretPayloadPermissionDenied = "unable to request secret payload to get secret: permission denied"
|
||||||
|
errSecretPayloadNotFound = "unable to request secret payload to get secret: secret not found"
|
||||||
|
)
|
||||||
|
|
||||||
func TestNewClient(t *testing.T) {
|
func TestNewClient(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
const namespace = "namespace"
|
const namespace = "namespace"
|
||||||
|
@ -54,17 +60,17 @@ func TestNewClient(t *testing.T) {
|
||||||
|
|
||||||
k8sClient := clientfake.NewClientBuilder().Build()
|
k8sClient := clientfake.NewClientBuilder().Build()
|
||||||
secretClient, err := provider.NewClient(context.Background(), store, k8sClient, namespace)
|
secretClient, err := provider.NewClient(context.Background(), store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name")
|
tassert.EqualError(t, err, errMissingKey)
|
||||||
tassert.Nil(t, secretClient)
|
tassert.Nil(t, secretClient)
|
||||||
|
|
||||||
store.Spec.Provider.YandexLockbox.Auth = esv1alpha1.YandexLockboxAuth{}
|
store.Spec.Provider.YandexLockbox.Auth = esv1alpha1.YandexLockboxAuth{}
|
||||||
secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
|
secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name")
|
tassert.EqualError(t, err, errMissingKey)
|
||||||
tassert.Nil(t, secretClient)
|
tassert.Nil(t, secretClient)
|
||||||
|
|
||||||
store.Spec.Provider.YandexLockbox.Auth.AuthorizedKey = esmeta.SecretKeySelector{}
|
store.Spec.Provider.YandexLockbox.Auth.AuthorizedKey = esmeta.SecretKeySelector{}
|
||||||
secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
|
secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace)
|
||||||
tassert.EqualError(t, err, "invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name")
|
tassert.EqualError(t, err, errMissingKey)
|
||||||
tassert.Nil(t, secretClient)
|
tassert.Nil(t, secretClient)
|
||||||
|
|
||||||
const authorizedKeySecretName = "authorizedKeySecretName"
|
const authorizedKeySecretName = "authorizedKeySecretName"
|
||||||
|
@ -248,7 +254,7 @@ func TestGetSecretUnauthorized(t *testing.T) {
|
||||||
secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
|
secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
|
||||||
tassert.Nil(t, err)
|
tassert.Nil(t, err)
|
||||||
_, err = secretsClient.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID})
|
_, err = secretsClient.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID})
|
||||||
tassert.EqualError(t, err, "unable to request secret payload to get secret: permission denied")
|
tassert.EqualError(t, err, errSecretPayloadPermissionDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSecretNotFound(t *testing.T) {
|
func TestGetSecretNotFound(t *testing.T) {
|
||||||
|
@ -271,7 +277,7 @@ func TestGetSecretNotFound(t *testing.T) {
|
||||||
secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
|
secretsClient, err := provider.NewClient(ctx, store, k8sClient, namespace)
|
||||||
tassert.Nil(t, err)
|
tassert.Nil(t, err)
|
||||||
_, err = secretsClient.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: "no-secret-with-this-id"})
|
_, err = secretsClient.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: "no-secret-with-this-id"})
|
||||||
tassert.EqualError(t, err, "unable to request secret payload to get secret: secret not found")
|
tassert.EqualError(t, err, errSecretPayloadNotFound)
|
||||||
|
|
||||||
secretID, _ := lockboxBackend.CreateSecret(authorizedKey,
|
secretID, _ := lockboxBackend.CreateSecret(authorizedKey,
|
||||||
textEntry("k1", "v1"),
|
textEntry("k1", "v1"),
|
||||||
|
@ -320,11 +326,11 @@ func TestGetSecretWithTwoNamespaces(t *testing.T) {
|
||||||
tassert.Nil(t, err)
|
tassert.Nil(t, err)
|
||||||
data, err = secretsClient1.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
data, err = secretsClient1.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
||||||
tassert.Nil(t, data)
|
tassert.Nil(t, data)
|
||||||
tassert.EqualError(t, err, "unable to request secret payload to get secret: permission denied")
|
tassert.EqualError(t, err, errSecretPayloadPermissionDenied)
|
||||||
|
|
||||||
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
|
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
|
||||||
tassert.Nil(t, data)
|
tassert.Nil(t, data)
|
||||||
tassert.EqualError(t, err, "unable to request secret payload to get secret: permission denied")
|
tassert.EqualError(t, err, errSecretPayloadPermissionDenied)
|
||||||
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
||||||
tassert.Equal(t, v2, string(data))
|
tassert.Equal(t, v2, string(data))
|
||||||
tassert.Nil(t, err)
|
tassert.Nil(t, err)
|
||||||
|
@ -381,11 +387,11 @@ func TestGetSecretWithTwoApiEndpoints(t *testing.T) {
|
||||||
tassert.Nil(t, err)
|
tassert.Nil(t, err)
|
||||||
data, err = secretsClient1.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
data, err = secretsClient1.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
||||||
tassert.Nil(t, data)
|
tassert.Nil(t, data)
|
||||||
tassert.EqualError(t, err, "unable to request secret payload to get secret: secret not found")
|
tassert.EqualError(t, err, errSecretPayloadNotFound)
|
||||||
|
|
||||||
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
|
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID1, Property: k1})
|
||||||
tassert.Nil(t, data)
|
tassert.Nil(t, data)
|
||||||
tassert.EqualError(t, err, "unable to request secret payload to get secret: secret not found")
|
tassert.EqualError(t, err, errSecretPayloadNotFound)
|
||||||
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
data, err = secretsClient2.GetSecret(ctx, esv1alpha1.ExternalSecretDataRemoteRef{Key: secretID2, Property: k2})
|
||||||
tassert.Equal(t, v2, string(data))
|
tassert.Equal(t, v2, string(data))
|
||||||
tassert.Nil(t, err)
|
tassert.Nil(t, err)
|
||||||
|
|
|
@ -39,7 +39,14 @@ func MergeStringMap(dest, src map[string]string) {
|
||||||
|
|
||||||
// IsNil checks if an Interface is nil.
|
// IsNil checks if an Interface is nil.
|
||||||
func IsNil(i interface{}) bool {
|
func IsNil(i interface{}) bool {
|
||||||
return i == nil || reflect.ValueOf(i).IsNil()
|
if i == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
value := reflect.ValueOf(i)
|
||||||
|
if value.Type().Kind() == reflect.Ptr {
|
||||||
|
return value.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectHash calculates md5 sum of the data contained in the secret.
|
// ObjectHash calculates md5 sum of the data contained in the secret.
|
||||||
|
|
|
@ -17,6 +17,7 @@ package utils
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
vault "github.com/oracle/oci-go-sdk/v45/vault"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,3 +61,88 @@ func TestObjectHash(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsNil(t *testing.T) {
|
||||||
|
tbl := []struct {
|
||||||
|
name string
|
||||||
|
val interface{}
|
||||||
|
exp bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple nil val",
|
||||||
|
val: nil,
|
||||||
|
exp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil slice",
|
||||||
|
val: (*[]struct{})(nil),
|
||||||
|
exp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "struct pointer",
|
||||||
|
val: &testing.T{},
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "struct",
|
||||||
|
val: testing.T{},
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "slice of struct",
|
||||||
|
val: []struct{}{{}},
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "slice of ptr",
|
||||||
|
val: []*testing.T{nil},
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "slice",
|
||||||
|
val: []struct{}(nil),
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int default value",
|
||||||
|
val: 0,
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty str",
|
||||||
|
val: "",
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "oracle vault",
|
||||||
|
val: vault.VaultsClient{},
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "func",
|
||||||
|
val: func() {
|
||||||
|
// noop for testing and to make linter happy
|
||||||
|
},
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "channel",
|
||||||
|
val: make(chan struct{}),
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "map",
|
||||||
|
val: map[string]string{},
|
||||||
|
exp: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range tbl {
|
||||||
|
t.Run(row.name, func(t *testing.T) {
|
||||||
|
res := IsNil(row.val)
|
||||||
|
if res != row.exp {
|
||||||
|
t.Errorf("IsNil(%#v)=%t, expected %t", row.val, res, row.exp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
tools.go
1
tools.go
|
@ -4,5 +4,6 @@
|
||||||
package tools
|
package tools
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "github.com/onsi/ginkgo/ginkgo"
|
||||||
_ "sigs.k8s.io/controller-tools/cmd/controller-gen"
|
_ "sigs.k8s.io/controller-tools/cmd/controller-gen"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue