Просмотр исходного кода

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

feat(e2e): implement aws tests, enhance gcp tests
paul-the-alien[bot] 4 лет назад
Родитель
Сommit
0b9c142a22
61 измененных файлов с 1330 добавлено и 731 удалено
  1. 78 34
      .github/workflows/e2e-managed.yml
  2. 4 4
      .github/workflows/e2e.yml
  3. 1 0
      .github/workflows/ok-to-test-managed.yml
  4. 17 21
      Makefile
  5. 1 1
      e2e/Dockerfile
  6. 14 32
      e2e/Makefile
  7. 3 12
      e2e/e2e_test.go
  8. 6 9
      e2e/entrypoint.sh
  9. 1 1
      e2e/framework/addon/addon.go
  10. 1 1
      e2e/framework/addon/chart.go
  11. 64 57
      e2e/framework/addon/eso.go
  12. 0 44
      e2e/framework/addon/localstack.go
  13. 1 1
      e2e/framework/addon/vault.go
  14. 1 1
      e2e/framework/eso.go
  15. 7 10
      e2e/framework/framework.go
  16. 2 4
      e2e/framework/log/log.go
  17. 13 9
      e2e/framework/util/util.go
  18. 0 12
      e2e/k8s/eso.scoped.values.yaml
  19. 0 11
      e2e/k8s/eso.values.yaml
  20. 12 8
      e2e/run.sh
  21. 4 8
      e2e/suite/akeyless/akeyless.go
  22. 8 1
      e2e/suite/akeyless/provider.go
  23. 4 12
      e2e/suite/alibaba/alibaba.go
  24. 9 1
      e2e/suite/alibaba/provider.go
  25. 150 33
      e2e/suite/aws/provider.go
  26. 4 91
      e2e/suite/aws/secretsmanager.go
  27. 102 0
      e2e/suite/aws/secretsmanager_managed.go
  28. 3 14
      e2e/suite/azure/azure.go
  29. 21 5
      e2e/suite/azure/provider.go
  30. 55 63
      e2e/suite/gcp/gcp.go
  31. 111 0
      e2e/suite/gcp/gcp_managed.go
  32. 107 101
      e2e/suite/gcp/provider.go
  33. 0 86
      e2e/suite/gcpmanaged/gcpmanaged.go
  34. 5 13
      e2e/suite/gitlab/gitlab.go
  35. 8 1
      e2e/suite/gitlab/provider.go
  36. 0 1
      e2e/suite/import.go
  37. 4 10
      e2e/suite/oracle/oracle.go
  38. 11 1
      e2e/suite/oracle/provider.go
  39. 1 1
      e2e/suite/vault/provider.go
  40. 4 6
      e2e/suite/vault/vault.go
  41. 2 3
      go.mod
  42. 4 0
      go.sum
  43. 1 1
      main.go
  44. 1 2
      pkg/controllers/externalsecret/externalsecret_controller_test.go
  45. 2 2
      pkg/controllers/externalsecret/suite_test.go
  46. 2 2
      pkg/controllers/secretstore/suite_test.go
  47. 8 0
      terraform/aws/main.tf
  48. 60 0
      terraform/aws/modules/cluster/auth.tf
  49. 57 0
      terraform/aws/modules/cluster/irsa.tf
  50. 127 0
      terraform/aws/modules/cluster/main.tf
  51. 135 0
      terraform/aws/modules/cluster/outputs.tf
  52. 10 0
      terraform/aws/modules/cluster/provider.tf
  53. 16 0
      terraform/aws/modules/cluster/variables.tf
  54. 11 0
      terraform/aws/outputs.tf
  55. 11 0
      terraform/aws/provider.tf
  56. 19 0
      terraform/aws/variables.tf
  57. 20 0
      terraform/gcp/eso_gcp_modules/gke/main.tf
  58. 2 0
      terraform/gcp/eso_gcp_modules/network/main.tf
  59. 3 0
      terraform/gcp/eso_gcp_modules/network/variable.tf
  60. 1 0
      terraform/gcp/main.tf
  61. 1 1
      tools.go

+ 78 - 34
.github/workflows/e2e-managed.yml

@@ -24,24 +24,62 @@ env:
   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_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_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}}
   TENANT_ID: ${{ secrets.TENANT_ID}}
   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
 
 jobs:
-  # Repo owner has commented /ok-to-test-managed on a (fork-based) pull request
-  integration-fork-managed:
+  integration-managed:
     runs-on: ubuntu-latest
-    if:
-      github.event_name == 'repository_dispatch'
+    if: github.event_name == 'repository_dispatch'
+
     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
     - name: Fork based /ok-to-test-managed checkout
       uses: actions/checkout@v2
@@ -75,13 +113,7 @@ jobs:
         path: ${{ steps.go.outputs.mod-cache }}
         key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
         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
       uses: terraform-linters/setup-tflint@v1
       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'
 
     - name: Setup TF Gcloud Provider
+      if: github.event.client_payload.slash_command.args.named.provider == 'gcp'
       run: |-
         mkdir -p terraform/gcp/secrets
         echo ${GCP_SM_SA_GKE_JSON} > terraform/gcp/secrets/gcloud-service-account-key.json
 
-    - name: Show TF GKE
+    - name: Show TF
       run: |-
-        make tf.show.gcp
+        PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
+        make tf.show.${PROVIDER}
 
     - name: Setup Infracost
       uses: infracost/actions/setup@v1
       with:
         api-key: ${{ secrets.INFRACOST_API_KEY }}
 
-    - name: Generate Infracost JSON for GKE
-      run: infracost breakdown --path terraform/gcp/plan.json --format json --out-file /tmp/infracost.json
+    - name: Generate Infracost JSON for provider
+      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
       uses: infracost/actions/comment@v1
       with:
         path: /tmp/infracost.json
-        # Choose the commenting behavior, 'update' is a good default:
-        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.
+        behavior: update
 
-    - name: Apply TF GKE
+    - name: Apply TF
       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
+      if: github.event.client_payload.slash_command.args.named.provider == 'gke'
       run: |-
         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
       uses: docker/login-action@v1
       if: env.GHCR_USERNAME != ''
@@ -133,25 +177,25 @@ jobs:
         username: ${{ secrets.GHCR_USERNAME }}
         password: ${{ secrets.GHCR_TOKEN }}
 
-    - name: Run e2e Tests for GCP
+    - name: Run managed e2e Tests
       run: |
-        export E2E_VERSION=$GITHUB_SHA
-        export PR_IMG_TAG=$GITHUB_SHA
         export PATH=$PATH:$(go env GOPATH)/bin
-        go get github.com/onsi/ginkgo/ginkgo
-        make test.e2e.managed FOCUS="gcpmanaged"
+        PROVIDER=${{github.event.client_payload.slash_command.args.named.provider}}
+        go get github.com/onsi/ginkgo/v2/ginkgo
+        make test.e2e.managed GINKGO_LABELS="${PROVIDER}"
 
-    - name: Destroy TF GKE
+    - name: Destroy TF
       if: always()
       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
       id: update-check-run
       if: ${{ always() }}
       env:
-        number: ${{ github.event.client_payload.pull_request.number }}
+        number: ${{ github.event.client_payload.slash_command.args.named.pull }}
         job: ${{ github.job }}
         # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
         conclusion: ${{ job.status }}

+ 4 - 4
.github/workflows/e2e.yml

@@ -19,12 +19,12 @@ env:
   GCP_GSA_NAME: ${{ secrets.GCP_GSA_NAME}} # Goolge Service Account
   GCP_KSA_NAME: ${{ secrets.GCP_KSA_NAME}} # Kubernetes Service Account
   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_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}}
   TENANT_ID: ${{ secrets.TENANT_ID}}
   VAULT_URL: ${{ secrets.VAULT_URL}}
-  E2E_IMAGE_REGISTRY: local/external-secrets-e2e
-  E2E_VERSION: test
 
 name: e2e tests
 
@@ -87,7 +87,7 @@ jobs:
         BUILD_ARGS: "--load"
       run: |
         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
 
   # Repo owner has commented /ok-to-test on a (fork-based) pull request
@@ -150,7 +150,7 @@ jobs:
         BUILD_ARGS: "--load"
       run: |
         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
 
     # Update check run called "integration-fork"

+ 1 - 0
.github/workflows/ok-to-test-managed.yml

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

+ 17 - 21
Makefile

@@ -14,9 +14,7 @@ BUILD_ARGS ?=
 all: $(addprefix build-,$(ARCH))
 
 # Image registry for build/push image targets
-IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets
-
-PR_IMG_TAG ?=
+export IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets
 
 CRD_DIR     ?= deploy/crds
 
@@ -35,10 +33,10 @@ endif
 # check if there are any existing `git tag` values
 ifeq ($(shell git tag),)
 # 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
 # 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
 
 # ====================================================================================
@@ -87,13 +85,13 @@ test: generate ## Run tests
 test.e2e: generate ## Run e2e tests
 	@$(INFO) go test e2e-tests
 	$(MAKE) -C ./e2e test
-	@$(OK) go test unit-tests
+	@$(OK) go test e2e-tests
 
 .PHONY: test.e2e.managed
-test.e2e.managed: generate ## Run e2e tests
-	@$(INFO) go test e2e-tests
+test.e2e.managed: generate ## Run e2e tests managed
+	@$(INFO) go test e2e-tests-managed
 	$(MAKE) -C ./e2e test.managed
-	@$(OK) go test unit-tests
+	@$(OK) go test e2e-tests-managed
 
 .PHONY: build
 build: $(addprefix build-,$(ARCH)) ## Build binary
@@ -211,7 +209,7 @@ docker.push: ## Push the docker image to the registry
 	@docker push $(IMAGE_REGISTRY):$(VERSION)
 	@$(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.
 RELEASE_TAG ?= main
 SOURCE_TAG ?= $(VERSION)
@@ -230,29 +228,27 @@ docker.promote: ## Promote the docker image to the registry
 # ====================================================================================
 # Terraform
 
-tf.plan.gcp: ## Runs terrform plan for gcp provider bringing GKE up
-	@cd $(TF_DIR)/gcp; \
+tf.plan.%: ## Runs terrform plan for a provider
+	@cd $(TF_DIR)/$*; \
 	terraform init; \
-	terraform plan -auto-approve
+	terraform plan
 
-tf.apply.gcp: ## Runs terrform apply for gcp provider bringing GKE up
-	@cd $(TF_DIR)/gcp; \
+tf.apply.%: ## Runs terrform apply for a provider
+	@cd $(TF_DIR)/$*; \
 	terraform init; \
 	terraform apply -auto-approve
 
-tf.destroy.gcp: ## Runs terrform destroy for gcp provider bringing GKE down
-	@cd $(TF_DIR)/gcp; \
+tf.destroy.%: ## Runs terrform destroy for a provider
+	@cd $(TF_DIR)/$*; \
 	terraform init; \
 	terraform destroy -auto-approve
 
-tf.show.gcp: ## Runs terrform show for gcp and outputs to a file
-	@cd $(TF_DIR)/gcp; \
+tf.show.%: ## Runs terrform show for a provider and outputs to a file
+	@cd $(TF_DIR)/$*; \
 	terraform init; \
 	terraform plan -out tfplan.binary; \
 	terraform show -json tfplan.binary > plan.json
 
-
-
 # ====================================================================================
 # Help
 

+ 1 - 1
e2e/Dockerfile

@@ -4,7 +4,7 @@ FROM golang:$GO_VERSION-buster as builder
 ENV KUBECTL_VERSION="v1.21.2"
 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 && \
     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 && \

+ 14 - 32
e2e/Makefile

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

+ 3 - 12
e2e/e2e_test.go

@@ -17,7 +17,7 @@ import (
 	"testing"
 
 	// nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	// nolint
 	. "github.com/onsi/gomega"
 
@@ -30,25 +30,16 @@ var _ = SynchronizedBeforeSuite(func() []byte {
 	cfg := &addon.Config{}
 	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")
 	addon.InstallGlobalAddon(addon.NewESO(), cfg)
 
-	By("installing scoped eso")
-	addon.InstallGlobalAddon(addon.NewScopedESO(), cfg)
 	return nil
 }, func([]byte) {})
 
 var _ = SynchronizedAfterSuite(func() {}, func() {
 	By("Cleaning up global addons")
 	addon.UninstallGlobalAddons()
-	if CurrentGinkgoTestDescription().Failed {
+	if CurrentSpecReport().Failed() {
 		addon.PrintLogs()
 	}
 })
@@ -56,5 +47,5 @@ var _ = SynchronizedAfterSuite(func() {}, func() {
 func TestE2E(t *testing.T) {
 	NewWithT(t)
 	RegisterFailHandler(Fail)
-	RunSpecs(t, "external-secrets e2e suite")
+	RunSpecs(t, "external-secrets e2e suite", Label("e2e"))
 }

+ 6 - 9
e2e/entrypoint.sh

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

+ 1 - 1
e2e/framework/addon/addon.go

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

+ 1 - 1
e2e/framework/addon/chart.go

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

+ 64 - 57
e2e/framework/addon/eso.go

@@ -14,92 +14,99 @@ limitations under the License.
 package addon
 
 import (
-	"fmt"
 	"os"
 
 	// nolint
-	. "github.com/onsi/ginkgo"
-	// nolint
-	. "github.com/onsi/gomega"
-
-	// nolint
-	"github.com/external-secrets/external-secrets/e2e/framework/util"
+	. "github.com/onsi/ginkgo/v2"
 )
 
 type ESO struct {
-	Addon
+	*HelmChart
 }
 
-func NewESO() *ESO {
-	return &ESO{
+func NewESO(mutators ...MutationFunc) *ESO {
+	eso := &ESO{
 		&HelmChart{
 			Namespace:   "default",
 			ReleaseName: "eso",
 			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",
+				},
+			},
 		},
 	}
-}
 
-func (l *ESO) Install() error {
-	By("Installing eso\n")
-	err := l.Addon.Install()
-	if err != nil {
-		return err
+	for _, f := range mutators {
+		f(eso)
 	}
 
-	By("afterInstall eso\n")
-	err = l.afterInstall()
-	if err != nil {
-		return err
-	}
+	return eso
+}
 
-	return nil
+type MutationFunc func(eso *ESO)
+
+func WithReleaseName(name string) MutationFunc {
+	return func(eso *ESO) {
+		eso.HelmChart.ReleaseName = name
+	}
 }
 
-func (l *ESO) afterInstall() error {
-	err := gcpPreparation()
-	Expect(err).NotTo(HaveOccurred())
-	err = awsPreparation()
-	Expect(err).NotTo(HaveOccurred())
-	if err != nil {
-		return err
+func WithNamespace(namespace string) MutationFunc {
+	return func(eso *ESO) {
+		eso.HelmChart.Namespace = namespace
 	}
-	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()
+func WithNamespaceScope(namespace string) MutationFunc {
+	return func(eso *ESO) {
+		eso.HelmChart.Vars = append(eso.HelmChart.Vars, StringTuple{
+			Key:   "scopedNamespace",
+			Value: namespace,
+		})
+	}
+}
 
-	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())
+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",
+			},
+		}...)
+	}
+}
 
-	_, err = util.UpdateKubeSA("external-secrets-e2e", kubeClientSet, "default", annotations)
-	Expect(err).NotTo(HaveOccurred())
+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 {
+	By("Installing eso\n")
+	err := l.HelmChart.Install()
 	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"},
-		},
-	}
-}

+ 0 - 44
e2e/framework/addon/localstack.go

@@ -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")
-}

+ 1 - 1
e2e/framework/addon/vault.go

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

+ 1 - 1
e2e/framework/eso.go

@@ -33,7 +33,7 @@ import (
 // with the provided values.
 func (f *Framework) WaitForSecretValue(namespace, name string, expected *v1.Secret) (*v1.Secret, error) {
 	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{
 			Namespace: namespace,
 			Name:      name,

+ 7 - 10
e2e/framework/framework.go

@@ -16,12 +16,10 @@ package framework
 import (
 
 	// nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 
 	// nolint
 	. "github.com/onsi/gomega"
-	// nolint
-	. "github.com/onsi/ginkgo/extensions/table"
 	api "k8s.io/api/core/v1"
 	"k8s.io/client-go/kubernetes"
 	kscheme "k8s.io/client-go/kubernetes/scheme"
@@ -30,6 +28,7 @@ import (
 
 	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/log"
 	"github.com/external-secrets/external-secrets/e2e/framework/util"
 )
 
@@ -72,11 +71,9 @@ func New(baseName string) *Framework {
 // BeforeEach creates a namespace.
 func (f *Framework) BeforeEach() {
 	var err error
-	By("Building a namespace api object")
 	f.Namespace, err = util.CreateKubeNamespace(f.BaseName, f.KubeClientSet)
-	Expect(err).NotTo(HaveOccurred())
-
-	By("Using the namespace " + f.Namespace.Name)
+	log.Logf("created test namespace %s", f.Namespace.Name)
+	Expect(err).ToNot(HaveOccurred())
 }
 
 // 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
 	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)
 	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 {
 	desc, tfn := fn(f)
 	tweaks = append(tweaks, tfn)
-	te := Entry(desc + " " + descAppend)
 
 	// need to convert []func to []interface{}
 	ifs := make([]interface{}, len(tweaks))
 	for i := 0; i < len(tweaks); i++ {
 		ifs[i] = tweaks[i]
 	}
-	te.Parameters = ifs
+	te := Entry(desc+" "+descAppend, ifs...)
+
 	return te
 }

+ 2 - 4
e2e/framework/log/log.go

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

+ 13 - 9
e2e/framework/util/util.go

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

+ 0 - 12
e2e/k8s/eso.scoped.values.yaml

@@ -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"

+ 0 - 11
e2e/k8s/eso.values.yaml

@@ -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"

+ 12 - 8
e2e/run.sh

@@ -42,20 +42,22 @@ done
 
 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 \
   --attach \
   --restart=Never \
-  --pod-running-timeout=10m \
-  --env="FOCUS=${FOCUS:-.*}" \
+  --pod-running-timeout=5m \
+  --env="GINKGO_LABELS=${GINKGO_LABELS:-.*}" \
   --env="GCP_SM_SA_JSON=${GCP_SM_SA_JSON:-}" \
   --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_KSA_NAME=${GCP_KSA_NAME:-}" \
-  --env="TF_VAR_GCP_GSA_NAME=${TF_VAR_GCP_GSA_NAME:-}" \
-  --env="TF_VAR_GCP_KSA_NAME=${TF_VAR_GCP_KSA_NAME:-}" \
+  --env="GCP_GKE_ZONE=${GCP_GKE_ZONE:-}" \
+  --env="GCP_GKE_CLUSTER=${GCP_GKE_CLUSTER:-}" \
+  --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_SECRET=${AZURE_CLIENT_SECRET:-}" \
   --env="AKEYLESS_ACCESS_ID=${AKEYLESS_ACCESS_ID:-}" \
@@ -70,5 +72,7 @@ kubectl run --rm \
   --env="ORACLE_REGION=${ORACLE_REGION:-}" \
   --env="ORACLE_FINGERPRINT=${ORACLE_FINGERPRINT:-}" \
   --env="ORACLE_KEY=${ORACLE_KEY:-}" \
+  --env="IMAGE_REGISTRY=${IMAGE_REGISTRY}" \
+  --env="VERSION=${VERSION}" \
   --overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \
-  e2e --image=${E2E_IMAGE_REGISTRY}:${E2E_VERSION}
+  e2e --image=${E2E_IMAGE_REGISTRY}:${VERSION}

+ 4 - 8
e2e/suite/akeyless/akeyless.go

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

+ 8 - 1
e2e/suite/akeyless/provider.go

@@ -29,7 +29,7 @@ import (
 	"github.com/akeylesslabs/akeyless-go/v2"
 
 	//nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 
 	//nolint
 	. "github.com/onsi/gomega"
@@ -75,6 +75,13 @@ func newAkeylessProvider(f *framework.Framework, accessID, accessType, accessTyp
 	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.
 func (a *akeylessProvider) CreateSecret(key, val string) {
 	token, err := a.GetToken()

+ 4 - 12
e2e/suite/alibaba/alibaba.go

@@ -15,27 +15,19 @@ limitations under the License.
 package alibaba
 
 import (
-	"os"
 
 	// nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	// 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/suite/common"
 )
 
-var _ = Describe("[alibaba] ", func() {
+var _ = Describe("[alibaba]", Label("alibaba"), func() {
 	f := framework.New("eso-alibaba")
-	accessKeyID := os.Getenv("ACCESS_KEY_ID")
-	accessKeySecret := os.Getenv("ACCESS_KEY_SECRET")
-	regionID := os.Getenv("REGION_ID")
-	prov := &alibabaProvider{}
-
-	if accessKeyID != "" && accessKeySecret != "" && regionID != "" {
-		prov = newAlibabaProvider(f, accessKeyID, accessKeySecret, regionID)
-	}
+	prov := newFromEnv(f)
 
 	DescribeTable("sync secrets", framework.TableFunc(f, prov),
 		Entry(common.SimpleDataSync(f)),

+ 9 - 1
e2e/suite/alibaba/provider.go

@@ -16,11 +16,12 @@ package alibaba
 
 import (
 	"context"
+	"os"
 
 	"github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
 
 	//nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 
 	//nolint
 	. "github.com/onsi/gomega"
@@ -54,6 +55,13 @@ func newAlibabaProvider(f *framework.Framework, accessKeyID, accessKeySecret, re
 	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.
 func (s *alibabaProvider) CreateSecret(key, val string) {
 	client, err := kms.NewClientWithAccessKey(s.regionID, s.accessKeyID, s.accessKeySecret)

+ 150 - 33
e2e/suite/aws/provider.go

@@ -16,6 +16,8 @@ package aws
 
 import (
 	"context"
+	"os"
+	"time"
 
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/credentials"
@@ -23,79 +25,194 @@ import (
 	"github.com/aws/aws-sdk-go/service/secretsmanager"
 
 	//nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 
 	// 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/controller/controllerutil"
 
 	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/pkg/provider/aws/auth"
+	"github.com/external-secrets/external-secrets/e2e/framework/log"
 )
 
 type SMProvider struct {
-	url       string
+	ServiceAccountName      string
+	ServiceAccountNamespace string
+
+	kid       string
+	sak       string
+	region    string
 	client    *secretsmanager.SecretsManager
 	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{
 		Config: aws.Config{
-			Credentials: credentials.NewStaticCredentials("foobar", "foobar", "secret-manager"),
-			EndpointResolver: auth.ResolveEndpointWithServiceMap(map[string]string{
-				"secretsmanager": url,
-			}),
-			Region: aws.String("eu-east-1"),
+			Credentials: credentials.NewStaticCredentials(kid, sak, ""),
+			Region:      aws.String(region),
 		},
 	})
-	Expect(err).ToNot(HaveOccurred())
+	if err != nil {
+		Fail(err.Error())
+	}
 	sm := secretsmanager.New(sess)
 	prov := &SMProvider{
-		url:       url,
-		client:    sm,
-		framework: f,
+		ServiceAccountName:      saName,
+		ServiceAccountNamespace: saNamespace,
+		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
 }
 
+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) {
-	_, err := s.client.CreateSecret(&secretsmanager.CreateSecretInput{
-		Name:         aws.String(key),
-		SecretString: aws.String(val),
-	})
-	Expect(err).ToNot(HaveOccurred())
+	// we re-use some secret names throughout our test suite
+	// due to the fact that there is a short delay before the secret is actually deleted
+	// we have to retry creating the secret
+	attempts := 20
+	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) {
+	log.Logf("deleting secret %s", key)
 	_, err := s.client.DeleteSecret(&secretsmanager.DeleteSecretInput{
-		SecretId: aws.String(key),
+		SecretId:                   aws.String(key),
+		ForceDeleteWithoutRecovery: aws.Bool(true),
 	})
 	Expect(err).ToNot(HaveOccurred())
 }
 
-func (s *SMProvider) BeforeEach() {
-	By("creating a AWS SM credentials secret")
+// MountedIRSAStore is a SecretStore without auth config
+// 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{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      secretName,
+			Name:      staticCredentialsSecretName,
 			Namespace: s.framework.Namespace.Name,
 		},
 		StringData: map[string]string{
-			"kid": "foobar",
-			"sak": "foobar",
+			"kid": s.kid,
+			"sak": s.sak,
 		},
 	}
 	err := s.framework.CRClient.Create(context.Background(), awsCreds)
 	Expect(err).ToNot(HaveOccurred())
 
-	By("creating a AWS SM secret store")
 	secretStore := &esv1alpha1.SecretStore{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      s.framework.Namespace.Name,
@@ -105,15 +222,15 @@ func (s *SMProvider) BeforeEach() {
 			Provider: &esv1alpha1.SecretStoreProvider{
 				AWS: &esv1alpha1.AWSProvider{
 					Service: esv1alpha1.AWSServiceSecretsManager,
-					Region:  "us-east-1",
+					Region:  s.region,
 					Auth: esv1alpha1.AWSAuth{
 						SecretRef: &esv1alpha1.AWSAuthSecretRef{
-							AccessKeyID: esmeta.SecretKeySelector{
-								Name: secretName,
+							AccessKeyID: esmetav1.SecretKeySelector{
+								Name: staticCredentialsSecretName,
 								Key:  "kid",
 							},
-							SecretAccessKey: esmeta.SecretKeySelector{
-								Name: secretName,
+							SecretAccessKey: esmetav1.SecretKeySelector{
+								Name: staticCredentialsSecretName,
 								Key:  "sak",
 							},
 						},

+ 4 - 91
e2e/suite/aws/secretsmanager.go

@@ -15,103 +15,17 @@ limitations under the License.
 package aws
 
 import (
-	"context"
-	"fmt"
 
 	// 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/suite/common"
 )
 
-var _ = Describe("[aws] ", func() {
-	f := framework.New("eso-aws")
-	prov := newSMProvider(f, "http://localstack.default")
-
-	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,
-				},
-			},
-		}
-	}
+var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
+	f := framework.New("eso-aws-sm")
+	prov := NewFromEnv(f)
 
 	DescribeTable("sync secrets",
 		framework.TableFunc(f,
@@ -121,7 +35,6 @@ var _ = Describe("[aws] ", func() {
 		Entry(common.JSONDataFromSync(f)),
 		Entry(common.JSONDataWithProperty(f)),
 		Entry(common.JSONDataWithTemplate(f)),
-		Entry("should sync secrets with jwt auth", jwt),
 		Entry(common.DockerJSONConfig(f)),
 		Entry(common.DataPropertyDockerconfigJSON(f)),
 		Entry(common.SSHKeySync(f)),

+ 102 - 0
e2e/suite/aws/secretsmanager_managed.go

@@ -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()
+	}
+}

+ 3 - 14
e2e/suite/azure/azure.go

@@ -13,28 +13,17 @@ limitations under the License.
 package azure
 
 import (
-	"os"
 
 	// nolint
-	. "github.com/onsi/ginkgo"
-	// nolint
-	. "github.com/onsi/ginkgo/extensions/table"
+	. "github.com/onsi/ginkgo/v2"
 
 	"github.com/external-secrets/external-secrets/e2e/framework"
 	"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")
-	vaultURL := os.Getenv("VAULT_URL")
-	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)
-	}
+	prov := newFromEnv(f)
 
 	DescribeTable("sync secrets", framework.TableFunc(f, prov),
 		Entry(common.SimpleDataSync(f)),

+ 21 - 5
e2e/suite/azure/provider.go

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

+ 55 - 63
e2e/suite/gcp/gcp.go

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

+ 111 - 0
e2e/suite/gcp/gcp_managed.go

@@ -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()
+	}
+}

+ 107 - 101
e2e/suite/gcp/provider.go

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

+ 0 - 86
e2e/suite/gcpmanaged/gcpmanaged.go

@@ -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
-}

+ 5 - 13
e2e/suite/gitlab/gitlab.go

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

+ 8 - 1
e2e/suite/gitlab/provider.go

@@ -15,10 +15,11 @@ package gitlab
 
 import (
 	"context"
+	"os"
 	"strings"
 
 	// nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 
 	// nolint
 	. "github.com/onsi/gomega"
@@ -47,6 +48,12 @@ func newGitlabProvider(f *framework.Framework, credentials, projectID string) *g
 	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) {
 	// **Open the client
 	client, err := gitlab.NewClient(s.credentials)

+ 0 - 1
e2e/suite/import.go

@@ -19,6 +19,5 @@ import (
 	_ "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/gcp"
-	_ "github.com/external-secrets/external-secrets/e2e/suite/gcpmanaged"
 	_ "github.com/external-secrets/external-secrets/e2e/suite/vault"
 )

+ 4 - 10
e2e/suite/oracle/oracle.go

@@ -13,25 +13,19 @@ limitations under the License.
 package oracle
 
 import (
-	"os"
 
 	// nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	// 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/suite/common"
 )
 
-var _ = Describe("[oracle] ", func() {
+var _ = Describe("[oracle]", Label("oracle"), func() {
 	f := framework.New("eso-oracle")
-	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")
-	prov := newOracleProvider(f, tenancy, user, region, fingerprint, privateKey)
+	prov := newFromEnv(f)
 
 	DescribeTable("sync secrets", framework.TableFunc(f, prov),
 		Entry(common.SimpleDataSync(f)),

+ 11 - 1
e2e/suite/oracle/provider.go

@@ -14,9 +14,10 @@ package oracle
 
 import (
 	"context"
+	"os"
 
 	// nolint
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 
 	// nolint
 	. "github.com/onsi/gomega"
@@ -58,6 +59,15 @@ func newOracleProvider(f *framework.Framework, tenancy, user, region, fingerprin
 	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) {
 	configurationProvider := common.NewRawConfigurationProvider(p.tenancy, p.user, p.region, p.fingerprint, p.privateKey, nil)
 	client, err := vault.NewVaultsClientWithConfigurationProvider(configurationProvider)

+ 1 - 1
e2e/suite/vault/provider.go

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

+ 4 - 6
e2e/suite/vault/vault.go

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

+ 2 - 3
go.mod

@@ -57,7 +57,7 @@ require (
 	github.com/hashicorp/vault/api v1.3.1
 	github.com/huandu/xstrings v1.3.2 // indirect
 	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/oracle/oci-go-sdk/v45 v45.2.0
 	github.com/prometheus/client_golang v1.11.0
@@ -133,6 +133,7 @@ require (
 	github.com/golang/snappy v0.0.4 // indirect
 	github.com/google/go-querystring v1.0.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/gnostic v0.5.5 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -174,7 +175,6 @@ require (
 	github.com/moby/spdystream v0.2.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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/ulid v1.3.1 // 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/ini.v1 v1.66.2 // 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
 	honnef.co/go/tools v0.1.4 // indirect
 	k8s.io/apiextensions-apiserver v0.23.0 // indirect

+ 4 - 0
go.sum

@@ -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-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-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-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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 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.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 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 v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=

+ 1 - 1
main.go

@@ -56,7 +56,7 @@ func main() {
 			"Enabling this will ensure there is only one active controller manager.")
 	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(&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()
 
 	var lvl zapcore.Level

+ 1 - 2
pkg/controllers/externalsecret/externalsecret_controller_test.go

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

+ 2 - 2
pkg/controllers/externalsecret/suite_test.go

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

+ 2 - 2
pkg/controllers/secretstore/suite_test.go

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

+ 8 - 0
terraform/aws/main.tf

@@ -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
+}

+ 60 - 0
terraform/aws/modules/cluster/auth.tf

@@ -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
+  }
+}

+ 57 - 0
terraform/aws/modules/cluster/irsa.tf

@@ -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
+  }
+}

+ 127 - 0
terraform/aws/modules/cluster/main.tf

@@ -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
+}
+

+ 135 - 0
terraform/aws/modules/cluster/outputs.tf

@@ -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
+}

+ 10 - 0
terraform/aws/modules/cluster/provider.tf

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

+ 16 - 0
terraform/aws/modules/cluster/variables.tf

@@ -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 - 0
terraform/aws/outputs.tf

@@ -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 - 0
terraform/aws/provider.tf

@@ -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 {}
+}

+ 19 - 0
terraform/aws/variables.tf

@@ -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"
+}

+ 20 - 0
terraform/gcp/eso_gcp_modules/gke/main.tf

@@ -1,4 +1,5 @@
 resource "google_service_account" "default" {
+  project    = var.project_id
   account_id = var.GCP_GSA_NAME
 }
 
@@ -27,6 +28,7 @@ resource "google_service_account_iam_member" "pod_identity_e2e" {
 }
 
 resource "google_container_cluster" "primary" {
+  project                  = var.project_id
   name                     = "${var.env}-cluster"
   location                 = var.zone
   remove_default_node_pool = true
@@ -43,6 +45,7 @@ resource "google_container_cluster" "primary" {
 }
 
 resource "google_container_node_pool" "nodes" {
+  project    = var.project_id
   name       = "${google_container_cluster.primary.name}-node-pool"
   location   = google_container_cluster.primary.location
   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"
+    }
+  }
+}

+ 2 - 0
terraform/gcp/eso_gcp_modules/network/main.tf

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

+ 3 - 0
terraform/gcp/eso_gcp_modules/network/variable.tf

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

+ 1 - 0
terraform/gcp/main.tf

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

+ 1 - 1
tools.go

@@ -5,6 +5,6 @@ package tools
 
 import (
 	_ "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"
 )