1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-15 17:51:01 +00:00

Merge pull request #618 from external-secrets/feature/aws-e2e-managed

feat(e2e): implement aws tests, enhance gcp tests
This commit is contained in:
paul-the-alien[bot] 2022-01-24 10:46:02 +00:00 committed by GitHub
commit 0b9c142a22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 1342 additions and 743 deletions

View file

@ -24,24 +24,62 @@ env:
GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account
TF_VAR_GCP_GSA_NAME: ${{ secrets.GCP_GSA_NAME}} # Goolge Service Account for tf TF_VAR_GCP_GSA_NAME: ${{ secrets.GCP_GSA_NAME}} # Goolge Service Account for tf
TF_VAR_GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account for tf TF_VAR_GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account for tf
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_SA_NAME: ${{ secrets.AWS_SA_NAME }}
AWS_SA_NAMESPACE: ${{ secrets.AWS_SA_NAMESPACE }}
AWS_REGION: "eu-west-1"
AWS_CLUSTER_NAME: "eso-e2e-managed"
TF_VAR_AWS_SA_NAME: ${{ secrets.AWS_SA_NAME }}
TF_VAR_AWS_SA_NAMESPACE: ${{ secrets.AWS_SA_NAMESPACE }}
TF_VAR_AWS_REGION: "eu-west-1"
TF_VAR_AWS_CLUSTER_NAME: "eso-e2e-managed"
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID}} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID}}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}}
TENANT_ID: ${{ secrets.TENANT_ID}} TENANT_ID: ${{ secrets.TENANT_ID}}
VAULT_URL: ${{ secrets.VAULT_URL}} VAULT_URL: ${{ secrets.VAULT_URL}}
IMAGE_REGISTRY: ghcr.io/external-secrets/external-secrets
E2E_IMAGE_REGISTRY: ghcr.io/external-secrets/external-secrets-e2e
E2E_VERSION: test
name: e2e tests name: e2e tests
jobs: jobs:
# Repo owner has commented /ok-to-test-managed on a (fork-based) pull request integration-managed:
integration-fork-managed:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: if: github.event_name == 'repository_dispatch'
github.event_name == 'repository_dispatch'
steps: steps:
# set status=in_progress
- uses: actions/github-script@v1
id: update-check-run
env:
number: ${{ github.event.client_payload.slash_command.args.named.pull }}
job: ${{ github.job }}
conclusion: ${{ job.status }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: pull } = await github.pulls.get({
...context.repo,
pull_number: process.env.number
});
const ref = pull.head.sha;
console.log("\n\nPR sha: " + ref)
const { data: checks } = await github.checks.listForRef({
...context.repo,
ref
});
console.log("\n\nPR CHECKS: " + checks)
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({
...context.repo,
check_run_id: check[0].id,
status: 'in_progress',
});
return result;
# Check out merge commit # Check out merge commit
- name: Fork based /ok-to-test-managed checkout - name: Fork based /ok-to-test-managed checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@ -75,13 +113,7 @@ jobs:
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-
- name: Setup gcloud CLI
uses: google-github-actions/setup-gcloud@master
with:
service_account_key: ${{ env.GCP_SM_SA_GKE_JSON }}
project_id: ${{ env.GCP_PROJECT_ID }}
- name: Setup TFLint - name: Setup TFLint
uses: terraform-linters/setup-tflint@v1 uses: terraform-linters/setup-tflint@v1
with: with:
@ -91,40 +123,52 @@ jobs:
run: find ${{ github.workspace }} | grep tf$ | xargs -n1 dirname | xargs -IXXX -n1 /bin/sh -c 'set -o errexit; cd XXX; pwd; tflint --loglevel=info .; cd - >/dev/null' run: find ${{ github.workspace }} | grep tf$ | xargs -n1 dirname | xargs -IXXX -n1 /bin/sh -c 'set -o errexit; cd XXX; pwd; tflint --loglevel=info .; cd - >/dev/null'
- name: Setup TF Gcloud Provider - name: Setup TF Gcloud Provider
if: github.event.client_payload.slash_command.args.named.provider == 'gcp'
run: |- run: |-
mkdir -p terraform/gcp/secrets mkdir -p terraform/gcp/secrets
echo ${GCP_SM_SA_GKE_JSON} > terraform/gcp/secrets/gcloud-service-account-key.json echo ${GCP_SM_SA_GKE_JSON} > terraform/gcp/secrets/gcloud-service-account-key.json
- name: Show TF GKE - name: Show TF
run: |- run: |-
make tf.show.gcp PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
make tf.show.${PROVIDER}
- name: Setup Infracost - name: Setup Infracost
uses: infracost/actions/setup@v1 uses: infracost/actions/setup@v1
with: with:
api-key: ${{ secrets.INFRACOST_API_KEY }} api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Generate Infracost JSON for GKE - name: Generate Infracost JSON for provider
run: infracost breakdown --path terraform/gcp/plan.json --format json --out-file /tmp/infracost.json run: infracost breakdown --path terraform/${{github.event.client_payload.slash_command.args.named.provider}}/plan.json --format json --out-file /tmp/infracost.json
- name: Post Infracost comment - name: Post Infracost comment
uses: infracost/actions/comment@v1 uses: infracost/actions/comment@v1
with: with:
path: /tmp/infracost.json path: /tmp/infracost.json
# Choose the commenting behavior, 'update' is a good default: behavior: update
behavior: update # Create a single comment and update it. The "quietest" option.
# behavior: delete-and-new # Delete previous comments and create a new one.
# behavior: hide-and-new # Minimize previous comments and create a new one.
# behavior: new # Create a new cost estimate comment on every push.
- name: Apply TF GKE - name: Apply TF
run: |- run: |-
make tf.apply.gcp PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
make tf.apply.${PROVIDER}
- name: Setup gcloud CLI
if: github.event.client_payload.slash_command.args.named.provider == 'gcp'
uses: google-github-actions/setup-gcloud@master
with:
service_account_key: ${{ env.GCP_SM_SA_GKE_JSON }}
project_id: ${{ env.GCP_PROJECT_ID }}
- name: Get the GKE credentials - name: Get the GKE credentials
if: github.event.client_payload.slash_command.args.named.provider == 'gke'
run: |- run: |-
gcloud container clusters get-credentials "$GCP_GKE_CLUSTER" --zone "$GCP_GKE_ZONE" --project "$GCP_PROJECT_ID" gcloud container clusters get-credentials "$GCP_GKE_CLUSTER" --zone "$GCP_GKE_ZONE" --project "$GCP_PROJECT_ID"
- name: Get the AWS credentials
if: github.event.client_payload.slash_command.args.named.provider == 'aws'
run: |-
aws --region $AWS_REGION eks update-kubeconfig --name $AWS_CLUSTER_NAME
- name: Login to Docker - name: Login to Docker
uses: docker/login-action@v1 uses: docker/login-action@v1
if: env.GHCR_USERNAME != '' if: env.GHCR_USERNAME != ''
@ -133,25 +177,25 @@ jobs:
username: ${{ secrets.GHCR_USERNAME }} username: ${{ secrets.GHCR_USERNAME }}
password: ${{ secrets.GHCR_TOKEN }} password: ${{ secrets.GHCR_TOKEN }}
- name: Run e2e Tests for GCP - name: Run managed e2e Tests
run: | run: |
export E2E_VERSION=$GITHUB_SHA
export PR_IMG_TAG=$GITHUB_SHA
export PATH=$PATH:$(go env GOPATH)/bin export PATH=$PATH:$(go env GOPATH)/bin
go get github.com/onsi/ginkgo/ginkgo PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
make test.e2e.managed FOCUS="gcpmanaged" go get github.com/onsi/ginkgo/v2/ginkgo
make test.e2e.managed GINKGO_LABELS="${PROVIDER}"
- name: Destroy TF GKE - name: Destroy TF
if: always() if: always()
run: |- run: |-
make tf.destroy.gcp PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
make tf.destroy.${PROVIDER}
# Update check run called "integration-fork" # set status=completed
- uses: actions/github-script@v1 - uses: actions/github-script@v1
id: update-check-run id: update-check-run
if: ${{ always() }} if: ${{ always() }}
env: env:
number: ${{ github.event.client_payload.pull_request.number }} number: ${{ github.event.client_payload.slash_command.args.named.pull }}
job: ${{ github.job }} job: ${{ github.job }}
# Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
conclusion: ${{ job.status }} conclusion: ${{ job.status }}

View file

@ -19,12 +19,12 @@ env:
GCP_GSA_NAME: ${{ secrets.GCP_GSA_NAME}} # Goolge Service Account GCP_GSA_NAME: ${{ secrets.GCP_GSA_NAME}} # Goolge Service Account
GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID}} GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID}}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID}} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID}}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}}
TENANT_ID: ${{ secrets.TENANT_ID}} TENANT_ID: ${{ secrets.TENANT_ID}}
VAULT_URL: ${{ secrets.VAULT_URL}} VAULT_URL: ${{ secrets.VAULT_URL}}
E2E_IMAGE_REGISTRY: local/external-secrets-e2e
E2E_VERSION: test
name: e2e tests name: e2e tests
@ -87,7 +87,7 @@ jobs:
BUILD_ARGS: "--load" BUILD_ARGS: "--load"
run: | run: |
export PATH=$PATH:$(go env GOPATH)/bin export PATH=$PATH:$(go env GOPATH)/bin
go get github.com/onsi/ginkgo/ginkgo go get github.com/onsi/ginkgo/v2/ginkgo
make test.e2e make test.e2e
# Repo owner has commented /ok-to-test on a (fork-based) pull request # Repo owner has commented /ok-to-test on a (fork-based) pull request
@ -150,7 +150,7 @@ jobs:
BUILD_ARGS: "--load" BUILD_ARGS: "--load"
run: | run: |
export PATH=$PATH:$(go env GOPATH)/bin export PATH=$PATH:$(go env GOPATH)/bin
go get github.com/onsi/ginkgo/ginkgo go get github.com/onsi/ginkgo/v2/ginkgo
make test.e2e make test.e2e
# Update check run called "integration-fork" # Update check run called "integration-fork"

View file

@ -30,6 +30,7 @@ jobs:
token: ${{ env.TOKEN }} # GitHub App installation access token token: ${{ env.TOKEN }} # GitHub App installation access token
# token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work # token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work
reaction-token: ${{ secrets.GITHUB_TOKEN }} reaction-token: ${{ secrets.GITHUB_TOKEN }}
static-args: pull=${{ github.event.client_payload.pull_request.number }}
issue-type: pull-request issue-type: pull-request
commands: ok-to-test-managed commands: ok-to-test-managed
permission: maintain permission: maintain

View file

@ -14,9 +14,7 @@ BUILD_ARGS ?=
all: $(addprefix build-,$(ARCH)) all: $(addprefix build-,$(ARCH))
# Image registry for build/push image targets # Image registry for build/push image targets
IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets export IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets
PR_IMG_TAG ?=
CRD_DIR ?= deploy/crds CRD_DIR ?= deploy/crds
@ -35,10 +33,10 @@ endif
# check if there are any existing `git tag` values # check if there are any existing `git tag` values
ifeq ($(shell git tag),) ifeq ($(shell git tag),)
# no tags found - default to initial tag `v0.0.0` # no tags found - default to initial tag `v0.0.0`
VERSION := $(shell echo "v0.0.0-$$(git rev-list HEAD --count)-g$$(git describe --dirty --always)" | sed 's/-/./2' | sed 's/-/./2') export VERSION := $(shell echo "v0.0.0-$$(git rev-list HEAD --count)-g$$(git describe --dirty --always)" | sed 's/-/./2' | sed 's/-/./2')
else else
# use tags # use tags
VERSION := $(shell git describe --dirty --always --tags --exclude 'helm*' | sed 's/-/./2' | sed 's/-/./2') export VERSION := $(shell git describe --dirty --always --tags --exclude 'helm*' | sed 's/-/./2' | sed 's/-/./2')
endif endif
# ==================================================================================== # ====================================================================================
@ -87,13 +85,13 @@ test: generate ## Run tests
test.e2e: generate ## Run e2e tests test.e2e: generate ## Run e2e tests
@$(INFO) go test e2e-tests @$(INFO) go test e2e-tests
$(MAKE) -C ./e2e test $(MAKE) -C ./e2e test
@$(OK) go test unit-tests @$(OK) go test e2e-tests
.PHONY: test.e2e.managed .PHONY: test.e2e.managed
test.e2e.managed: generate ## Run e2e tests test.e2e.managed: generate ## Run e2e tests managed
@$(INFO) go test e2e-tests @$(INFO) go test e2e-tests-managed
$(MAKE) -C ./e2e test.managed $(MAKE) -C ./e2e test.managed
@$(OK) go test unit-tests @$(OK) go test e2e-tests-managed
.PHONY: build .PHONY: build
build: $(addprefix build-,$(ARCH)) ## Build binary build: $(addprefix build-,$(ARCH)) ## Build binary
@ -211,7 +209,7 @@ docker.push: ## Push the docker image to the registry
@docker push $(IMAGE_REGISTRY):$(VERSION) @docker push $(IMAGE_REGISTRY):$(VERSION)
@$(OK) docker push @$(OK) docker push
# RELEASE_TAG is tag to promote. Default is promooting to main branch, but can be overriden # RELEASE_TAG is tag to promote. Default is promoting to main branch, but can be overriden
# to promote a tag to a specific version. # to promote a tag to a specific version.
RELEASE_TAG ?= main RELEASE_TAG ?= main
SOURCE_TAG ?= $(VERSION) SOURCE_TAG ?= $(VERSION)
@ -230,29 +228,27 @@ docker.promote: ## Promote the docker image to the registry
# ==================================================================================== # ====================================================================================
# Terraform # Terraform
tf.plan.gcp: ## Runs terrform plan for gcp provider bringing GKE up tf.plan.%: ## Runs terrform plan for a provider
@cd $(TF_DIR)/gcp; \ @cd $(TF_DIR)/$*; \
terraform init; \ terraform init; \
terraform plan -auto-approve terraform plan
tf.apply.gcp: ## Runs terrform apply for gcp provider bringing GKE up tf.apply.%: ## Runs terrform apply for a provider
@cd $(TF_DIR)/gcp; \ @cd $(TF_DIR)/$*; \
terraform init; \ terraform init; \
terraform apply -auto-approve terraform apply -auto-approve
tf.destroy.gcp: ## Runs terrform destroy for gcp provider bringing GKE down tf.destroy.%: ## Runs terrform destroy for a provider
@cd $(TF_DIR)/gcp; \ @cd $(TF_DIR)/$*; \
terraform init; \ terraform init; \
terraform destroy -auto-approve terraform destroy -auto-approve
tf.show.gcp: ## Runs terrform show for gcp and outputs to a file tf.show.%: ## Runs terrform show for a provider and outputs to a file
@cd $(TF_DIR)/gcp; \ @cd $(TF_DIR)/$*; \
terraform init; \ terraform init; \
terraform plan -out tfplan.binary; \ terraform plan -out tfplan.binary; \
terraform show -json tfplan.binary > plan.json terraform show -json tfplan.binary > plan.json
# ==================================================================================== # ====================================================================================
# Help # Help

View file

@ -4,7 +4,7 @@ FROM golang:$GO_VERSION-buster as builder
ENV KUBECTL_VERSION="v1.21.2" ENV KUBECTL_VERSION="v1.21.2"
ENV HELM_VERSION="v3.7.1" ENV HELM_VERSION="v3.7.1"
RUN go get -u github.com/onsi/ginkgo/ginkgo RUN go get -u github.com/onsi/ginkgo/v2/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 && \
chmod +x /usr/local/bin/kubectl && \ chmod +x /usr/local/bin/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 && \

View file

@ -2,15 +2,11 @@ MAKEFLAGS += --warn-undefined-variables
SHELL := /bin/bash SHELL := /bin/bash
.SHELLFLAGS := -euo pipefail -c .SHELLFLAGS := -euo pipefail -c
IMG_TAG = test KIND_IMG = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6"
IMG = local/external-secrets-e2e:$(IMG_TAG) BUILD_ARGS ?=
KIND_IMG = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6"
BUILD_ARGS ?=
IMAGE_REGISTRY ?=
export FOCUS := $(FOCUS)
export E2E_IMAGE_REGISTRY ?= export E2E_IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets-e2e
export E2E_VERSION ?= export GINKGO_LABELS ?= !managed
start-kind: ## Start kind cluster start-kind: ## Start kind cluster
kind create cluster \ kind create cluster \
@ -21,49 +17,35 @@ start-kind: ## Start kind cluster
test: e2e-image ## Run e2e tests against current kube context 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=$(IMAGE_REGISTRY) \
VERSION=$(IMG_TAG) \ VERSION=$(VERSION) \
ARCH=amd64 \ ARCH=amd64 \
BUILD_ARGS="${BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux" 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" $(IMAGE_REGISTRY):$(VERSION)
kind load docker-image --name="external-secrets" $(IMG) kind load docker-image --name="external-secrets" $(E2E_IMAGE_REGISTRY):$(VERSION)
./run.sh ./run.sh
test.managed: e2e-remote-values e2e-image.managed ## Run e2e tests against current kube context test.managed: e2e-image ## Run e2e tests against current kube context
$(MAKE) -C ../ docker.build \ $(MAKE) -C ../ docker.build \
VERSION=$(PR_IMG_TAG) \ VERSION=$(VERSION) \
ARCH=amd64 \ ARCH=amd64 \
BUILD_ARGS="${BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux" BUILD_ARGS="${BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
$(MAKE) -C ../ docker.push \ $(MAKE) -C ../ docker.push \
VERSION=$(PR_IMG_TAG) VERSION=$(VERSION)
$(MAKE) -C ../ docker.push \ $(MAKE) -C ../ docker.push \
IMAGE_REGISTRY=$(E2E_IMAGE_REGISTRY) \ IMAGE_REGISTRY=$(E2E_IMAGE_REGISTRY) \
VERSION=$(E2E_VERSION) VERSION=$(VERSION)
./run.sh ./run.sh
e2e-remote-values:
sed -i "s|repository: [^ ]*|repository: $(IMAGE_REGISTRY)|g" k8s/eso.values.yaml
sed -i "s|tag: [^ ]*|tag: $(PR_IMG_TAG)|g" k8s/eso.values.yaml
sed -i "s|repository: [^ ]*|repository: $(IMAGE_REGISTRY)|g" k8s/eso.scoped.values.yaml
sed -i "s|tag: [^ ]*|tag: $(PR_IMG_TAG)|g" k8s/eso.scoped.values.yaml
e2e-bin: e2e-bin:
CGO_ENABLED=0 go run github.com/onsi/ginkgo/ginkgo build . CGO_ENABLED=0 go run github.com/onsi/ginkgo/v2/ginkgo build .
e2e-image: e2e-bin e2e-image: e2e-bin
-rm -rf ./k8s/deploy -rm -rf ./k8s/deploy
mkdir -p k8s mkdir -p k8s
$(MAKE) -C ../ helm.generate $(MAKE) -C ../ helm.generate
cp -r ../deploy ./k8s cp -r ../deploy ./k8s
docker build $(BUILD_ARGS) -t $(IMG) . docker build $(BUILD_ARGS) -t $(E2E_IMAGE_REGISTRY):$(VERSION) .
e2e-image.managed: e2e-bin
-rm -rf ./k8s/deploy
mkdir -p k8s
$(MAKE) -C ../ helm.generate
cp -r ../deploy ./k8s
docker build $(BUILD_ARGS) -t ghcr.io/external-secrets/external-secrets-e2e:$(E2E_VERSION) .
stop-kind: ## Stop kind cluster stop-kind: ## Stop kind cluster
kind delete cluster \ kind delete cluster \

View file

@ -17,7 +17,7 @@ import (
"testing" "testing"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -30,25 +30,16 @@ var _ = SynchronizedBeforeSuite(func() []byte {
cfg := &addon.Config{} cfg := &addon.Config{}
cfg.KubeConfig, cfg.KubeClientSet, cfg.CRClient = util.NewConfig() cfg.KubeConfig, cfg.KubeClientSet, cfg.CRClient = util.NewConfig()
By("installing localstack")
addon.InstallGlobalAddon(addon.NewLocalstack(), cfg)
By("waiting for localstack")
err := util.WaitForURL("http://localstack.default/health")
Expect(err).ToNot(HaveOccurred())
By("installing eso") By("installing eso")
addon.InstallGlobalAddon(addon.NewESO(), cfg) addon.InstallGlobalAddon(addon.NewESO(), cfg)
By("installing scoped eso")
addon.InstallGlobalAddon(addon.NewScopedESO(), cfg)
return nil return nil
}, func([]byte) {}) }, func([]byte) {})
var _ = SynchronizedAfterSuite(func() {}, func() { var _ = SynchronizedAfterSuite(func() {}, func() {
By("Cleaning up global addons") By("Cleaning up global addons")
addon.UninstallGlobalAddons() addon.UninstallGlobalAddons()
if CurrentGinkgoTestDescription().Failed { if CurrentSpecReport().Failed() {
addon.PrintLogs() addon.PrintLogs()
} }
}) })
@ -56,5 +47,5 @@ var _ = SynchronizedAfterSuite(func() {}, func() {
func TestE2E(t *testing.T) { func TestE2E(t *testing.T) {
NewWithT(t) NewWithT(t)
RegisterFailHandler(Fail) RegisterFailHandler(Fail)
RunSpecs(t, "external-secrets e2e suite") RunSpecs(t, "external-secrets e2e suite", Label("e2e"))
} }

View file

@ -19,8 +19,6 @@ set -euo pipefail
NC='\e[0m' NC='\e[0m'
BGREEN='\e[32m' BGREEN='\e[32m'
SLOW_E2E_THRESHOLD=${SLOW_E2E_THRESHOLD:-50}
FOCUS=${FOCUS:-.*}
E2E_NODES=${E2E_NODES:-5} E2E_NODES=${E2E_NODES:-5}
if [ ! -f "${HOME}/.kube/config" ]; then if [ ! -f "${HOME}/.kube/config" ]; then
@ -31,13 +29,13 @@ if [ ! -f "${HOME}/.kube/config" ]; then
fi fi
ginkgo_args=( ginkgo_args=(
"-randomizeSuites" "--randomize-suites"
"-randomizeAllSpecs" "--randomize-all"
"-flakeAttempts=2" "--flake-attempts=2"
"-p" "-p"
"-progress" "-progress"
"-trace" "-trace"
"-slowSpecThreshold=${SLOW_E2E_THRESHOLD}" "--slow-spec-threshold=5m"
"-r" "-r"
"-v" "-v"
"-timeout=45m" "-timeout=45m"
@ -45,9 +43,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 (LABELS=${GINKGO_LABELS})...${NC}"
ACK_GINKGO_RC=true ginkgo "${ginkgo_args[@]}" \ ACK_GINKGO_RC=true ginkgo "${ginkgo_args[@]}" \
-focus="${FOCUS}" \ -label-filter="${GINKGO_LABELS}" \
-skip="\[Serial\]|\[MemoryLeak\]" \
-nodes="${E2E_NODES}" \ -nodes="${E2E_NODES}" \
/e2e.test /e2e.test

View file

@ -14,7 +14,7 @@ limitations under the License.
package addon package addon
import ( import (
"github.com/onsi/ginkgo" "github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega" "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"

View file

@ -63,7 +63,7 @@ func (c *HelmChart) Install() error {
args := []string{"install", c.ReleaseName, c.Chart, args := []string{"install", c.ReleaseName, c.Chart,
"--wait", "--wait",
"--timeout", "600s", "--timeout", "120s",
"--namespace", c.Namespace, "--namespace", c.Namespace,
} }

View file

@ -14,92 +14,99 @@ limitations under the License.
package addon package addon
import ( import (
"fmt"
"os" "os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/gomega"
// nolint
"github.com/external-secrets/external-secrets/e2e/framework/util"
) )
type ESO struct { type ESO struct {
Addon *HelmChart
} }
func NewESO() *ESO { func NewESO(mutators ...MutationFunc) *ESO {
return &ESO{ eso := &ESO{
&HelmChart{ &HelmChart{
Namespace: "default", Namespace: "default",
ReleaseName: "eso", ReleaseName: "eso",
Chart: "/k8s/deploy/charts/external-secrets", Chart: "/k8s/deploy/charts/external-secrets",
Values: []string{"/k8s/eso.values.yaml"}, Vars: []StringTuple{
{
Key: "image.repository",
Value: os.Getenv("IMAGE_REGISTRY"),
},
{
Key: "image.tag",
Value: os.Getenv("VERSION"),
},
{
Key: "installCRDs",
Value: "false",
},
},
}, },
} }
for _, f := range mutators {
f(eso)
}
return eso
}
type MutationFunc func(eso *ESO)
func WithReleaseName(name string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.ReleaseName = name
}
}
func WithNamespace(namespace string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Namespace = namespace
}
}
func WithNamespaceScope(namespace string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Vars = append(eso.HelmChart.Vars, StringTuple{
Key: "scopedNamespace",
Value: namespace,
})
}
}
func WithServiceAccount(saName string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Vars = append(eso.HelmChart.Vars, []StringTuple{
{
Key: "serviceAccount.create",
Value: "false",
},
{
Key: "serviceAccount.name",
Value: "eso-e2e-test",
},
}...)
}
}
func WithControllerClass(class string) MutationFunc {
return func(eso *ESO) {
eso.HelmChart.Vars = append(eso.HelmChart.Vars, StringTuple{
Key: "extraArgs.controller-class",
Value: class,
})
}
} }
func (l *ESO) Install() error { func (l *ESO) Install() error {
By("Installing eso\n") By("Installing eso\n")
err := l.Addon.Install() err := l.HelmChart.Install()
if err != nil {
return err
}
By("afterInstall eso\n")
err = l.afterInstall()
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
func (l *ESO) afterInstall() error {
err := gcpPreparation()
Expect(err).NotTo(HaveOccurred())
err = awsPreparation()
Expect(err).NotTo(HaveOccurred())
if err != nil {
return err
}
return nil
}
func gcpPreparation() error {
gcpProjectID := os.Getenv("GCP_PROJECT_ID")
gcpGSAName := os.Getenv("GCP_GSA_NAME")
gcpKSAName := os.Getenv("GCP_KSA_NAME")
_, kubeClientSet, _ := util.NewConfig()
annotations := make(map[string]string)
annotations["iam.gke.io/gcp-service-account"] = fmt.Sprintf("%s@%s.iam.gserviceaccount.com", gcpGSAName, gcpProjectID)
_, err := util.UpdateKubeSA(gcpKSAName, kubeClientSet, "default", annotations)
Expect(err).NotTo(HaveOccurred())
_, err = util.UpdateKubeSA("external-secrets-e2e", kubeClientSet, "default", annotations)
Expect(err).NotTo(HaveOccurred())
if err != nil {
return err
}
return nil
}
func awsPreparation() error {
return nil
}
func NewScopedESO() *ESO {
return &ESO{
&HelmChart{
Namespace: "default",
ReleaseName: "eso-aws-sm",
Chart: "/k8s/deploy/charts/external-secrets",
Values: []string{"/k8s/eso.scoped.values.yaml"},
},
}
}

View file

@ -1,44 +0,0 @@
/*
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 addon
import "github.com/external-secrets/external-secrets/e2e/framework/util"
type Localstack struct {
Addon
}
func NewLocalstack() *Localstack {
return &Localstack{
&HelmChart{
Namespace: "default",
ReleaseName: "localstack",
Chart: "localstack-charts/localstack",
ChartVersion: "0.2.0",
Repo: ChartRepo{
Name: "localstack-charts",
URL: "https://localstack.github.io/helm-charts",
},
Values: []string{"/k8s/localstack.values.yaml"},
},
}
}
func (l *Localstack) Install() error {
err := l.Addon.Install()
if err != nil {
return err
}
return util.WaitForURL("http://localstack.default/health")
}

View file

@ -32,7 +32,7 @@ import (
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

View file

@ -33,7 +33,7 @@ import (
// with the provided values. // with the provided values.
func (f *Framework) WaitForSecretValue(namespace, name string, expected *v1.Secret) (*v1.Secret, error) { func (f *Framework) WaitForSecretValue(namespace, name string, expected *v1.Secret) (*v1.Secret, error) {
secret := &v1.Secret{} secret := &v1.Secret{}
err := wait.PollImmediate(time.Second*2, time.Minute*2, func() (bool, error) { err := wait.PollImmediate(time.Second*5, time.Minute, func() (bool, error) {
err := f.CRClient.Get(context.Background(), types.NamespacedName{ err := f.CRClient.Get(context.Background(), types.NamespacedName{
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,

View file

@ -16,12 +16,10 @@ package framework
import ( import (
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
kscheme "k8s.io/client-go/kubernetes/scheme" kscheme "k8s.io/client-go/kubernetes/scheme"
@ -30,6 +28,7 @@ import (
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/e2e/framework/addon" "github.com/external-secrets/external-secrets/e2e/framework/addon"
"github.com/external-secrets/external-secrets/e2e/framework/log"
"github.com/external-secrets/external-secrets/e2e/framework/util" "github.com/external-secrets/external-secrets/e2e/framework/util"
) )
@ -72,11 +71,9 @@ func New(baseName string) *Framework {
// BeforeEach creates a namespace. // BeforeEach creates a namespace.
func (f *Framework) BeforeEach() { func (f *Framework) BeforeEach() {
var err error var err error
By("Building a namespace api object")
f.Namespace, err = util.CreateKubeNamespace(f.BaseName, f.KubeClientSet) f.Namespace, err = util.CreateKubeNamespace(f.BaseName, f.KubeClientSet)
Expect(err).NotTo(HaveOccurred()) log.Logf("created test namespace %s", f.Namespace.Name)
Expect(err).ToNot(HaveOccurred())
By("Using the namespace " + f.Namespace.Name)
} }
// AfterEach deletes the namespace and cleans up the registered addons. // AfterEach deletes the namespace and cleans up the registered addons.
@ -87,7 +84,7 @@ func (f *Framework) AfterEach() {
} }
// reset addons to default once the run is done // reset addons to default once the run is done
f.Addons = []addon.Addon{} f.Addons = []addon.Addon{}
By("deleting test namespace") log.Logf("deleting test namespace %s", f.Namespace.Name)
err := util.DeleteKubeNamespace(f.Namespace.Name, f.KubeClientSet) err := util.DeleteKubeNamespace(f.Namespace.Name, f.KubeClientSet)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
@ -111,13 +108,13 @@ func (f *Framework) Install(a addon.Addon) {
func Compose(descAppend string, f *Framework, fn func(f *Framework) (string, func(*TestCase)), tweaks ...func(*TestCase)) TableEntry { func Compose(descAppend string, f *Framework, fn func(f *Framework) (string, func(*TestCase)), tweaks ...func(*TestCase)) TableEntry {
desc, tfn := fn(f) desc, tfn := fn(f)
tweaks = append(tweaks, tfn) tweaks = append(tweaks, tfn)
te := Entry(desc + " " + descAppend)
// need to convert []func to []interface{} // need to convert []func to []interface{}
ifs := make([]interface{}, len(tweaks)) ifs := make([]interface{}, len(tweaks))
for i := 0; i < len(tweaks); i++ { for i := 0; i < len(tweaks); i++ {
ifs[i] = tweaks[i] ifs[i] = tweaks[i]
} }
te.Parameters = ifs te := Entry(desc+" "+descAppend, ifs...)
return te return te
} }

View file

@ -14,12 +14,10 @@ limitations under the License.
package log package log
import ( import (
"fmt" "github.com/onsi/ginkgo/v2"
"github.com/onsi/ginkgo"
) )
// Logf logs the format string to ginkgo stdout. // Logf logs the format string to ginkgo stdout.
func Logf(format string, args ...interface{}) { func Logf(format string, args ...interface{}) {
fmt.Fprintf(ginkgo.GinkgoWriter, format, args...) ginkgo.GinkgoWriter.Printf(format, args)
} }

View file

@ -22,9 +22,7 @@ import (
"time" "time"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -242,19 +240,25 @@ func NewConfig() (*restclient.Config, *kubernetes.Clientset, crclient.Client) {
kcPath := os.Getenv("KUBECONFIG") kcPath := os.Getenv("KUBECONFIG")
if kcPath != "" { if kcPath != "" {
kubeConfig, err = clientcmd.BuildConfigFromFlags("", kcPath) kubeConfig, err = clientcmd.BuildConfigFromFlags("", kcPath)
Expect(err).NotTo(HaveOccurred()) if err != nil {
Fail(err.Error())
}
} else { } else {
kubeConfig, err = restclient.InClusterConfig() kubeConfig, err = restclient.InClusterConfig()
Expect(err).NotTo(HaveOccurred()) if err != nil {
Fail(err.Error())
}
} }
By("creating a kubernetes client")
kubeClientSet, err := kubernetes.NewForConfig(kubeConfig) kubeClientSet, err := kubernetes.NewForConfig(kubeConfig)
Expect(err).NotTo(HaveOccurred()) if err != nil {
Fail(err.Error())
}
By("creating a controller-runtime client")
CRClient, err := crclient.New(kubeConfig, crclient.Options{Scheme: Scheme}) CRClient, err := crclient.New(kubeConfig, crclient.Options{Scheme: Scheme})
Expect(err).NotTo(HaveOccurred()) if err != nil {
Fail(err.Error())
}
return kubeConfig, kubeClientSet, CRClient return kubeConfig, kubeClientSet, CRClient
} }

View file

@ -1,12 +0,0 @@
installCRDs: false
image:
repository: local/external-secrets
tag: test
scopedNamespace: test
extraEnv:
- name: AWS_SECRETSMANAGER_ENDPOINT
value: "http://localstack.default"
- name: AWS_STS_ENDPOINT
value: "http://localstack.default"
- name: AWS_SSM_ENDPOINT
value: "http://localstack.default"

View file

@ -1,11 +0,0 @@
installCRDs: false
image:
repository: local/external-secrets
tag: test
extraEnv:
- name: AWS_SECRETSMANAGER_ENDPOINT
value: "http://localstack.default"
- name: AWS_STS_ENDPOINT
value: "http://localstack.default"
- name: AWS_SSM_ENDPOINT
value: "http://localstack.default"

View file

@ -42,20 +42,22 @@ done
kubectl apply -f ${DIR}/k8s/deploy/crds kubectl apply -f ${DIR}/k8s/deploy/crds
echo -e "Starting the e2e test pod" echo -e "Starting the e2e test pod ${E2E_IMAGE_REGISTRY}:${VERSION}"
kubectl run --rm \ kubectl run --rm \
--attach \ --attach \
--restart=Never \ --restart=Never \
--pod-running-timeout=10m \ --pod-running-timeout=5m \
--env="FOCUS=${FOCUS:-.*}" \ --env="GINKGO_LABELS=${GINKGO_LABELS:-.*}" \
--env="GCP_SM_SA_JSON=${GCP_SM_SA_JSON:-}" \ --env="GCP_SM_SA_JSON=${GCP_SM_SA_JSON:-}" \
--env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \ --env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \
--env="TF_VAR_GCP_PROJECT_ID=${TF_VAR_GCP_PROJECT_ID:-}" \
--env="GCP_GSA_NAME=${GCP_GSA_NAME:-}" \ --env="GCP_GSA_NAME=${GCP_GSA_NAME:-}" \
--env="GCP_KSA_NAME=${GCP_KSA_NAME:-}" \ --env="GCP_GKE_ZONE=${GCP_GKE_ZONE:-}" \
--env="TF_VAR_GCP_GSA_NAME=${TF_VAR_GCP_GSA_NAME:-}" \ --env="GCP_GKE_CLUSTER=${GCP_GKE_CLUSTER:-}" \
--env="TF_VAR_GCP_KSA_NAME=${TF_VAR_GCP_KSA_NAME:-}" \ --env="AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}" \
--env="AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}" \
--env="AWS_SA_NAME=${AWS_SA_NAME:-}" \
--env="AWS_SA_NAMESPACE=${AWS_SA_NAMESPACE:-}" \
--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_ID=${AKEYLESS_ACCESS_ID:-}" \
@ -70,5 +72,7 @@ kubectl run --rm \
--env="ORACLE_REGION=${ORACLE_REGION:-}" \ --env="ORACLE_REGION=${ORACLE_REGION:-}" \
--env="ORACLE_FINGERPRINT=${ORACLE_FINGERPRINT:-}" \ --env="ORACLE_FINGERPRINT=${ORACLE_FINGERPRINT:-}" \
--env="ORACLE_KEY=${ORACLE_KEY:-}" \ --env="ORACLE_KEY=${ORACLE_KEY:-}" \
--env="IMAGE_REGISTRY=${IMAGE_REGISTRY}" \
--env="VERSION=${VERSION}" \
--overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \ --overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \
e2e --image=${E2E_IMAGE_REGISTRY}:${E2E_VERSION} e2e --image=${E2E_IMAGE_REGISTRY}:${VERSION}

View file

@ -15,23 +15,19 @@ limitations under the License.
package akeyless package akeyless
import ( import (
"os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
) )
var _ = Describe("[akeyless] ", func() { var _ = Describe("[akeyless]", Label("akeyless"), func() {
f := framework.New("eso-akeyless") f := framework.New("eso-akeyless")
accessID := os.Getenv("AKEYLESS_ACCESS_ID") prov := newFromEnv(f)
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), DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)), Entry(common.SimpleDataSync(f)),

View file

@ -29,7 +29,7 @@ import (
"github.com/akeylesslabs/akeyless-go/v2" "github.com/akeylesslabs/akeyless-go/v2"
//nolint //nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
//nolint //nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -75,6 +75,13 @@ func newAkeylessProvider(f *framework.Framework, accessID, accessType, accessTyp
return prov return prov
} }
func newFromEnv(f *framework.Framework) *akeylessProvider {
accessID := os.Getenv("AKEYLESS_ACCESS_ID")
accessType := os.Getenv("AKEYLESS_ACCESS_TYPE")
accessTypeParam := os.Getenv("AKEYLESS_ACCESS_TYPE_PARAM")
return newAkeylessProvider(f, accessID, accessType, accessTypeParam)
}
// CreateSecret creates a secret. // CreateSecret creates a secret.
func (a *akeylessProvider) CreateSecret(key, val string) { func (a *akeylessProvider) CreateSecret(key, val string) {
token, err := a.GetToken() token, err := a.GetToken()

View file

@ -15,27 +15,19 @@ limitations under the License.
package alibaba package alibaba
import ( import (
"os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
) )
var _ = Describe("[alibaba] ", func() { var _ = Describe("[alibaba]", Label("alibaba"), func() {
f := framework.New("eso-alibaba") f := framework.New("eso-alibaba")
accessKeyID := os.Getenv("ACCESS_KEY_ID") prov := newFromEnv(f)
accessKeySecret := os.Getenv("ACCESS_KEY_SECRET")
regionID := os.Getenv("REGION_ID")
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)),

View file

@ -16,11 +16,12 @@ package alibaba
import ( import (
"context" "context"
"os"
"github.com/aliyun/alibaba-cloud-sdk-go/services/kms" "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
//nolint //nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
//nolint //nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -54,6 +55,13 @@ func newAlibabaProvider(f *framework.Framework, accessKeyID, accessKeySecret, re
return prov return prov
} }
func newFromEnv(f *framework.Framework) *alibabaProvider {
accessKeyID := os.Getenv("ACCESS_KEY_ID")
accessKeySecret := os.Getenv("ACCESS_KEY_SECRET")
regionID := os.Getenv("REGION_ID")
return newAlibabaProvider(f, accessKeyID, accessKeySecret, regionID)
}
// CreateSecret creates a secret in both kv v1 and v2 provider. // CreateSecret creates a secret in both kv v1 and v2 provider.
func (s *alibabaProvider) CreateSecret(key, val string) { func (s *alibabaProvider) CreateSecret(key, val string) {
client, err := kms.NewClientWithAccessKey(s.regionID, s.accessKeyID, s.accessKeySecret) client, err := kms.NewClientWithAccessKey(s.regionID, s.accessKeyID, s.accessKeySecret)

View file

@ -16,6 +16,8 @@ package aws
import ( import (
"context" "context"
"os"
"time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
@ -23,79 +25,194 @@ import (
"github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/secretsmanager"
//nolint //nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1" esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/pkg/provider/aws/auth" "github.com/external-secrets/external-secrets/e2e/framework/log"
) )
type SMProvider struct { type SMProvider struct {
url string ServiceAccountName string
ServiceAccountNamespace string
kid string
sak string
region string
client *secretsmanager.SecretsManager client *secretsmanager.SecretsManager
framework *framework.Framework framework *framework.Framework
} }
const secretName = "provider-secret" const (
staticCredentialsSecretName = "provider-secret"
)
func newSMProvider(f *framework.Framework, url string) *SMProvider { func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *SMProvider {
sess, err := session.NewSessionWithOptions(session.Options{ sess, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{ Config: aws.Config{
Credentials: credentials.NewStaticCredentials("foobar", "foobar", "secret-manager"), Credentials: credentials.NewStaticCredentials(kid, sak, ""),
EndpointResolver: auth.ResolveEndpointWithServiceMap(map[string]string{ Region: aws.String(region),
"secretsmanager": url,
}),
Region: aws.String("eu-east-1"),
}, },
}) })
Expect(err).ToNot(HaveOccurred()) if err != nil {
Fail(err.Error())
}
sm := secretsmanager.New(sess) sm := secretsmanager.New(sess)
prov := &SMProvider{ prov := &SMProvider{
url: url, ServiceAccountName: saName,
client: sm, ServiceAccountNamespace: saNamespace,
framework: f, kid: kid,
sak: sak,
region: region,
client: sm,
framework: f,
} }
BeforeEach(prov.BeforeEach)
BeforeEach(func() {
prov.SetupStaticStore()
prov.SetupReferencedIRSAStore()
prov.SetupMountedIRSAStore()
})
AfterEach(func() {
// Cleanup ClusterSecretStore
err := prov.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: prov.ReferencedIRSAStoreName(),
},
})
Expect(err).ToNot(HaveOccurred())
})
return prov return prov
} }
func NewFromEnv(f *framework.Framework) *SMProvider {
kid := os.Getenv("AWS_ACCESS_KEY_ID")
sak := os.Getenv("AWS_SECRET_ACCESS_KEY")
region := "eu-west-1"
saName := os.Getenv("AWS_SA_NAME")
saNamespace := os.Getenv("AWS_SA_NAMESPACE")
return NewSMProvider(f, kid, sak, region, saName, saNamespace)
}
// CreateSecret creates a secret at the provider.
func (s *SMProvider) CreateSecret(key, val string) { func (s *SMProvider) CreateSecret(key, val string) {
_, err := s.client.CreateSecret(&secretsmanager.CreateSecretInput{ // we re-use some secret names throughout our test suite
Name: aws.String(key), // due to the fact that there is a short delay before the secret is actually deleted
SecretString: aws.String(val), // we have to retry creating the secret
}) attempts := 20
Expect(err).ToNot(HaveOccurred()) for {
log.Logf("creating secret %s / attempts left: %d", key, attempts)
_, err := s.client.CreateSecret(&secretsmanager.CreateSecretInput{
Name: aws.String(key),
SecretString: aws.String(val),
})
if err == nil {
return
}
attempts--
if attempts < 0 {
Fail("unable to create secret: " + err.Error())
}
<-time.After(time.Second * 5)
}
} }
// DeleteSecret deletes a secret at the provider.
// There may be a short delay between calling this function
// and the removal of the secret on the provider side.
func (s *SMProvider) DeleteSecret(key string) { func (s *SMProvider) DeleteSecret(key string) {
log.Logf("deleting secret %s", key)
_, err := s.client.DeleteSecret(&secretsmanager.DeleteSecretInput{ _, err := s.client.DeleteSecret(&secretsmanager.DeleteSecretInput{
SecretId: aws.String(key), SecretId: aws.String(key),
ForceDeleteWithoutRecovery: aws.Bool(true),
}) })
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
func (s *SMProvider) BeforeEach() { // MountedIRSAStore is a SecretStore without auth config
By("creating a AWS SM credentials secret") // ESO relies on the pod-mounted ServiceAccount when using this store.
func (s *SMProvider) SetupMountedIRSAStore() {
secretStore := &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.MountedIRSAStoreName(),
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager,
Region: s.region,
Auth: esv1alpha1.AWSAuth{},
},
},
},
}
err := s.framework.CRClient.Create(context.Background(), secretStore)
Expect(err).ToNot(HaveOccurred())
}
func (s *SMProvider) MountedIRSAStoreName() string {
return "irsa-mounted-" + s.framework.Namespace.Name
}
// ReferncedIRSAStore is a ClusterStore
// that references a (IRSA-) ServiceAccount in the default namespace.
func (s *SMProvider) SetupReferencedIRSAStore() {
log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
secretStore := &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.ReferencedIRSAStoreName(),
},
}
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
secretStore.Spec.Provider = &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager,
Region: s.region,
Auth: esv1alpha1.AWSAuth{
JWTAuth: &esv1alpha1.AWSJWTAuth{
ServiceAccountRef: &esmetav1.ServiceAccountSelector{
Name: s.ServiceAccountName,
Namespace: &s.ServiceAccountNamespace,
},
},
},
},
}
return nil
})
Expect(err).ToNot(HaveOccurred())
}
func (s *SMProvider) ReferencedIRSAStoreName() string {
return "irsa-ref-" + s.framework.Namespace.Name
}
// StaticStore is namespaced and references
// static credentials from a secret.
func (s *SMProvider) SetupStaticStore() {
awsCreds := &v1.Secret{ awsCreds := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: secretName, Name: staticCredentialsSecretName,
Namespace: s.framework.Namespace.Name, Namespace: s.framework.Namespace.Name,
}, },
StringData: map[string]string{ StringData: map[string]string{
"kid": "foobar", "kid": s.kid,
"sak": "foobar", "sak": s.sak,
}, },
} }
err := s.framework.CRClient.Create(context.Background(), awsCreds) err := s.framework.CRClient.Create(context.Background(), awsCreds)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
By("creating a AWS SM secret store")
secretStore := &esv1alpha1.SecretStore{ secretStore := &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name, Name: s.framework.Namespace.Name,
@ -105,15 +222,15 @@ func (s *SMProvider) BeforeEach() {
Provider: &esv1alpha1.SecretStoreProvider{ Provider: &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{ AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager, Service: esv1alpha1.AWSServiceSecretsManager,
Region: "us-east-1", Region: s.region,
Auth: esv1alpha1.AWSAuth{ Auth: esv1alpha1.AWSAuth{
SecretRef: &esv1alpha1.AWSAuthSecretRef{ SecretRef: &esv1alpha1.AWSAuthSecretRef{
AccessKeyID: esmeta.SecretKeySelector{ AccessKeyID: esmetav1.SecretKeySelector{
Name: secretName, Name: staticCredentialsSecretName,
Key: "kid", Key: "kid",
}, },
SecretAccessKey: esmeta.SecretKeySelector{ SecretAccessKey: esmetav1.SecretKeySelector{
Name: secretName, Name: staticCredentialsSecretName,
Key: "sak", Key: "sak",
}, },
}, },

View file

@ -15,103 +15,17 @@ limitations under the License.
package aws package aws
import ( import (
"context"
"fmt"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
// nolint
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
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" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
) )
var _ = Describe("[aws] ", func() { var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
f := framework.New("eso-aws") f := framework.New("eso-aws-sm")
prov := newSMProvider(f, "http://localstack.default") prov := NewFromEnv(f)
jwt := func(tc *framework.TestCase) {
saName := "my-sa"
err := f.CRClient.Create(context.Background(), &v1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: saName,
Namespace: f.Namespace.Name,
Annotations: map[string]string{
"eks.amazonaws.com/role-arn": "arn:aws:iam::account:role/my-example-role",
},
},
})
Expect(err).ToNot(HaveOccurred())
// create secret store
secretStore := &esv1alpha1.SecretStore{
TypeMeta: metav1.TypeMeta{
Kind: esv1alpha1.SecretStoreKind,
APIVersion: esv1alpha1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: f.Namespace.Name,
Namespace: f.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
AWS: &esv1alpha1.AWSProvider{
Service: esv1alpha1.AWSServiceSecretsManager,
Region: "us-east-1",
Auth: esv1alpha1.AWSAuth{
JWTAuth: &esv1alpha1.AWSJWTAuth{
ServiceAccountRef: &esmeta.ServiceAccountSelector{
Name: saName,
Namespace: &f.Namespace.Name,
},
},
},
},
},
},
}
err = f.CRClient.Patch(context.Background(), secretStore, client.Apply, client.FieldOwner("e2e-case"), client.ForceOwnership)
Expect(err).ToNot(HaveOccurred())
secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "other")
secretValue := "bar"
tc.Secrets = map[string]string{
secretKey1: secretValue,
secretKey2: secretValue,
}
tc.ExpectedSecret = &v1.Secret{
Type: v1.SecretTypeOpaque,
Data: map[string][]byte{
secretKey1: []byte(secretValue),
secretKey2: []byte(secretValue),
},
}
tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
{
SecretKey: secretKey1,
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Key: secretKey1,
},
},
{
SecretKey: secretKey2,
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Key: secretKey2,
},
},
}
}
DescribeTable("sync secrets", DescribeTable("sync secrets",
framework.TableFunc(f, framework.TableFunc(f,
@ -121,7 +35,6 @@ var _ = Describe("[aws] ", func() {
Entry(common.JSONDataFromSync(f)), Entry(common.JSONDataFromSync(f)),
Entry(common.JSONDataWithProperty(f)), Entry(common.JSONDataWithProperty(f)),
Entry(common.JSONDataWithTemplate(f)), Entry(common.JSONDataWithTemplate(f)),
Entry("should sync secrets with jwt auth", jwt),
Entry(common.DockerJSONConfig(f)), Entry(common.DockerJSONConfig(f)),
Entry(common.DataPropertyDockerconfigJSON(f)), Entry(common.DataPropertyDockerconfigJSON(f)),
Entry(common.SSHKeySync(f)), Entry(common.SSHKeySync(f)),

View file

@ -0,0 +1,102 @@
/*
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 aws
import (
// nolint
. "github.com/onsi/ginkgo/v2"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/framework/addon"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
const (
withReferencedIRSA = "with referenced IRSA"
withMountedIRSA = "with mounted IRSA"
)
// here we use the global eso instance
// that uses the service account in the default namespace
// which was created by terraform.
var _ = Describe("[awsmanaged] IRSA via referenced service account", Label("aws", "secretsmanager", "managed"), func() {
f := framework.New("eso-aws-managed")
prov := NewFromEnv(f)
DescribeTable("sync secrets",
framework.TableFunc(f,
prov),
framework.Compose(withReferencedIRSA, f, common.SimpleDataSync, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.NestedJSONWithGJSON, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.JSONDataFromSync, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.JSONDataWithProperty, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.JSONDataWithTemplate, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.DockerJSONConfig, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.DataPropertyDockerconfigJSON, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.SSHKeySync, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.SSHKeySyncDataProperty, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.SyncWithoutTargetName, useClusterSecretStore(prov)),
framework.Compose(withReferencedIRSA, f, common.JSONDataWithoutTargetName, useClusterSecretStore(prov)),
)
})
// here we create a central eso instance in the default namespace
// that mounts the service account which was created by terraform.
var _ = Describe("[awsmanaged] with mounted IRSA", Label("aws", "secretsmanager", "managed"), func() {
f := framework.New("eso-aws-managed")
prov := NewFromEnv(f)
// each test case gets its own ESO instance
BeforeEach(func() {
f.Install(addon.NewESO(
addon.WithControllerClass(f.BaseName),
addon.WithServiceAccount(prov.ServiceAccountName),
addon.WithReleaseName(f.Namespace.Name),
addon.WithNamespace("default"),
))
})
DescribeTable("sync secrets",
framework.TableFunc(f,
prov),
framework.Compose(withMountedIRSA, f, common.SimpleDataSync, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.NestedJSONWithGJSON, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.JSONDataFromSync, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.JSONDataWithProperty, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.JSONDataWithTemplate, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.DockerJSONConfig, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.DataPropertyDockerconfigJSON, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.SSHKeySync, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.SSHKeySyncDataProperty, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.SyncWithoutTargetName, useMountedIRSAStore(prov)),
framework.Compose(withMountedIRSA, f, common.JSONDataWithoutTargetName, useMountedIRSAStore(prov)),
)
})
func useClusterSecretStore(prov *SMProvider) func(*framework.TestCase) {
return func(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind
tc.ExternalSecret.Spec.SecretStoreRef.Name = prov.ReferencedIRSAStoreName()
}
}
func useMountedIRSAStore(prov *SMProvider) func(*framework.TestCase) {
return func(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.SecretStoreKind
tc.ExternalSecret.Spec.SecretStoreRef.Name = prov.MountedIRSAStoreName()
}
}

View file

@ -13,28 +13,17 @@ limitations under the License.
package azure package azure
import ( import (
"os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
) )
var _ = Describe("[azure] ", func() { var _ = Describe("[azure]", Label("azure", "keyvault"), func() {
f := framework.New("eso-azure") f := framework.New("eso-azure")
vaultURL := os.Getenv("VAULT_URL") prov := newFromEnv(f)
tenantID := os.Getenv("TENANT_ID")
clientID := os.Getenv("AZURE_CLIENT_ID")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
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)),

View file

@ -14,15 +14,17 @@ package azure
import ( import (
"context" "context"
"os"
"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault" "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
kvauth "github.com/Azure/go-autorest/autorest/azure/auth" kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/gomega"
// nolint // nolint
. "github.com/onsi/gomega" . "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
@ -45,7 +47,9 @@ func newazureProvider(f *framework.Framework, clientID, clientSecret, tenantID,
clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID) clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
clientCredentialsConfig.Resource = "https://vault.azure.net" clientCredentialsConfig.Resource = "https://vault.azure.net"
authorizer, err := clientCredentialsConfig.Authorizer() authorizer, err := clientCredentialsConfig.Authorizer()
Expect(err).ToNot(HaveOccurred()) if err != nil {
Fail(err.Error())
}
basicClient := keyvault.New() basicClient := keyvault.New()
basicClient.Authorizer = authorizer basicClient.Authorizer = authorizer
@ -57,10 +61,22 @@ func newazureProvider(f *framework.Framework, clientID, clientSecret, tenantID,
vaultURL: vaultURL, vaultURL: vaultURL,
client: &basicClient, client: &basicClient,
} }
BeforeEach(prov.BeforeEach)
BeforeEach(func() {
prov.CreateSecretStore()
})
return prov return prov
} }
func newFromEnv(f *framework.Framework) *azureProvider {
vaultURL := os.Getenv("VAULT_URL")
tenantID := os.Getenv("TENANT_ID")
clientID := os.Getenv("AZURE_CLIENT_ID")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
return newazureProvider(f, clientID, clientSecret, tenantID, vaultURL)
}
func (s *azureProvider) CreateSecret(key, val string) { func (s *azureProvider) CreateSecret(key, val string) {
_, err := s.client.SetSecret( _, err := s.client.SetSecret(
context.Background(), context.Background(),
@ -84,7 +100,7 @@ func (s *azureProvider) DeleteSecret(key string) {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
func (s *azureProvider) BeforeEach() { func (s *azureProvider) CreateSecretStore() {
azureCreds := &v1.Secret{ azureCreds := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "provider-secret", Name: "provider-secret",

View file

@ -17,12 +17,9 @@ import (
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
p12 "software.sslmate.com/src/go-pkcs12" p12 "software.sslmate.com/src/go-pkcs12"
@ -32,21 +29,32 @@ import (
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
) )
var _ = Describe("[gcp] ", func() { // This test uses the global ESO.
var _ = Describe("[gcp]", Label("gcp", "secretsmanager"), func() {
f := framework.New("eso-gcp") f := framework.New("eso-gcp")
credentials := os.Getenv("GCP_SM_SA_JSON") prov := NewFromEnv(f, "")
projectID := os.Getenv("GCP_PROJECT_ID")
prov := &GcpProvider{}
if credentials != "" && projectID != "" { DescribeTable("sync secrets", framework.TableFunc(f, prov),
prov = NewgcpProvider(f, credentials, projectID, "", "", "", "") Entry(common.SimpleDataSync(f)),
} Entry(common.JSONDataWithProperty(f)),
Entry(common.JSONDataFromSync(f)),
Entry(common.NestedJSONWithGJSON(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)),
Entry("should sync p12 encoded cert secret", p12Cert),
)
})
// 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.
p12Cert := func(tc *framework.TestCase) { var p12Cert = func(tc *framework.TestCase) {
cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, "p12-cert-example") cloudSecretName := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "p12-cert-example")
certPEM := `-----BEGIN CERTIFICATE----- certPEM := `-----BEGIN CERTIFICATE-----
MIIFQjCCBCqgAwIBAgISBHszg5W2maz/7CIxGrf7mqukMA0GCSqGSIb3DQEBCwUA MIIFQjCCBCqgAwIBAgISBHszg5W2maz/7CIxGrf7mqukMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMTA3MjQxMjQyMzNaFw0yMTEwMjIxMjQyMzFaMCgxJjAkBgNVBAMT EwJSMzAeFw0yMTA3MjQxMjQyMzNaFw0yMTEwMjIxMjQyMzFaMCgxJjAkBgNVBAMT
@ -78,7 +86,7 @@ XMYitHfpGhc+DTTiTWMQ13J0b1j4yv8A7ZaG2366aa28oSTD6eQFhmVCBwa54j++
IOwzHn5R IOwzHn5R
-----END CERTIFICATE----- -----END CERTIFICATE-----
` `
privkeyPEM := `-----BEGIN PRIVATE KEY----- privkeyPEM := `-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDJFE51myQDyqca MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDJFE51myQDyqca
egyBDlHLkxVj+WCjcfOWEqrTa7bcnbDXjD4uIRTaFxIkpi/k5fKxt+rszna7bNdh egyBDlHLkxVj+WCjcfOWEqrTa7bcnbDXjD4uIRTaFxIkpi/k5fKxt+rszna7bNdh
lezqSuRBmVg2kXDul5nQm1RtWRKlJP9fhvUYkoNKRGzt9OL6/6lv05P2tNu13yN8 lezqSuRBmVg2kXDul5nQm1RtWRKlJP9fhvUYkoNKRGzt9OL6/6lv05P2tNu13yN8
@ -107,55 +115,39 @@ Jdx0ECYawviQoreDAyIXV6HouoeRbDtLZ9AJvxMoIjGcjAR2FQHc3yx4h/lf3Tfx
x6HaRh+EUwU51von6M9lEF9/p5Q= x6HaRh+EUwU51von6M9lEF9/p5Q=
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
` `
blockCert, _ := pem.Decode([]byte(certPEM)) blockCert, _ := pem.Decode([]byte(certPEM))
cert, _ := x509.ParseCertificate(blockCert.Bytes) cert, _ := x509.ParseCertificate(blockCert.Bytes)
blockPrivKey, _ := pem.Decode([]byte(privkeyPEM)) blockPrivKey, _ := pem.Decode([]byte(privkeyPEM))
privkey, _ := x509.ParsePKCS8PrivateKey(blockPrivKey.Bytes) privkey, _ := x509.ParsePKCS8PrivateKey(blockPrivKey.Bytes)
emptyCACerts := []*x509.Certificate{} emptyCACerts := []*x509.Certificate{}
p12Cert, _ := p12.Encode(rand.Reader, privkey, cert, emptyCACerts, "") p12Cert, _ := p12.Encode(rand.Reader, privkey, cert, emptyCACerts, "")
tc.Secrets = map[string]string{ tc.Secrets = map[string]string{
cloudSecretName: string(p12Cert), cloudSecretName: string(p12Cert),
}
tc.ExpectedSecret = &v1.Secret{
Type: v1.SecretTypeTLS,
Data: map[string][]byte{
"tls.crt": []byte(certPEM),
"tls.key": []byte(privkeyPEM),
},
}
tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
{
SecretKey: "mysecret",
RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Key: cloudSecretName,
},
},
}
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
Type: v1.SecretTypeTLS,
Data: map[string]string{
"tls.crt": "{{ .mysecret | pkcs12cert | pemCertificate }}",
"tls.key": "{{ .mysecret | pkcs12key | pemPrivateKey }}",
},
}
} }
DescribeTable("sync secrets", framework.TableFunc(f, prov), tc.ExpectedSecret = &v1.Secret{
Entry(common.SimpleDataSync(f)), Type: v1.SecretTypeTLS,
Entry(common.JSONDataWithProperty(f)), Data: map[string][]byte{
Entry(common.JSONDataFromSync(f)), "tls.crt": []byte(certPEM),
Entry(common.NestedJSONWithGJSON(f)), "tls.key": []byte(privkeyPEM),
Entry(common.JSONDataWithTemplate(f)), },
Entry(common.DockerJSONConfig(f)), }
Entry(common.DataPropertyDockerconfigJSON(f)),
Entry(common.SSHKeySync(f)), tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
Entry(common.SSHKeySyncDataProperty(f)), {
Entry(common.SyncWithoutTargetName(f)), SecretKey: "mysecret",
Entry(common.JSONDataWithoutTargetName(f)), RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
Entry("should sync p12 encoded cert secret", p12Cert), Key: cloudSecretName,
) },
}) },
}
tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
Type: v1.SecretTypeTLS,
Data: map[string]string{
"tls.crt": "{{ .mysecret | pkcs12cert | pemCertificate }}",
"tls.key": "{{ .mysecret | pkcs12key | pemPrivateKey }}",
},
}
}

View file

@ -0,0 +1,111 @@
/*
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.
limitations under the License.
*/
package gcp
import (
// nolint
. "github.com/onsi/ginkgo/v2"
// nolint
// . "github.com/onsi/gomega"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/framework/addon"
"github.com/external-secrets/external-secrets/e2e/suite/common"
)
const (
withPodID = "sync secrets with pod identity"
withSpecifcSA = "sync secrets with specificSA identity"
)
// Deploys eso to the default namespace
// that uses the service account provisioned by terraform
// to test pod-identity authentication.
var _ = Describe("[gcpmanaged] with pod identity", Label("gcp", "secretsmanager", "managed", "pod-identity"), func() {
f := framework.New("eso-gcpmanaged")
prov := NewFromEnv(f, f.BaseName)
// each test case gets its own ESO instance
BeforeEach(func() {
f.Install(addon.NewESO(
addon.WithControllerClass(f.BaseName),
addon.WithServiceAccount(prov.ServiceAccountName),
addon.WithReleaseName(f.Namespace.Name),
addon.WithNamespace("default"),
))
})
DescribeTable("sync secrets",
framework.TableFunc(f,
prov),
// uses pod id
framework.Compose(withPodID, f, common.SimpleDataSync, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataFromSync, usePodIDESReference),
framework.Compose(withPodID, f, common.NestedJSONWithGJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithTemplate, usePodIDESReference),
framework.Compose(withPodID, f, common.DockerJSONConfig, usePodIDESReference),
framework.Compose(withPodID, f, common.DataPropertyDockerconfigJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySync, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySyncDataProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.SyncWithoutTargetName, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithoutTargetName, usePodIDESReference),
)
})
// We're using a namespace scoped ESO
// that runs WITHOUT pod identity (with default sa)
// It uses a specific service account defined in the ClusterSecretStore spec
// to authenticate against cloud provider APIs.
var _ = Describe("[gcpmanaged] with service account", Label("gcp", "secretsmanager", "managed", "service-account"), func() {
f := framework.New("eso-gcpmanaged")
prov := NewFromEnv(f, f.BaseName)
BeforeEach(func() {
f.Install(addon.NewESO(
addon.WithControllerClass(f.BaseName),
addon.WithReleaseName(f.Namespace.Name),
addon.WithNamespace(f.Namespace.Name),
))
})
DescribeTable("sync secrets",
framework.TableFunc(f,
prov),
// uses specific sa
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataWithProperty, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.NestedJSONWithGJSON, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataWithTemplate, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.DockerJSONConfig, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.DataPropertyDockerconfigJSON, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.SSHKeySync, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.SSHKeySyncDataProperty, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.SyncWithoutTargetName, useSpecifcSAESReference(prov)),
framework.Compose(withSpecifcSA, f, common.JSONDataWithoutTargetName, useSpecifcSAESReference(prov)),
)
})
func usePodIDESReference(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = PodIDSecretStoreName
}
func useSpecifcSAESReference(prov *GcpProvider) func(*framework.TestCase) {
return func(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind
tc.ExternalSecret.Spec.SecretStoreRef.Name = prov.SAClusterSecretStoreName()
}
}

View file

@ -15,116 +15,97 @@ package gcp
import ( import (
"context" "context"
"fmt" "fmt"
"os"
secretmanager "cloud.google.com/go/secretmanager/apiv1" secretmanager "cloud.google.com/go/secretmanager/apiv1"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt" "golang.org/x/oauth2/jwt"
"google.golang.org/api/option" "google.golang.org/api/option"
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1" esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1" esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/framework/log"
gcpsm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager" gcpsm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
) )
const ( const (
PodIDSecretStoreName = "pod-identity" PodIDSecretStoreName = "pod-identity"
SpecifcSASecretStoreName = "specific-sa" staticCredentialsSecretName = "provider-secret"
) )
func makeStore(s *GcpProvider) *esv1alpha1.SecretStore {
return &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name,
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
GCPSM: &esv1alpha1.GCPSMProvider{
ProjectID: s.projectID,
},
},
},
}
}
func makeCStore(s *GcpProvider) *esv1alpha1.ClusterSecretStore {
return &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name,
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Provider: &esv1alpha1.SecretStoreProvider{
GCPSM: &esv1alpha1.GCPSMProvider{
ProjectID: s.projectID,
},
},
},
}
}
// nolint // Better to keep names consistent even if it stutters; // nolint // Better to keep names consistent even if it stutters;
type GcpProvider struct { type GcpProvider struct {
credentials string ServiceAccountName string
projectID string ServiceAccountNamespace string
framework *framework.Framework
clusterLocation string framework *framework.Framework
clusterName string credentials string
serviceAccountName string projectID string
serviceAccountNamespace string clusterLocation string
clusterName string
controllerClass string
} }
func NewgcpProvider(f *framework.Framework, credentials, projectID string, func NewGCPProvider(f *framework.Framework, credentials, projectID string,
clusterLocation string, clusterName string, serviceAccountName string, serviceAccountNamespace string) *GcpProvider { clusterLocation string, clusterName string, serviceAccountName string, serviceAccountNamespace string, controllerClass string) *GcpProvider {
prov := &GcpProvider{ prov := &GcpProvider{
credentials: credentials, credentials: credentials,
projectID: projectID, projectID: projectID,
framework: f, framework: f,
clusterLocation: clusterLocation, clusterLocation: clusterLocation,
clusterName: clusterName, clusterName: clusterName,
serviceAccountName: serviceAccountName, ServiceAccountName: serviceAccountName,
serviceAccountNamespace: serviceAccountNamespace, ServiceAccountNamespace: serviceAccountNamespace,
controllerClass: controllerClass,
} }
BeforeEach(prov.BeforeEach)
BeforeEach(func() {
prov.CreateSAKeyStore(f.Namespace.Name)
prov.CreateSpecifcSASecretStore(f.Namespace.Name)
prov.CreatePodIDStore(f.Namespace.Name)
})
AfterEach(func() {
prov.DeleteSpecifcSASecretStore()
})
return prov return prov
} }
func (s *GcpProvider) getClient(ctx context.Context, credentials string) (client *secretmanager.Client, err error) { func NewFromEnv(f *framework.Framework, controllerClass string) *GcpProvider {
if credentials == "" { projectID := os.Getenv("GCP_PROJECT_ID")
var ts oauth2.TokenSource credentials := os.Getenv("GCP_SM_SA_JSON")
ts, err = google.DefaultTokenSource(ctx, gcpsm.CloudPlatformRole) serviceAccountName := os.Getenv("GCP_KSA_NAME")
Expect(err).ToNot(HaveOccurred()) serviceAccountNamespace := "default"
client, err = secretmanager.NewClient(ctx, option.WithTokenSource(ts)) clusterLocation := os.Getenv("GCP_GKE_ZONE")
Expect(err).ToNot(HaveOccurred()) clusterName := os.Getenv("GCP_GKE_CLUSTER")
} else { return NewGCPProvider(f, credentials, projectID, clusterLocation, clusterName, serviceAccountName, serviceAccountNamespace, controllerClass)
var config *jwt.Config }
config, err = google.JWTConfigFromJSON([]byte(s.credentials), gcpsm.CloudPlatformRole)
Expect(err).ToNot(HaveOccurred()) func (s *GcpProvider) getClient(ctx context.Context) (client *secretmanager.Client, err error) {
ts := config.TokenSource(ctx) var config *jwt.Config
client, err = secretmanager.NewClient(ctx, option.WithTokenSource(ts)) config, err = google.JWTConfigFromJSON([]byte(s.credentials), gcpsm.CloudPlatformRole)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} ts := config.TokenSource(ctx)
client, err = secretmanager.NewClient(ctx, option.WithTokenSource(ts))
Expect(err).ToNot(HaveOccurred())
return client, err return client, err
} }
func (s *GcpProvider) CreateSecret(key, val string) { func (s *GcpProvider) CreateSecret(key, val string) {
ctx := context.Background() ctx := context.Background()
client, err := s.getClient(ctx, s.credentials) client, err := s.getClient(ctx)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
defer client.Close() defer client.Close()
// Create the request to create the secret. // Create the request to create the secret.
@ -153,7 +134,7 @@ func (s *GcpProvider) CreateSecret(key, val string) {
func (s *GcpProvider) DeleteSecret(key string) { func (s *GcpProvider) DeleteSecret(key string) {
ctx := context.Background() ctx := context.Background()
client, err := s.getClient(ctx, s.credentials) client, err := s.getClient(ctx)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
defer client.Close() defer client.Close()
@ -164,11 +145,27 @@ func (s *GcpProvider) DeleteSecret(key string) {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
func (s *GcpProvider) BeforeEach() { func makeStore(s *GcpProvider) *esv1alpha1.SecretStore {
By("creating a gcp secret") return &esv1alpha1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.framework.Namespace.Name,
Namespace: s.framework.Namespace.Name,
},
Spec: esv1alpha1.SecretStoreSpec{
Controller: s.controllerClass,
Provider: &esv1alpha1.SecretStoreProvider{
GCPSM: &esv1alpha1.GCPSMProvider{
ProjectID: s.projectID,
},
},
},
}
}
func (s *GcpProvider) CreateSAKeyStore(ns string) {
gcpCreds := &v1.Secret{ gcpCreds := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "provider-secret", Name: staticCredentialsSecretName,
Namespace: s.framework.Namespace.Name, Namespace: s.framework.Namespace.Name,
}, },
StringData: map[string]string{ StringData: map[string]string{
@ -180,23 +177,16 @@ func (s *GcpProvider) BeforeEach() {
err = s.framework.CRClient.Update(context.Background(), gcpCreds) err = s.framework.CRClient.Update(context.Background(), gcpCreds)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
By("creating an secret stores gcp")
s.CreateSAKeyStore(s.framework.Namespace.Name)
s.CreatePodIDStore(s.framework.Namespace.Name)
s.CreateSpecifcSASecretStore(s.framework.Namespace.Name)
}
func (s *GcpProvider) CreateSAKeyStore(ns string) {
secretStore := makeStore(s) secretStore := makeStore(s)
secretStore.Spec.Provider.GCPSM.Auth = esv1alpha1.GCPSMAuth{ secretStore.Spec.Provider.GCPSM.Auth = esv1alpha1.GCPSMAuth{
SecretRef: &esv1alpha1.GCPSMAuthSecretRef{ SecretRef: &esv1alpha1.GCPSMAuthSecretRef{
SecretAccessKey: esmeta.SecretKeySelector{ SecretAccessKey: esmeta.SecretKeySelector{
Name: "provider-secret", Name: staticCredentialsSecretName,
Key: "secret-access-credentials", Key: "secret-access-credentials",
}, },
}, },
} }
err := s.framework.CRClient.Create(context.Background(), secretStore) err = s.framework.CRClient.Create(context.Background(), secretStore)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
@ -207,29 +197,45 @@ func (s *GcpProvider) CreatePodIDStore(ns string) {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
func (s *GcpProvider) SAClusterSecretStoreName() string {
return "gcpsa-" + s.framework.Namespace.Name
}
func (s *GcpProvider) CreateSpecifcSASecretStore(ns string) { func (s *GcpProvider) CreateSpecifcSASecretStore(ns string) {
clusterSecretStore := makeCStore(s) clusterSecretStore := &esv1alpha1.ClusterSecretStore{
clusterSecretStore.ObjectMeta.Name = SpecifcSASecretStoreName ObjectMeta: metav1.ObjectMeta{
clusterSecretStore.Spec.Provider.GCPSM.Auth = esv1alpha1.GCPSMAuth{ Name: s.SAClusterSecretStoreName(),
WorkloadIdentity: &esv1alpha1.GCPWorkloadIdentity{
ClusterLocation: s.clusterLocation,
ClusterName: s.clusterName,
ServiceAccountRef: esmeta.ServiceAccountSelector{
Name: s.serviceAccountName,
Namespace: utilpointer.StringPtr(s.serviceAccountNamespace),
},
}, },
} }
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, clusterSecretStore, func() error {
var cSS esv1alpha1.ClusterSecretStore clusterSecretStore.Spec.Controller = s.controllerClass
clusterSecretStore.Spec.Provider = &esv1alpha1.SecretStoreProvider{
err := s.framework.CRClient.Get(context.Background(), types.NamespacedName{ GCPSM: &esv1alpha1.GCPSMProvider{
Name: SpecifcSASecretStoreName, ProjectID: s.projectID,
}, &cSS) Auth: esv1alpha1.GCPSMAuth{
if apierrors.IsNotFound(err) { WorkloadIdentity: &esv1alpha1.GCPWorkloadIdentity{
err := s.framework.CRClient.Create(context.Background(), clusterSecretStore) ClusterLocation: s.clusterLocation,
Expect(err).ToNot(HaveOccurred()) ClusterName: s.clusterName,
} else { ServiceAccountRef: esmeta.ServiceAccountSelector{
log.Logf("%s CSStore already created", SpecifcSASecretStoreName) Name: s.ServiceAccountName,
} Namespace: utilpointer.StringPtr(s.ServiceAccountNamespace),
},
},
},
},
}
return nil
})
Expect(err).ToNot(HaveOccurred())
}
// Cleanup removes global resources that may have been
// created by this provider.
func (s *GcpProvider) DeleteSpecifcSASecretStore() {
err := s.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{
ObjectMeta: metav1.ObjectMeta{
Name: s.SAClusterSecretStoreName(),
},
})
Expect(err).ToNot(HaveOccurred())
} }

View file

@ -1,86 +0,0 @@
/*
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.
limitations under the License.
*/
package gcpmanaged
import (
"os"
// nolint
. "github.com/onsi/ginkgo"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
// nolint
// . "github.com/onsi/gomega"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common"
"github.com/external-secrets/external-secrets/e2e/suite/gcp"
)
const (
withPodID = "sync secrets with pod identity"
withSpecifcSA = "sync secrets with specificSA identity"
)
var _ = Describe("[gcpmanaged] ", func() {
if os.Getenv("FOCUS") == "gcpmanaged" {
f := framework.New("eso-gcp-managed")
projectID := os.Getenv("GCP_PROJECT_ID")
clusterLocation := "europe-west1-b"
clusterName := "test-cluster"
serviceAccountName := os.Getenv("GCP_KSA_NAME")
serviceAccountNamespace := "default"
prov := &gcp.GcpProvider{}
if projectID != "" {
prov = gcp.NewgcpProvider(f, "", projectID, clusterLocation, clusterName, serviceAccountName, serviceAccountNamespace)
}
DescribeTable("sync secrets",
framework.TableFunc(f,
prov),
// uses pod id
framework.Compose(withPodID, f, common.SimpleDataSync, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataFromSync, usePodIDESReference),
framework.Compose(withPodID, f, common.NestedJSONWithGJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithTemplate, usePodIDESReference),
framework.Compose(withPodID, f, common.DockerJSONConfig, usePodIDESReference),
framework.Compose(withPodID, f, common.DataPropertyDockerconfigJSON, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySync, usePodIDESReference),
framework.Compose(withPodID, f, common.SSHKeySyncDataProperty, usePodIDESReference),
framework.Compose(withPodID, f, common.SyncWithoutTargetName, usePodIDESReference),
framework.Compose(withPodID, f, common.JSONDataWithoutTargetName, usePodIDESReference),
// uses specific sa
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataWithProperty, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataFromSync, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.NestedJSONWithGJSON, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataWithTemplate, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.DockerJSONConfig, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.DataPropertyDockerconfigJSON, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.SSHKeySync, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.SSHKeySyncDataProperty, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.SyncWithoutTargetName, useSpecifcSAESReference),
framework.Compose(withSpecifcSA, f, common.JSONDataWithoutTargetName, useSpecifcSAESReference),
)
}
})
func usePodIDESReference(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Name = gcp.PodIDSecretStoreName
}
func useSpecifcSAESReference(tc *framework.TestCase) {
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind
tc.ExternalSecret.Spec.SecretStoreRef.Name = gcp.SpecifcSASecretStoreName
}

View file

@ -18,27 +18,19 @@ package gitlab
// and in e2e/suite/common/common.go, but this breaks Azure provider. // and in e2e/suite/common/common.go, but this breaks Azure provider.
import ( import (
"os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
) )
var _ = Describe("[gitlab] ", func() { var _ = Describe("[gitlab]", Label("gitlab"), func() {
f := framework.New("esogitlab") f := framework.New("eso-gitlab")
credentials := os.Getenv("GITLAB_TOKEN") prov := newFromEnv(f)
projectID := os.Getenv("GITLAB_PROJECT_ID")
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)),

View file

@ -15,10 +15,11 @@ package gitlab
import ( import (
"context" "context"
"os"
"strings" "strings"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -47,6 +48,12 @@ func newGitlabProvider(f *framework.Framework, credentials, projectID string) *g
return prov return prov
} }
func newFromEnv(f *framework.Framework) *gitlabProvider {
credentials := os.Getenv("GITLAB_TOKEN")
projectID := os.Getenv("GITLAB_PROJECT_ID")
return newGitlabProvider(f, credentials, projectID)
}
func (s *gitlabProvider) CreateSecret(key, val string) { func (s *gitlabProvider) CreateSecret(key, val string) {
// **Open the client // **Open the client
client, err := gitlab.NewClient(s.credentials) client, err := gitlab.NewClient(s.credentials)

View file

@ -19,6 +19,5 @@ import (
_ "github.com/external-secrets/external-secrets/e2e/suite/aws" _ "github.com/external-secrets/external-secrets/e2e/suite/aws"
_ "github.com/external-secrets/external-secrets/e2e/suite/azure" _ "github.com/external-secrets/external-secrets/e2e/suite/azure"
_ "github.com/external-secrets/external-secrets/e2e/suite/gcp" _ "github.com/external-secrets/external-secrets/e2e/suite/gcp"
_ "github.com/external-secrets/external-secrets/e2e/suite/gcpmanaged"
_ "github.com/external-secrets/external-secrets/e2e/suite/vault" _ "github.com/external-secrets/external-secrets/e2e/suite/vault"
) )

View file

@ -13,25 +13,19 @@ limitations under the License.
package oracle package oracle
import ( import (
"os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/ginkgo/v2/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
) )
var _ = Describe("[oracle] ", func() { var _ = Describe("[oracle]", Label("oracle"), func() {
f := framework.New("eso-oracle") f := framework.New("eso-oracle")
tenancy := os.Getenv("OCI_TENANCY_OCID") prov := newFromEnv(f)
user := os.Getenv("OCI_USER_OCID")
region := os.Getenv("OCI_REGION")
fingerprint := os.Getenv("OCI_FINGERPRINT")
privateKey := os.Getenv("OCI_PRIVATE_KEY")
prov := newOracleProvider(f, tenancy, user, region, fingerprint, privateKey)
DescribeTable("sync secrets", framework.TableFunc(f, prov), DescribeTable("sync secrets", framework.TableFunc(f, prov),
Entry(common.SimpleDataSync(f)), Entry(common.SimpleDataSync(f)),

View file

@ -14,9 +14,10 @@ package oracle
import ( import (
"context" "context"
"os"
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint // nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@ -58,6 +59,15 @@ func newOracleProvider(f *framework.Framework, tenancy, user, region, fingerprin
return prov return prov
} }
func newFromEnv(f *framework.Framework) *oracleProvider {
tenancy := os.Getenv("OCI_TENANCY_OCID")
user := os.Getenv("OCI_USER_OCID")
region := os.Getenv("OCI_REGION")
fingerprint := os.Getenv("OCI_FINGERPRINT")
privateKey := os.Getenv("OCI_PRIVATE_KEY")
return newOracleProvider(f, tenancy, user, region, fingerprint, privateKey)
}
func (p *oracleProvider) CreateSecret(key, val string) { func (p *oracleProvider) CreateSecret(key, val string) {
configurationProvider := common.NewRawConfigurationProvider(p.tenancy, p.user, p.region, p.fingerprint, p.privateKey, nil) configurationProvider := common.NewRawConfigurationProvider(p.tenancy, p.user, p.region, p.fingerprint, p.privateKey, nil)
client, err := vault.NewVaultsClientWithConfigurationProvider(configurationProvider) client, err := vault.NewVaultsClientWithConfigurationProvider(configurationProvider)

View file

@ -21,7 +21,7 @@ import (
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
//nolint //nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
//nolint //nolint
. "github.com/onsi/gomega" . "github.com/onsi/gomega"

View file

@ -15,9 +15,7 @@ package vault
import ( import (
// nolint // nolint
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
// nolint
. "github.com/onsi/ginkgo/extensions/table"
"github.com/external-secrets/external-secrets/e2e/framework" "github.com/external-secrets/external-secrets/e2e/framework"
"github.com/external-secrets/external-secrets/e2e/suite/common" "github.com/external-secrets/external-secrets/e2e/suite/common"
@ -32,12 +30,12 @@ const (
withK8s = "with kubernetes provider" withK8s = "with kubernetes provider"
) )
var _ = Describe("[vault] ", func() { var _ = Describe("[vault]", Label("vault"), func() {
f := framework.New("eso-vault") f := framework.New("eso-vault")
prov := newVaultProvider(f)
DescribeTable("sync secrets", DescribeTable("sync secrets",
framework.TableFunc(f, framework.TableFunc(f, prov),
newVaultProvider(f)),
// uses token auth // uses token auth
framework.Compose(withTokenAuth, f, common.JSONDataFromSync, useTokenAuth), framework.Compose(withTokenAuth, f, common.JSONDataFromSync, useTokenAuth),
framework.Compose(withTokenAuth, f, common.JSONDataWithProperty, useTokenAuth), framework.Compose(withTokenAuth, f, common.JSONDataWithProperty, useTokenAuth),

5
go.mod
View file

@ -57,7 +57,7 @@ require (
github.com/hashicorp/vault/api v1.3.1 github.com/hashicorp/vault/api v1.3.1
github.com/huandu/xstrings v1.3.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect
github.com/lestrrat-go/jwx v1.2.1 github.com/lestrrat-go/jwx v1.2.1
github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo/v2 v2.0.0
github.com/onsi/gomega v1.17.0 github.com/onsi/gomega v1.17.0
github.com/oracle/oci-go-sdk/v45 v45.2.0 github.com/oracle/oci-go-sdk/v45 v45.2.0
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.11.0
@ -133,6 +133,7 @@ require (
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-querystring v1.0.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect github.com/googleapis/gnostic v0.5.5 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
@ -174,7 +175,6 @@ require (
github.com/moby/spdystream v0.2.0 // indirect github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/oklog/run v1.1.0 // indirect github.com/oklog/run v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect github.com/oklog/ulid v1.3.1 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect
@ -211,7 +211,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
honnef.co/go/tools v0.1.4 // indirect honnef.co/go/tools v0.1.4 // indirect
k8s.io/apiextensions-apiserver v0.23.0 // indirect k8s.io/apiextensions-apiserver v0.23.0 // indirect

4
go.sum
View file

@ -412,8 +412,10 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/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/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -680,6 +682,8 @@ github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw
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 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
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.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=

View file

@ -56,7 +56,7 @@ func main() {
"Enabling this will ensure there is only one active controller manager.") "Enabling this will ensure there is only one active controller manager.")
flag.IntVar(&concurrent, "concurrent", 1, "The number of concurrent ExternalSecret reconciles.") flag.IntVar(&concurrent, "concurrent", 1, "The number of concurrent ExternalSecret reconciles.")
flag.StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal") flag.StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
flag.StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only") flag.StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces")
flag.Parse() flag.Parse()
var lvl zapcore.Level var lvl zapcore.Level

View file

@ -20,8 +20,7 @@ import (
"strconv" "strconv"
"time" "time"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"

View file

@ -19,7 +19,7 @@ import (
"testing" "testing"
"time" "time"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
@ -89,7 +89,7 @@ var _ = BeforeSuite(func() {
defer GinkgoRecover() defer GinkgoRecover()
Expect(k8sManager.Start(ctrl.SetupSignalHandler())).ToNot(HaveOccurred()) Expect(k8sManager.Start(ctrl.SetupSignalHandler())).ToNot(HaveOccurred())
}() }()
}, 60) })
var _ = AfterSuite(func() { var _ = AfterSuite(func() {
By("tearing down the test environment") By("tearing down the test environment")

View file

@ -18,7 +18,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
@ -65,7 +65,7 @@ var _ = BeforeSuite(func() {
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil()) Expect(k8sClient).ToNot(BeNil())
}, 60) })
var _ = AfterSuite(func() { var _ = AfterSuite(func() {
By("tearing down the test environment") By("tearing down the test environment")

8
terraform/aws/main.tf Normal file
View file

@ -0,0 +1,8 @@
module "cluster" {
source = "./modules/cluster"
cluster_name = var.AWS_CLUSTER_NAME
cluster_region = var.AWS_REGION
irsa_sa_name = var.AWS_SA_NAME
irsa_sa_namespace = var.AWS_SA_NAMESPACE
}

View file

@ -0,0 +1,60 @@
data "aws_eks_cluster_auth" "this" {
name = module.eks.cluster_id
}
data "aws_caller_identity" "current" {}
locals {
kubeconfig = yamlencode({
apiVersion = "v1"
kind = "Config"
current-context = "terraform"
clusters = [{
name = module.eks.cluster_id
cluster = {
certificate-authority-data = module.eks.cluster_certificate_authority_data
server = module.eks.cluster_endpoint
}
}]
contexts = [{
name = "terraform"
context = {
cluster = module.eks.cluster_id
user = "terraform"
}
}]
users = [{
name = "terraform"
user = {
token = data.aws_eks_cluster_auth.this.token
}
}]
})
# we have to allow the root account to access the api
aws_auth_configmap_yaml = <<-EOT
${chomp(module.eks.aws_auth_configmap_yaml)}
- rolearn: arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/admin
username: system:aws:root
groups:
- system:masters
EOT
}
resource "null_resource" "patch_cm" {
triggers = {
kubeconfig = base64encode(local.kubeconfig)
cmd_patch = <<-EOT
kubectl patch configmap/aws-auth --patch "${local.aws_auth_configmap_yaml}" -n kube-system --kubeconfig <(echo $KUBECONFIG | base64 --decode)
EOT
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
environment = {
KUBECONFIG = self.triggers.kubeconfig
}
command = self.triggers.cmd_patch
}
}

View file

@ -0,0 +1,57 @@
locals {
sa_manifest = <<-EOT
apiVersion: v1
kind: ServiceAccount
metadata:
name: ${local.serviceaccount_name}
namespace: ${local.serviceaccount_namespace}
annotations:
eks.amazonaws.com/role-arn: "${aws_iam_role.eso-e2e-irsa.arn}"
EOT
}
data "aws_iam_policy_document" "assume-policy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
condition {
test = "StringEquals"
variable = "${trimprefix(module.eks.cluster_oidc_issuer_url, "https://")}:sub"
values = [
"system:serviceaccount:${local.serviceaccount_namespace}:${local.serviceaccount_name}"
]
}
principals {
type = "Federated"
identifiers = [module.eks.oidc_provider_arn]
}
}
}
resource "aws_iam_role" "eso-e2e-irsa" {
name = "eso-e2e-irsa"
path = "/"
assume_role_policy = data.aws_iam_policy_document.assume-policy.json
managed_policy_arns = [
"arn:aws:iam::aws:policy/SecretsManagerReadWrite"
]
}
resource "null_resource" "apply_sa" {
triggers = {
kubeconfig = base64encode(local.kubeconfig)
cmd_patch = <<-EOT
echo '${local.sa_manifest}' | kubectl --kubeconfig <(echo $KUBECONFIG | base64 --decode) apply -f -
EOT
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
environment = {
KUBECONFIG = self.triggers.kubeconfig
}
command = self.triggers.cmd_patch
}
}

View file

@ -0,0 +1,127 @@
provider "aws" {
region = local.region
}
locals {
name = var.cluster_name
cluster_version = "1.21"
region = var.cluster_region
serviceaccount_name = var.irsa_sa_name
serviceaccount_namespace = var.irsa_sa_namespace
tags = {
Example = local.name
GithubRepo = "external-secrets"
GithubOrg = "external-secrets"
}
}
module "eks" {
source = "git::https://github.com/terraform-aws-modules/terraform-aws-eks?ref=v18.2.0"
cluster_name = local.name
cluster_version = local.cluster_version
cluster_endpoint_private_access = true
cluster_endpoint_public_access = true
cluster_addons = {
coredns = {
resolve_conflicts = "OVERWRITE"
}
kube-proxy = {}
vpc-cni = {
resolve_conflicts = "OVERWRITE"
}
}
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
enable_irsa = true
# EKS Managed Node Group(s)
eks_managed_node_group_defaults = {
ami_type = "AL2_x86_64"
disk_size = 50
instance_types = ["m6i.large", "m5.large", "m5n.large", "m5zn.large"]
vpc_security_group_ids = [aws_security_group.additional.id]
}
eks_managed_node_groups = {
example = {
desired_size = 2
instance_types = ["t3.large"]
tags = local.tags
}
}
tags = local.tags
}
################################################################################
# Supporting resources
################################################################################
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3.0"
name = local.name
cidr = "10.0.0.0/16"
azs = ["${local.region}a", "${local.region}b", "${local.region}c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
enable_flow_log = true
create_flow_log_cloudwatch_iam_role = true
create_flow_log_cloudwatch_log_group = true
public_subnet_tags = {
"kubernetes.io/cluster/${local.name}" = "shared"
"kubernetes.io/role/elb" = 1
}
private_subnet_tags = {
"kubernetes.io/cluster/${local.name}" = "shared"
"kubernetes.io/role/internal-elb" = 1
}
tags = local.tags
}
resource "aws_security_group" "additional" {
name_prefix = "${local.name}-additional"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
]
}
# 443, 53, 123 is already allowed
egress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
tags = local.tags
}

View file

@ -0,0 +1,135 @@
################################################################################
# Cluster
################################################################################
output "cluster_arn" {
description = "The Amazon Resource Name (ARN) of the cluster"
value = module.eks.cluster_arn
}
output "cluster_certificate_authority_data" {
description = "Base64 encoded certificate data required to communicate with the cluster"
value = module.eks.cluster_certificate_authority_data
}
output "cluster_endpoint" {
description = "Endpoint for your Kubernetes API server"
value = module.eks.cluster_endpoint
}
output "cluster_id" {
description = "The name/id of the EKS cluster. Will block on cluster creation until the cluster is really ready"
value = module.eks.cluster_id
}
output "cluster_oidc_issuer_url" {
description = "The URL on the EKS cluster for the OpenID Connect identity provider"
value = module.eks.cluster_oidc_issuer_url
}
output "cluster_platform_version" {
description = "Platform version for the cluster"
value = module.eks.cluster_platform_version
}
output "cluster_status" {
description = "Status of the EKS cluster. One of `CREATING`, `ACTIVE`, `DELETING`, `FAILED`"
value = module.eks.cluster_status
}
output "cluster_security_group_id" {
description = "Cluster security group that was created by Amazon EKS for the cluster. Managed node groups use this security group for control-plane-to-data-plane communication. Referred to as 'Cluster security group' in the EKS console"
value = module.eks.cluster_security_group_id
}
################################################################################
# Security Group
################################################################################
output "cluster_security_group_arn" {
description = "Amazon Resource Name (ARN) of the cluster security group"
value = module.eks.cluster_security_group_arn
}
################################################################################
# IRSA
################################################################################
output "oidc_provider_arn" {
description = "The ARN of the OIDC Provider if `enable_irsa = true`"
value = module.eks.oidc_provider_arn
}
################################################################################
# IAM Role
################################################################################
output "cluster_iam_role_name" {
description = "IAM role name of the EKS cluster"
value = module.eks.cluster_iam_role_name
}
output "cluster_iam_role_arn" {
description = "IAM role ARN of the EKS cluster"
value = module.eks.cluster_iam_role_arn
}
output "cluster_iam_role_unique_id" {
description = "Stable and unique string identifying the IAM role"
value = module.eks.cluster_iam_role_unique_id
}
################################################################################
# EKS Addons
################################################################################
output "cluster_addons" {
description = "Map of attribute maps for all EKS cluster addons enabled"
value = module.eks.cluster_addons
}
################################################################################
# EKS Identity Provider
################################################################################
output "cluster_identity_providers" {
description = "Map of attribute maps for all EKS identity providers enabled"
value = module.eks.cluster_identity_providers
}
################################################################################
# CloudWatch Log Group
################################################################################
output "cloudwatch_log_group_name" {
description = "Name of cloudwatch log group created"
value = module.eks.cloudwatch_log_group_name
}
output "cloudwatch_log_group_arn" {
description = "Arn of cloudwatch log group created"
value = module.eks.cloudwatch_log_group_arn
}
################################################################################
# Fargate Profile
################################################################################
output "fargate_profiles" {
description = "Map of attribute maps for all EKS Fargate Profiles created"
value = module.eks.fargate_profiles
}
################################################################################
# Additional
################################################################################
output "aws_auth_configmap_yaml" {
description = "Formatted yaml output for base aws-auth configmap containing roles used in cluster node groups/fargate profiles"
value = module.eks.aws_auth_configmap_yaml
}
output "eks_cluster_auth_token" {
value = data.aws_eks_cluster_auth.this.token
sensitive = true
}

View file

@ -0,0 +1,10 @@
terraform {
required_version = ">= 0.13"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}

View file

@ -0,0 +1,16 @@
variable "cluster_name" {
type = string
default = "eso-e2e-managed"
}
variable "irsa_sa_name" {
type = string
}
variable "irsa_sa_namespace" {
type = string
}
variable "cluster_region" {
type = string
}

11
terraform/aws/outputs.tf Normal file
View file

@ -0,0 +1,11 @@
output "cluster_arn" {
value = module.cluster.cluster_arn
}
output "cluster_iam_role_arn" {
value = module.cluster.cluster_iam_role_arn
}
output "aws_auth_configmap_yaml" {
value = module.cluster.aws_auth_configmap_yaml
}

11
terraform/aws/provider.tf Normal file
View file

@ -0,0 +1,11 @@
terraform {
required_version = ">= 0.13"
backend "s3" {
bucket = "eso-e2e-aws-tfstate"
key = "aws-tfstate"
region = "eu-west-1"
}
required_providers {}
}

View file

@ -0,0 +1,19 @@
variable "AWS_SA_NAME" {
type = string
default = "eso-e2e-test"
}
variable "AWS_SA_NAMESPACE" {
type = string
default = "default"
}
variable "AWS_REGION" {
type = string
default = "eu-west-1"
}
variable "AWS_CLUSTER_NAME" {
type = string
default = "eso-e2e-managed"
}

View file

@ -1,4 +1,5 @@
resource "google_service_account" "default" { resource "google_service_account" "default" {
project = var.project_id
account_id = var.GCP_GSA_NAME account_id = var.GCP_GSA_NAME
} }
@ -27,6 +28,7 @@ resource "google_service_account_iam_member" "pod_identity_e2e" {
} }
resource "google_container_cluster" "primary" { resource "google_container_cluster" "primary" {
project = var.project_id
name = "${var.env}-cluster" name = "${var.env}-cluster"
location = var.zone location = var.zone
remove_default_node_pool = true remove_default_node_pool = true
@ -43,6 +45,7 @@ resource "google_container_cluster" "primary" {
} }
resource "google_container_node_pool" "nodes" { resource "google_container_node_pool" "nodes" {
project = var.project_id
name = "${google_container_cluster.primary.name}-node-pool" name = "${google_container_cluster.primary.name}-node-pool"
location = google_container_cluster.primary.location location = google_container_cluster.primary.location
cluster = google_container_cluster.primary.name cluster = google_container_cluster.primary.name
@ -57,3 +60,20 @@ resource "google_container_node_pool" "nodes" {
] ]
} }
} }
provider "kubernetes" {
host = "https://${google_container_cluster.primary.endpoint}"
token = data.google_client_config.default.access_token
cluster_ca_certificate = base64decode(google_container_cluster.primary.master_auth.0.cluster_ca_certificate)
}
data "google_client_config" "default" {}
resource "kubernetes_service_account" "test" {
metadata {
name = var.GCP_KSA_NAME
annotations = {
"iam.gke.io/gcp-service-account" : "${var.GCP_GSA_NAME}@${var.project_id}.iam.gserviceaccount.com"
}
}
}

View file

@ -1,9 +1,11 @@
resource "google_compute_network" "env-vpc" { resource "google_compute_network" "env-vpc" {
project = var.project_id
name = "${var.env}-vpc" name = "${var.env}-vpc"
auto_create_subnetworks = false auto_create_subnetworks = false
} }
resource "google_compute_subnetwork" "env-subnet" { resource "google_compute_subnetwork" "env-subnet" {
project = var.project_id
name = "${google_compute_network.env-vpc.name}-subnet" name = "${google_compute_network.env-vpc.name}-subnet"
region = var.region region = var.region
network = google_compute_network.env-vpc.name network = google_compute_network.env-vpc.name

View file

@ -13,3 +13,6 @@ variable "ip_service_range" {
variable "region" { variable "region" {
default = "europe-west1" default = "europe-west1"
} }
variable "project_id" {
type = string
}

View file

@ -11,6 +11,7 @@ module "test-network" {
env = var.env env = var.env
region = var.region region = var.region
ip_cidr_range = var.ip_cidr_range ip_cidr_range = var.ip_cidr_range
project_id = var.project_id
} }
module "test-cluster" { module "test-cluster" {

View file

@ -5,6 +5,6 @@ package tools
import ( import (
_ "github.com/ahmetb/gen-crd-api-reference-docs" _ "github.com/ahmetb/gen-crd-api-reference-docs"
_ "github.com/onsi/ginkgo/ginkgo" _ "github.com/onsi/ginkgo/v2/ginkgo"
_ "sigs.k8s.io/controller-tools/cmd/controller-gen" _ "sigs.k8s.io/controller-tools/cmd/controller-gen"
) )