فهرست منبع

Merge branch 'main' into feature/kubernetes-provider

rodrmartinez 4 سال پیش
والد
کامیت
7c4a17a9c3
100فایلهای تغییر یافته به همراه2417 افزوده شده و 384 حذف شده
  1. 0 5
      .github/codecov.yml
  2. 8 10
      .github/workflows/ci.yml
  3. 3 10
      .github/workflows/docs.yml
  4. 27 14
      .github/workflows/e2e-managed.yml
  5. 10 8
      .github/workflows/e2e.yml
  6. 3 3
      .github/workflows/helm.yml
  7. 11 0
      .github/workflows/release.yml
  8. 1 0
      ADOPTERS.md
  9. 6 2
      Makefile
  10. 3 3
      README.md
  11. 18 0
      apis/externalsecrets/v1alpha1/externalsecret_types.go
  12. 29 0
      apis/externalsecrets/v1alpha1/generic_store.go
  13. 27 0
      apis/externalsecrets/v1alpha1/secretstore_fake_types.go
  14. 2 4
      apis/externalsecrets/v1alpha1/secretstore_kubernetes_types.go
  15. 16 14
      apis/externalsecrets/v1alpha1/secretstore_oracle_types.go
  16. 9 0
      apis/externalsecrets/v1alpha1/secretstore_types.go
  17. 54 1
      apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
  18. 2 2
      deploy/charts/external-secrets/Chart.yaml
  19. 2 1
      deploy/charts/external-secrets/README.md
  20. 4 0
      deploy/charts/external-secrets/templates/deployment.yaml
  21. 6 0
      deploy/charts/external-secrets/templates/rbac.yaml
  22. 1 1
      deploy/charts/external-secrets/templates/service.yaml
  23. 4 0
      deploy/charts/external-secrets/values.yaml
  24. 42 12
      deploy/crds/external-secrets.io_clustersecretstores.yaml
  25. 6 0
      deploy/crds/external-secrets.io_externalsecrets.yaml
  26. 45 12
      deploy/crds/external-secrets.io_secretstores.yaml
  27. 199 0
      design/001-design-crd-v1beta1.md
  28. 28 6
      docs/contributing-devguide.md
  29. 76 0
      docs/contributing-process.md
  30. 31 0
      docs/examples-anchore-engine-credentials.md
  31. 0 0
      docs/examples-gitops-using-fluxcd.md
  32. 65 0
      docs/examples-jenkins-kubernetes-credentials.md
  33. 19 0
      docs/guides-controller-class.md
  34. 50 0
      docs/guides-templating-v1.md
  35. 130 28
      docs/guides-templating.md
  36. 2 1
      docs/provider-akeyless.md
  37. 1 0
      docs/provider-aws-parameter-store.md
  38. 1 1
      docs/provider-aws-secrets-manager.md
  39. 1 0
      docs/provider-azure-key-vault.md
  40. 26 0
      docs/provider-fake.md
  41. 1 0
      docs/provider-gitlab-project-variables.md
  42. 5 26
      docs/provider-google-secrets-manager.md
  43. 91 3
      docs/provider-hashicorp-vault.md
  44. 1 0
      docs/provider-ibm-secrets-manager.md
  45. 5 1
      docs/provider-oracle-vault.md
  46. 4 2
      docs/provider-webhook.md
  47. 1 0
      docs/provider-yandex-lockbox.md
  48. 15 0
      docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml
  49. 27 0
      docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml
  50. 23 0
      docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml
  51. 28 0
      docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml
  52. 0 1
      docs/snippets/aws-parameter-store.yaml
  53. 0 1
      docs/snippets/aws-sm-store.yaml
  54. 0 4
      docs/snippets/azkv-external-secret.yaml
  55. 1 1
      docs/snippets/azkv-secret-store-mi.yaml
  56. 0 2
      docs/snippets/basic-secret-store.yaml
  57. 17 0
      docs/snippets/controller-class-store.yaml
  58. 18 0
      docs/snippets/fake-provider-es.yaml
  59. 9 0
      docs/snippets/fake-provider-secret.yaml
  60. 20 0
      docs/snippets/fake-provider-store.yaml
  61. 8 0
      docs/snippets/gcpsm-pod-wi-secret-store.yaml
  62. 18 0
      docs/snippets/gcpsm-wi-secret-store.yaml
  63. 25 0
      docs/snippets/jwk-template-v2-external-secret.yaml
  64. 4 0
      docs/snippets/multiline-template-v1-external-secret.yaml
  65. 32 0
      docs/snippets/multiline-template-v2-external-secret.yaml
  66. 18 6
      docs/snippets/oracle-secret-store.yaml
  67. 0 0
      docs/snippets/pkcs12-template-v1-external-secret.yaml
  68. 19 0
      docs/snippets/pkcs12-template-v2-external-secret.yaml
  69. 2 0
      docs/snippets/provider-aws-access.md
  70. 0 0
      docs/snippets/template-v1-from-secret.yaml
  71. 41 0
      docs/snippets/template-v2-from-secret.yaml
  72. 55 0
      docs/snippets/vault-anchore-engine-access-credentials-external-secret.yaml
  73. 0 1
      docs/snippets/vault-approle-store.yaml
  74. 34 0
      docs/snippets/vault-jenkins-credential-github-ssh-access-external-secret.yaml
  75. 34 0
      docs/snippets/vault-jenkins-credential-harbor-chart-robot-external-secret.yaml
  76. 28 0
      docs/snippets/vault-jenkins-credential-sonarqube-api-token-external-secret.yaml
  77. 0 1
      docs/snippets/vault-jwt-store.yaml
  78. 0 2
      docs/snippets/vault-kubernetes-store.yaml
  79. 0 1
      docs/snippets/vault-ldap-store.yaml
  80. 0 1
      docs/snippets/vault-token-store.yaml
  81. 139 0
      docs/spec.md
  82. 1 1
      e2e/Makefile
  83. 4 2
      e2e/framework/addon/chart.go
  84. 1 1
      e2e/framework/addon/eso.go
  85. 1 3
      e2e/framework/eso.go
  86. 7 2
      e2e/framework/framework.go
  87. 1 0
      e2e/run.sh
  88. 97 0
      e2e/suite/aws/common.go
  89. 45 0
      e2e/suite/aws/parameterstore/parameterstore.go
  90. 85 0
      e2e/suite/aws/parameterstore/parameterstore_managed.go
  91. 165 0
      e2e/suite/aws/parameterstore/provider.go
  92. 14 76
      e2e/suite/aws/secretsmanager/provider.go
  93. 0 0
      e2e/suite/aws/secretsmanager/secretsmanager.go
  94. 85 0
      e2e/suite/aws/secretsmanager/secretsmanager_managed.go
  95. 0 102
      e2e/suite/aws/secretsmanager_managed.go
  96. 66 0
      e2e/suite/azure/azure_cert.go
  97. 69 0
      e2e/suite/azure/azure_key.go
  98. 2 1
      e2e/suite/azure/azure_secret.go
  99. 80 4
      e2e/suite/azure/provider.go
  100. 3 1
      e2e/suite/import.go

+ 0 - 5
.github/codecov.yml

@@ -1,5 +0,0 @@
-ignore:
-- pkg/provider/**/fake
-coverage:
-  round: down
-  precision: 2

+ 8 - 10
.github/workflows/ci.yml

@@ -12,9 +12,7 @@ env:
   # Common versions
   GO_VERSION: '1.17'
   GOLANGCI_VERSION: 'v1.42.1'
-  # list of available versions: https://storage.googleapis.com/kubebuilder-tools
-  # TODO: 1.21.2 does not shut down properly with controller-runtime 0.9.2
-  KUBEBUILDER_TOOLS_VERSION: '1.20.2'
+  KUBERNETES_VERSION: '1.23.x'
   DOCKER_BUILDX_VERSION: 'v0.4.2'
 
   # Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run
@@ -164,22 +162,22 @@ jobs:
           key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
           restore-keys: ${{ runner.os }}-pkg-
 
-      - name: Add envtest binaries
-        run:  |
-          curl -sSLo envtest-bins.tar.gz "https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-${{env.KUBEBUILDER_TOOLS_VERSION}}-linux-amd64.tar.gz"
-          sudo mkdir -p /usr/local/kubebuilder
-          sudo tar -C /usr/local/kubebuilder --strip-components=1 -zvxf envtest-bins.tar.gz
+      - name: Add setup-envtest
+        run: |
+          go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
+          setup-envtest use ${{env.KUBERNETES_VERSION}} -p env --os $(go env GOOS) --arch $(go env GOARCH)
 
       - name: Cache envtest binaries
         uses: actions/cache@v2.1.7
         with:
-          path: /usr/local/kubebuilder
-          key: ${{ runner.os }}-kubebuilder-${{env.KUBEBUILDER_TOOLS_VERSION}}
+          path: /home/runner/.local/share/kubebuilder-envtest/
+          key: ${{ runner.os }}-kubebuilder-${{env.KUBERNETES_VERSION}}
           restore-keys: ${{ runner.os }}-kubebuilder-
 
       - name: Run Unit Tests
         run: |
           export KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT=true
+          source <(setup-envtest use ${{env.KUBERNETES_VERSION}} -p env --os $(go env GOOS) --arch $(go env GOARCH))
           make test
 
 

+ 3 - 10
.github/workflows/docs.yml

@@ -4,7 +4,6 @@ on:
   push:
     branches:
       - main
-
 env:
   # Common versions
   GO_VERSION: '1.17'
@@ -23,12 +22,6 @@ jobs:
           go-version: ${{ env.GO_VERSION }}
 
       - name: Build Docs
-        run: make docs
-
-      - name: Deploy
-        uses: peaceiris/actions-gh-pages@v3.8.0
-        with:
-          github_token: ${{ secrets.GITHUB_TOKEN }}
-          publish_dir: ./site
-          cname: external-secrets.io
-          keep_files: true
+        run: make docs.publish
+        env:
+          GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

+ 27 - 14
.github/workflows/e2e-managed.yml

@@ -1,6 +1,5 @@
 # Run secret-dependent e2e tests only after /ok-to-test-managed approval
 on:
-  pull_request:
   repository_dispatch:
     types: [ok-to-test-managed-command]
 
@@ -49,36 +48,48 @@ jobs:
 
     steps:
 
-    # set status=in_progress
-    - uses: actions/github-script@v1
+    # create new status check for this specific provider
+    - uses: actions/github-script@v6
+      if: ${{ always() }}
       env:
         number: ${{ github.event.client_payload.pull_request.number }}
+        provider: ${{ github.event.client_payload.slash_command.args.named.provider }}
         job: ${{ github.job }}
-        conclusion: ${{ job.status }}
       with:
         github-token: ${{ secrets.GITHUB_TOKEN }}
         script: |
-          const { data: pull } = await github.pulls.get({
+          const { data: pull } = await github.rest.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({
+          const { data: checks } = await github.rest.checks.listForRef({
             ...context.repo,
             ref
           });
+          const job_name = process.env.job + "-" + process.env.provider
           console.log("\n\nPR CHECKS: " + checks)
-          const check = checks.check_runs.filter(c => c.name === process.env.job);
+          const check = checks.check_runs.filter(c => c.name === job_name);
           console.log("\n\nPR Filtered CHECK: " + check)
           console.log(check)
-          const { data: result } = await github.checks.update({
+          if(check && check.length > 0){
+            const { data: result } = await github.rest.checks.update({
+              ...context.repo,
+              check_run_id: check[0].id,
+              status: 'in_progress',
+            });
+            return result;
+          }
+          const { data: result } = await github.rest.checks.create({
             ...context.repo,
-            check_run_id: check[0].id,
+            name: job_name,
+            head_sha: pull.head.sha,
             status: 'in_progress',
           });
           return result;
 
+
     # Check out merge commit
     - name: Fork based /ok-to-test-managed checkout
       uses: actions/checkout@v2
@@ -190,31 +201,33 @@ jobs:
         make tf.destroy.${PROVIDER}
 
     # set status=completed
-    - uses: actions/github-script@v1
+    - uses: actions/github-script@v6
       if: ${{ always() }}
       env:
         number: ${{ github.event.client_payload.pull_request.number }}
+        provider: ${{ github.event.client_payload.slash_command.args.named.provider }}
         job: ${{ github.job }}
         # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
         conclusion: ${{ job.status }}
       with:
         github-token: ${{ secrets.GITHUB_TOKEN }}
         script: |
-          const { data: pull } = await github.pulls.get({
+          const { data: pull } = await github.rest.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({
+          const { data: checks } = await github.rest.checks.listForRef({
             ...context.repo,
             ref
           });
+          const job_name = process.env.job + "-" + process.env.provider
           console.log("\n\nPR CHECKS: " + checks)
-          const check = checks.check_runs.filter(c => c.name === process.env.job);
+          const check = checks.check_runs.filter(c => c.name === job_name);
           console.log("\n\nPR Filtered CHECK: " + check)
           console.log(check)
-          const { data: result } = await github.checks.update({
+          const { data: result } = await github.rest.checks.update({
             ...context.repo,
             check_run_id: check[0].id,
             status: 'completed',

+ 10 - 8
.github/workflows/e2e.yml

@@ -9,6 +9,8 @@ env:
   GO_VERSION: '1.17'
   GOLANGCI_VERSION: 'v1.33'
   DOCKER_BUILDX_VERSION: 'v0.4.2'
+  KIND_VERSION: 'v0.11.1'
+  KIND_IMAGE: 'kindest/node:v1.23.3'
 
   # Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run
   # a step 'if env.GHCR_USERNAME' != ""', so we copy these to succinctly test whether
@@ -71,9 +73,9 @@ jobs:
     - name: Setup kind
       uses: engineerd/setup-kind@v0.5.0
       with:
-        version: "v0.11.1"
+        version: ${{env.KIND_VERSION}}
         wait: 10m
-        node_image: kindest/node:v1.20.7
+        node_image: ${{env.KIND_IMAGE}}
         name: external-secrets
 
     - name: Setup Docker Buildx
@@ -134,9 +136,9 @@ jobs:
     - name: Setup kind
       uses: engineerd/setup-kind@v0.5.0
       with:
-        version: "v0.11.1"
+        version: ${{env.KIND_VERSION}}
         wait: 10m
-        node_image: kindest/node:v1.20.7
+        node_image: ${{env.KIND_IMAGE}}
         name: external-secrets
 
     - name: Setup Docker Buildx
@@ -154,7 +156,7 @@ jobs:
         make test.e2e
 
     # Update check run called "integration-fork"
-    - uses: actions/github-script@v1
+    - uses: actions/github-script@v6
       id: update-check-run
       if: ${{ always() }}
       env:
@@ -165,13 +167,13 @@ jobs:
       with:
         github-token: ${{ secrets.GITHUB_TOKEN }}
         script: |
-          const { data: pull } = await github.pulls.get({
+          const { data: pull } = await github.rest.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({
+          const { data: checks } = await github.rest.checks.listForRef({
             ...context.repo,
             ref
           });
@@ -179,7 +181,7 @@ jobs:
           const check = checks.check_runs.filter(c => c.name === process.env.job);
           console.log("\n\nPR Filtered CHECK: " + check)
           console.log(check)
-          const { data: result } = await github.checks.update({
+          const { data: result } = await github.rest.checks.update({
             ...context.repo,
             check_run_id: check[0].id,
             status: 'completed',

+ 3 - 3
.github/workflows/helm.yml

@@ -26,7 +26,7 @@ jobs:
           make helm.generate
 
       - name: Set up Helm
-        uses: azure/setup-helm@v1.1
+        uses: azure/setup-helm@v2.0
         with:
           version: v3.4.2
 
@@ -62,7 +62,7 @@ jobs:
           git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
 
       - name: Set up Helm
-        uses: azure/setup-helm@v1.1
+        uses: azure/setup-helm@v2.0
         with:
           version: v3.4.2
 
@@ -71,7 +71,7 @@ jobs:
           make helm.generate
 
       - name: Run chart-releaser
-        uses: helm/chart-releaser-action@v1.2.1
+        uses: helm/chart-releaser-action@v1.3.0
         if: github.ref == 'refs/heads/main'
         env:
           CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

+ 11 - 0
.github/workflows/release.yml

@@ -10,6 +10,7 @@ on:
 
 env:
   GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
+  GO_VERSION: '1.17'
 
 jobs:
   release:
@@ -54,6 +55,16 @@ jobs:
         env:
           GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
 
+      - name: Setup Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: ${{ env.GO_VERSION }}
+
+      - name: Update Docs
+        run: make docs.publish DOCS_VERSION=${{ github.event.inputs.version }} DOCS_ALIAS=latest
+        env:
+          GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+
   promote:
     name: Promote Container Image
     runs-on: ubuntu-latest

+ 1 - 0
ADOPTERS.md

@@ -2,6 +2,7 @@
 
 <!-- Add yourself here if you are using ESO in your company or your project! -->
 
+- [Polarpoint](https://www.polarpoint.io/)
 - [Pento](https://www.pento.io/)
 - [Mixpanel](https://mixpanel.com)
 - [K8S Website Infra](https://k8s.io/)

+ 6 - 2
Makefile

@@ -190,8 +190,12 @@ helm.generate: helm.docs ## Copy crds to helm chart directory
 docs: generate ## Generate docs
 	$(MAKE) -C ./hack/api-docs build
 
-.PHONY: serve-docs
-serve-docs: ## Serve docs
+.PHONY: docs.publish
+docs.publish: generate ## Generate and deploys docs
+	$(MAKE) -C ./hack/api-docs build.publish
+
+.PHONY: docs.serve
+docs.serve: ## Serve docs
 	$(MAKE) -C ./hack/api-docs serve
 
 # ====================================================================================

+ 3 - 3
README.md

@@ -30,8 +30,8 @@ Multiple people and organizations are joining efforts to create a single Externa
 
 | Provider                                                                 | Stability |                                        Contact |
 | ------------------------------------------------------------------------ | :-------: | ---------------------------------------------: |
-| [AWS SM](https://external-secrets.io/provider-aws-secrets-manager/)      |   beta   | [ESO Org](https://github.com/external-secrets) |
-| [AWS PS](https://external-secrets.io/provider-aws-parameter-store/)      |   beta   | [ESO Org](https://github.com/external-secrets) |
+| [AWS SM](https://external-secrets.io/provider-aws-secrets-manager/)      |   stable   | [ESO Org](https://github.com/external-secrets) |
+| [AWS PS](https://external-secrets.io/provider-aws-parameter-store/)      |   stable   | [ESO Org](https://github.com/external-secrets) |
 | [Hashicorp Vault](https://external-secrets.io/provider-hashicorp-vault/) |   stable   | [ESO Org](https://github.com/external-secrets) |
 | [GCP SM](https://external-secrets.io/provider-google-secrets-manager/)   |   stable | [ESO Org](https://github.com/external-secrets) |
 
@@ -39,7 +39,7 @@ Multiple people and organizations are joining efforts to create a single Externa
 
 | Provider                                                            | Stability |                  Contact                   |
 | ------------------------------------------------------------------- | :-------: | :----------------------------------------: |
-| [Azure KV](https://external-secrets.io/provider-azure-key-vault/)   |   alpha   | [@ahmedmus-1A](https://github.com/ahmedmus-1A) [@asnowfix](https://github.com/asnowfix) [@ncourbet-1A](https://github.com/ncourbet-1A) [@1A-mj](https://github.com/1A-mj) |
+| [Azure KV](https://external-secrets.io/provider-azure-key-vault/)   |   beta   | [@ahmedmus-1A](https://github.com/ahmedmus-1A) [@asnowfix](https://github.com/asnowfix) [@ncourbet-1A](https://github.com/ncourbet-1A) [@1A-mj](https://github.com/1A-mj) |
 | [IBM SM](https://external-secrets.io/provider-ibm-secrets-manager/) |   alpha   |   [@knelasevero](https://github.com/knelasevero) [@sebagomez](https://github.com/sebagomez) [@ricardoptcosta](https://github.com/ricardoptcosta)  |
 | [Yandex Lockbox](https://external-secrets.io/provider-yandex-lockbox/) |   alpha   |   [@AndreyZamyslov](https://github.com/AndreyZamyslov) [@knelasevero](https://github.com/knelasevero)          |
 | [Gitlab Project Variables](https://external-secrets.io/provider-gitlab-project-variables/) |   alpha   |   [@Jabray5](https://github.com/Jabray5)          |

+ 18 - 0
apis/externalsecrets/v1alpha1/externalsecret_types.go

@@ -59,6 +59,12 @@ type ExternalSecretTemplate struct {
 	// +optional
 	Type corev1.SecretType `json:"type,omitempty"`
 
+	// EngineVersion specifies the template engine version
+	// that should be used to compile/execute the
+	// template specified in .data and .templateFrom[].
+	// +kubebuilder:default="v1"
+	EngineVersion TemplateEngineVersion `json:"engineVersion,omitempty"`
+
 	// +optional
 	Metadata ExternalSecretTemplateMetadata `json:"metadata,omitempty"`
 
@@ -69,6 +75,13 @@ type ExternalSecretTemplate struct {
 	TemplateFrom []TemplateFrom `json:"templateFrom,omitempty"`
 }
 
+type TemplateEngineVersion string
+
+const (
+	TemplateEngineV1 TemplateEngineVersion = "v1"
+	TemplateEngineV2 TemplateEngineVersion = "v2"
+)
+
 // +kubebuilder:validation:MinProperties=1
 // +kubebuilder:validation:MaxProperties=1
 type TemplateFrom struct {
@@ -180,6 +193,11 @@ const (
 	ConditionReasonSecretSyncedError = "SecretSyncedError"
 	// ConditionReasonSecretDeleted indicates that the secret has been deleted.
 	ConditionReasonSecretDeleted = "SecretDeleted"
+
+	ReasonInvalidStoreRef      = "InvalidStoreRef"
+	ReasonProviderClientConfig = "InvalidProviderClientConfig"
+	ReasonUpdateFailed         = "UpdateFailed"
+	ReasonUpdated              = "Updated"
 )
 
 type ExternalSecretStatus struct {

+ 29 - 0
apis/externalsecrets/v1alpha1/generic_store.go

@@ -33,8 +33,13 @@ type GenericStore interface {
 	metav1.Object
 
 	GetObjectMeta() *metav1.ObjectMeta
+	GetTypeMeta() *metav1.TypeMeta
+
 	GetSpec() *SecretStoreSpec
 	GetNamespacedName() string
+	GetStatus() SecretStoreStatus
+	SetStatus(status SecretStoreStatus)
+	Copy() GenericStore
 }
 
 // +kubebuilder:object:root:false
@@ -45,10 +50,22 @@ func (c *SecretStore) GetObjectMeta() *metav1.ObjectMeta {
 	return &c.ObjectMeta
 }
 
+func (c *SecretStore) GetTypeMeta() *metav1.TypeMeta {
+	return &c.TypeMeta
+}
+
 func (c *SecretStore) GetSpec() *SecretStoreSpec {
 	return &c.Spec
 }
 
+func (c *SecretStore) GetStatus() SecretStoreStatus {
+	return c.Status
+}
+
+func (c *SecretStore) SetStatus(status SecretStoreStatus) {
+	c.Status = status
+}
+
 func (c *SecretStore) GetNamespacedName() string {
 	return fmt.Sprintf("%s/%s", c.Namespace, c.Name)
 }
@@ -65,6 +82,10 @@ func (c *ClusterSecretStore) GetObjectMeta() *metav1.ObjectMeta {
 	return &c.ObjectMeta
 }
 
+func (c *ClusterSecretStore) GetTypeMeta() *metav1.TypeMeta {
+	return &c.TypeMeta
+}
+
 func (c *ClusterSecretStore) GetSpec() *SecretStoreSpec {
 	return &c.Spec
 }
@@ -73,6 +94,14 @@ func (c *ClusterSecretStore) Copy() GenericStore {
 	return c.DeepCopy()
 }
 
+func (c *ClusterSecretStore) GetStatus() SecretStoreStatus {
+	return c.Status
+}
+
+func (c *ClusterSecretStore) SetStatus(status SecretStoreStatus) {
+	c.Status = status
+}
+
 func (c *ClusterSecretStore) GetNamespacedName() string {
 	return fmt.Sprintf("%s/%s", c.Namespace, c.Name)
 }

+ 27 - 0
apis/externalsecrets/v1alpha1/secretstore_fake_types.go

@@ -0,0 +1,27 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+// FakeProvider configures a fake provider that returns static values.
+type FakeProvider struct {
+	Data []FakeProviderData `json:"data"`
+}
+
+type FakeProviderData struct {
+	Key      string            `json:"key"`
+	Value    string            `json:"value,omitempty"`
+	ValueMap map[string]string `json:"valueMap,omitempty"`
+	Version  string            `json:"version,omitempty"`
+}

+ 2 - 4
apis/externalsecrets/v1alpha1/secretstore_kubernetes_types.go

@@ -44,8 +44,6 @@ type KubernetesServer struct {
 // Configures a store to sync secrets with a Kubernetes instance.
 type KubernetesProvider struct {
 	// configures the Kubernetes server Address.
-	// +kubebuilder:default= kubernetes.default
-	// +optional
 	Server KubernetesServer `json:"server,omitempty"`
 
 	// Auth configures how secret-manager authenticates with a Kubernetes instance.
@@ -74,8 +72,8 @@ type KubernetesAuth struct {
 }
 
 type CertAuth struct {
-	ClientCert esmeta.SecretKeySelector `json:"cert,omitempty"`
-	ClientKey  esmeta.SecretKeySelector `json:"key,omitempty"`
+	ClientCert esmeta.SecretKeySelector `json:"clientCert,omitempty"`
+	ClientKey  esmeta.SecretKeySelector `json:"clientKey,omitempty"`
 }
 
 type TokenAuth struct {

+ 16 - 14
apis/externalsecrets/v1alpha1/secretstore_oracle_types.go

@@ -19,31 +19,33 @@ import (
 // Configures an store to sync secrets using a Oracle Vault
 // backend.
 type OracleProvider struct {
-	// Auth configures how secret-manager authenticates with the Oracle Vault.
-	Auth OracleAuth `json:"auth"`
-
-	// User is an access OCID specific to the account.
-	User string `json:"user,omitempty"`
-
-	// Tenancy is the tenancy OCID where secret is located.
-	Tenancy string `json:"tenancy,omitempty"`
-
-	// Region is the region where secret is located.
-	Region string `json:"region,omitempty"`
+	// Region is the region where vault is located.
+	Region string `json:"region"`
 
 	// Vault is the vault's OCID of the specific vault where secret is located.
-	Vault string `json:"vault,omitempty"`
+	Vault string `json:"vault"`
+
+	// Auth configures how secret-manager authenticates with the Oracle Vault.
+	// If empty, use the instance principal, otherwise the user credentials specified in Auth.
+	// +optional
+	Auth *OracleAuth `json:"auth,omitempty"`
 }
 
 type OracleAuth struct {
+	// Tenancy is the tenancy OCID where user is located.
+	Tenancy string `json:"tenancy"`
+
+	// User is an access OCID specific to the account.
+	User string `json:"user"`
+
 	// SecretRef to pass through sensitive information.
 	SecretRef OracleSecretRef `json:"secretRef"`
 }
 
 type OracleSecretRef struct {
 	// PrivateKey is the user's API Signing Key in PEM format, used for authentication.
-	PrivateKey esmeta.SecretKeySelector `json:"privatekey,omitempty"`
+	PrivateKey esmeta.SecretKeySelector `json:"privatekey"`
 
 	// Fingerprint is the fingerprint of the API private key.
-	Fingerprint esmeta.SecretKeySelector `json:"fingerprint,omitempty"`
+	Fingerprint esmeta.SecretKeySelector `json:"fingerprint"`
 }

+ 9 - 0
apis/externalsecrets/v1alpha1/secretstore_types.go

@@ -85,6 +85,9 @@ type SecretStoreProvider struct {
 	// Kubernetes configures this store to sync secrets using a Kubernetes cluster provider
 	// +optional
 	Kubernetes *KubernetesProvider `json:"kubernetes,omitempty"`
+	// Fake configures a store with static key/value pairs
+	// +optional
+	Fake *FakeProvider `json:"fake,omitempty"`
 }
 
 type SecretStoreRetrySettings struct {
@@ -96,6 +99,11 @@ type SecretStoreConditionType string
 
 const (
 	SecretStoreReady SecretStoreConditionType = "Ready"
+
+	ReasonInvalidStore          = "InvalidStoreConfiguration"
+	ReasonInvalidProviderConfig = "InvalidProviderConfig"
+	ReasonValidationFailed      = "ValidationFailed"
+	ReasonStoreValid            = "Valid"
 )
 
 type SecretStoreStatusCondition struct {
@@ -122,6 +130,7 @@ type SecretStoreStatus struct {
 
 // SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields.
 // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
+// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
 // +kubebuilder:subresource:status
 // +kubebuilder:resource:scope=Namespaced,categories={externalsecrets},shortName=ss
 type SecretStore struct {

+ 54 - 1
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -616,6 +616,50 @@ func (in *ExternalSecretTemplateMetadata) DeepCopy() *ExternalSecretTemplateMeta
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *FakeProvider) DeepCopyInto(out *FakeProvider) {
+	*out = *in
+	if in.Data != nil {
+		in, out := &in.Data, &out.Data
+		*out = make([]FakeProviderData, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeProvider.
+func (in *FakeProvider) DeepCopy() *FakeProvider {
+	if in == nil {
+		return nil
+	}
+	out := new(FakeProvider)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *FakeProviderData) DeepCopyInto(out *FakeProviderData) {
+	*out = *in
+	if in.ValueMap != nil {
+		in, out := &in.ValueMap, &out.ValueMap
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeProviderData.
+func (in *FakeProviderData) DeepCopy() *FakeProviderData {
+	if in == nil {
+		return nil
+	}
+	out := new(FakeProviderData)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *GCPSMAuth) DeepCopyInto(out *GCPSMAuth) {
 	*out = *in
@@ -881,7 +925,11 @@ func (in *OracleAuth) DeepCopy() *OracleAuth {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *OracleProvider) DeepCopyInto(out *OracleProvider) {
 	*out = *in
-	in.Auth.DeepCopyInto(&out.Auth)
+	if in.Auth != nil {
+		in, out := &in.Auth, &out.Auth
+		*out = new(OracleAuth)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleProvider.
@@ -1033,6 +1081,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
 		*out = new(KubernetesProvider)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Fake != nil {
+		in, out := &in.Fake, &out.Fake
+		*out = new(FakeProvider)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretStoreProvider.

+ 2 - 2
deploy/charts/external-secrets/Chart.yaml

@@ -2,8 +2,8 @@ apiVersion: v2
 name: external-secrets
 description: External secret management for Kubernetes
 type: application
-version: "0.3.11"
-appVersion: "v0.3.11"
+version: "0.4.2"
+appVersion: "v0.4.2"
 kubeVersion: ">= 1.11.0-0"
 keywords:
   - kubernetes-external-secrets

+ 2 - 1
deploy/charts/external-secrets/README.md

@@ -4,7 +4,7 @@
 
 [//]: # (README.md generated by gotmpl. DO NOT EDIT.)
 
-![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.3.11](https://img.shields.io/badge/Version-0.3.11-informational?style=flat-square)
+![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.4.2](https://img.shields.io/badge/Version-0.4.2-informational?style=flat-square)
 
 External secret management for Kubernetes
 
@@ -36,6 +36,7 @@ The command removes all the Kubernetes components associated with the chart and
 |-----|------|---------|-------------|
 | affinity | object | `{}` |  |
 | concurrent | int | `1` | Specifies the number of concurrent ExternalSecret Reconciles external-secret executes at a time. |
+| controllerClass | string | `""` | If set external secrets will filter matching Secret Stores with the appropriate controller values. |
 | deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
 | extraArgs | object | `{}` |  |
 | extraEnv | list | `[]` |  |

+ 4 - 0
deploy/charts/external-secrets/templates/deployment.yaml

@@ -51,6 +51,9 @@ spec:
           {{- if .Values.scopedNamespace }}
           - --namespace={{ .Values.scopedNamespace }}
           {{- end }}
+          {{- if .Values.controllerClass }}
+          - --controller-class={{ .Values.controllerClass }}
+          {{- end }}
           {{- if .Values.concurrent }}
           - --concurrent={{ .Values.concurrent }}
           {{- end }}
@@ -65,6 +68,7 @@ spec:
           ports:
             - containerPort: {{ .Values.prometheus.service.port }}
               protocol: TCP
+              name: metrics
           {{- with .Values.extraEnv }}
           env:
             {{- toYaml . | nindent 12 }}

+ 6 - 0
deploy/charts/external-secrets/templates/rbac.yaml

@@ -22,6 +22,12 @@ rules:
     - "externalsecrets"
     - "externalsecrets/status"
     - "externalsecrets/finalizers"
+    - "secretstores"
+    - "secretstores/status"
+    - "secretstores/finalizers"
+    - "clustersecretstores"
+    - "clustersecretstores/status"
+    - "clustersecretstores/finalizers"
     verbs:
     - "update"
     - "patch"

+ 1 - 1
deploy/charts/external-secrets/templates/service.yaml

@@ -13,8 +13,8 @@ spec:
   type: ClusterIP
   ports:
     - port: {{ .Values.prometheus.service.port }}
-      targetPort: {{ .Values.prometheus.service.port }}
       protocol: TCP
+      name: metrics
   selector:
     {{- include "external-secrets.selectorLabels" . | nindent 4 }}
 {{- end }}

+ 4 - 0
deploy/charts/external-secrets/values.yaml

@@ -17,6 +17,10 @@ fullnameOverride: ""
 # than one instance of external-secrets operates at a time.
 leaderElect: false
 
+# -- If set external secrets will filter matching
+# Secret Stores with the appropriate controller values.
+controllerClass: ""
+
 # -- If set external secrets are only reconciled in the
 # provided namespace
 scopedNamespace: ""

+ 42 - 12
deploy/crds/external-secrets.io_clustersecretstores.yaml

@@ -380,6 +380,29 @@ spec:
                     required:
                     - vaultUrl
                     type: object
+                  fake:
+                    description: Fake configures a store with static key/value pairs
+                    properties:
+                      data:
+                        items:
+                          properties:
+                            key:
+                              type: string
+                            value:
+                              type: string
+                            valueMap:
+                              additionalProperties:
+                                type: string
+                              type: object
+                            version:
+                              type: string
+                          required:
+                          - key
+                          type: object
+                        type: array
+                    required:
+                    - data
+                    type: object
                   gcpsm:
                     description: GCPSM configures this store to sync secrets using
                       Google Cloud Platform Secret Manager provider
@@ -540,7 +563,7 @@ spec:
                           cert:
                             description: has both clientCert and clientKey as secretKeySelector
                             properties:
-                              cert:
+                              clientCert:
                                 description: A reference to a specific 'key' within
                                   a Secret resource, In some instances, `key` is a
                                   required field.
@@ -562,7 +585,7 @@ spec:
                                       the referent.
                                     type: string
                                 type: object
-                              key:
+                              clientKey:
                                 description: A reference to a specific 'key' within
                                   a Secret resource, In some instances, `key` is a
                                   required field.
@@ -638,7 +661,6 @@ spec:
                         description: Remote namespace to fetch the secrets from
                         type: string
                       server:
-                        default: kubernetes.default
                         description: configures the Kubernetes server Address.
                         properties:
                           caBundle:
@@ -684,7 +706,8 @@ spec:
                     properties:
                       auth:
                         description: Auth configures how secret-manager authenticates
-                          with the Oracle Vault.
+                          with the Oracle Vault. If empty, use the instance principal,
+                          otherwise the user credentials specified in Auth.
                         properties:
                           secretRef:
                             description: SecretRef to pass through sensitive information.
@@ -731,25 +754,32 @@ spec:
                                       the referent.
                                     type: string
                                 type: object
+                            required:
+                            - fingerprint
+                            - privatekey
                             type: object
+                          tenancy:
+                            description: Tenancy is the tenancy OCID where user is
+                              located.
+                            type: string
+                          user:
+                            description: User is an access OCID specific to the account.
+                            type: string
                         required:
                         - secretRef
+                        - tenancy
+                        - user
                         type: object
                       region:
-                        description: Region is the region where secret is located.
-                        type: string
-                      tenancy:
-                        description: Tenancy is the tenancy OCID where secret is located.
-                        type: string
-                      user:
-                        description: User is an access OCID specific to the account.
+                        description: Region is the region where vault is located.
                         type: string
                       vault:
                         description: Vault is the vault's OCID of the specific vault
                           where secret is located.
                         type: string
                     required:
-                    - auth
+                    - region
+                    - vault
                     type: object
                   vault:
                     description: Vault configures this store to sync secrets using

+ 6 - 0
deploy/crds/external-secrets.io_externalsecrets.yaml

@@ -148,6 +148,12 @@ spec:
                         additionalProperties:
                           type: string
                         type: object
+                      engineVersion:
+                        default: v1
+                        description: EngineVersion specifies the template engine version
+                          that should be used to compile/execute the template specified
+                          in .data and .templateFrom[].
+                        type: string
                       metadata:
                         description: ExternalSecretTemplateMetadata defines metadata
                           fields for the Secret blueprint.

+ 45 - 12
deploy/crds/external-secrets.io_secretstores.yaml

@@ -22,6 +22,9 @@ spec:
     - jsonPath: .metadata.creationTimestamp
       name: AGE
       type: date
+    - jsonPath: .status.conditions[?(@.type=="Ready")].reason
+      name: Status
+      type: string
     name: v1alpha1
     schema:
       openAPIV3Schema:
@@ -380,6 +383,29 @@ spec:
                     required:
                     - vaultUrl
                     type: object
+                  fake:
+                    description: Fake configures a store with static key/value pairs
+                    properties:
+                      data:
+                        items:
+                          properties:
+                            key:
+                              type: string
+                            value:
+                              type: string
+                            valueMap:
+                              additionalProperties:
+                                type: string
+                              type: object
+                            version:
+                              type: string
+                          required:
+                          - key
+                          type: object
+                        type: array
+                    required:
+                    - data
+                    type: object
                   gcpsm:
                     description: GCPSM configures this store to sync secrets using
                       Google Cloud Platform Secret Manager provider
@@ -540,7 +566,7 @@ spec:
                           cert:
                             description: has both clientCert and clientKey as secretKeySelector
                             properties:
-                              cert:
+                              clientCert:
                                 description: A reference to a specific 'key' within
                                   a Secret resource, In some instances, `key` is a
                                   required field.
@@ -562,7 +588,7 @@ spec:
                                       the referent.
                                     type: string
                                 type: object
-                              key:
+                              clientKey:
                                 description: A reference to a specific 'key' within
                                   a Secret resource, In some instances, `key` is a
                                   required field.
@@ -638,7 +664,6 @@ spec:
                         description: Remote namespace to fetch the secrets from
                         type: string
                       server:
-                        default: kubernetes.default
                         description: configures the Kubernetes server Address.
                         properties:
                           caBundle:
@@ -684,7 +709,8 @@ spec:
                     properties:
                       auth:
                         description: Auth configures how secret-manager authenticates
-                          with the Oracle Vault.
+                          with the Oracle Vault. If empty, use the instance principal,
+                          otherwise the user credentials specified in Auth.
                         properties:
                           secretRef:
                             description: SecretRef to pass through sensitive information.
@@ -731,25 +757,32 @@ spec:
                                       the referent.
                                     type: string
                                 type: object
+                            required:
+                            - fingerprint
+                            - privatekey
                             type: object
+                          tenancy:
+                            description: Tenancy is the tenancy OCID where user is
+                              located.
+                            type: string
+                          user:
+                            description: User is an access OCID specific to the account.
+                            type: string
                         required:
                         - secretRef
+                        - tenancy
+                        - user
                         type: object
                       region:
-                        description: Region is the region where secret is located.
-                        type: string
-                      tenancy:
-                        description: Tenancy is the tenancy OCID where secret is located.
-                        type: string
-                      user:
-                        description: User is an access OCID specific to the account.
+                        description: Region is the region where vault is located.
                         type: string
                       vault:
                         description: Vault is the vault's OCID of the specific vault
                           where secret is located.
                         type: string
                     required:
-                    - auth
+                    - region
+                    - vault
                     type: object
                   vault:
                     description: Vault configures this store to sync secrets using

+ 199 - 0
design/001-design-crd-v1beta1.md

@@ -0,0 +1,199 @@
+```yaml
+---
+title: External Secrets CRD promotion
+version: v1beta1
+authors: all of us
+creation-date: 2022-feb-08
+status: draft
+---
+```
+
+# External Secrets Operator CRD
+
+## Table of Contents
+
+<!-- toc -->
+<!-- /toc -->
+
+## Summary
+
+This is a proposal to design the Promoted ExternalSecrets CRD.
+
+## Motivation
+
+The project came up to the point to have grown in users and maturity, hence we are starting to drive efforts to bring it to GA. The promotion of the ExternalSecrets CRD to beta is one of this efforts.
+This design documentation aims to capture some final changes for ExternalSecrets CRD.
+
+### Goals
+
+- Define a beta CRD
+- Define strucutre for getting all provider secrets
+- Define structure for new templating engine
+### Non-Goals
+
+This KEP proposes the CRD Spec and documents the use-cases, not the choice of technology or migration path towards implementing the CRD.
+
+## Terminology
+
+* External Secrets Operator `ESO`: A Application that runs a control loop which syncs secrets
+* ESO `instance`: A single entity that runs a control loop
+* Provider: Is a **source** for secrets. The Provider is external to ESO. It can be a hosted service like Alibaba Cloud SecretsManager, AWS SystemsManager, Azure KeyVault etc
+* SecretStore `ST`: A Custom Resource to authenticate and configure the connection between the ESO instance and the Provider
+* ExternalSecret `ES`: A Custom Resource that declares which secrets should be synced
+* Frontend: A **sink** for the synced secrets, usually a `Secret` resource
+* Secret: Credentials that act as a key to sensitive information
+
+### User Definitions
+* `operator :=` I manage one or multiple `ESO` instances
+* `user :=` I only create `ES`, ESO is managed by someone else
+
+### User Stories
+From that we can derive the following requirements or user stories:
+1. As a ESO operator I want to get all the secrets of a given path from a given provider, if the provider supports it
+2. As a ESO operator I want to handle templating like it is a natural language, not needing to worry about how it is actually implemented.
+
+## Proposal
+
+### External Secret
+
+```yaml
+#only changed fields are commented out.
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: "hello-world"
+  labels:
+    acme.org/owned-by: "q-team"
+  annotations:
+    acme.org/sha: 1234
+
+spec:
+  secretStoreRef:
+    name: secret-store-name
+    kind: SecretStore
+  refreshInterval: "1h"
+  target:
+    name: my-secret
+    creationPolicy: 'Merge'
+    template:
+      engineVersion: v2 #Defaults to v2 in v1beta1
+      type: kubernetes.io/dockerconfigjson 
+      metadata:
+        annotations: {}
+        labels: {}
+      data:
+        config.yml: |
+          endpoints:
+          - https://{{ .data.user }}:{{ .data.password }}@api.exmaple.com
+
+      templateFrom:
+      - configMap:
+          name: alertmanager
+          items:
+          - key: alertmanager.yaml
+  data:
+    - secretKey: secret-key-to-be-managed
+      remoteRef:
+        key: provider-key
+        version: provider-key-version
+        property: provider-key-property
+  dataFrom:
+  - extract: #extract all the keys from one given secret
+      key: provider-key
+      version: provider-key-version
+      property: provider-key-property
+  - find:
+      name:  #find secrets that match a particular pattern
+        regexp: .*pattern.*
+      tags:  #find secrets that match the following labels/tags
+        provider-label: provider-value
+status:
+  refreshTime: "2019-08-12T12:33:02Z"
+  conditions:
+  - type: Ready
+    status: "True"
+    reason: "SecretSynced"
+    message: "Secret was synced"
+    lastTransitionTime: "2019-08-12T12:33:02Z"
+```
+
+#### Behavior
+
+ExternalSecrets now will have a different structure for `dataFrom`, which will allow fetching several provider secrets with only one ExternalSecret definition. It should be possible to fetch secrets based on regular expressions or by a label/tag selector.
+
+If the user desires to rename the secret keys (e.g. because the key name is not a valid secret key name `/foo/bar`) they should use `template` functions to produce a mapping. 
+### Secret Store
+
+SecretStore and ClusterSecretStore do not have any changes from v1alpha1.
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+  name: example
+  namespace: example-ns
+spec:
+  controller: dev
+  retrySettings:
+    maxRetries: 5
+    retryInterval: "10s"
+  provider:
+    aws:
+      service: SecretsManager
+      role: iam-role
+      region: eu-central-1
+      auth:
+        secretRef:
+          accessKeyID:
+            name: awssm-secret
+            key: access-key
+          secretAccessKey:
+            name: awssm-secret
+            key: secret-access-key
+    vault:
+      server: "https://vault.acme.org"
+      path: "secret"
+      version: "v2"
+      namespace: "a-team"
+      caBundle: "..."
+      caProvider:
+        type: "Secret"
+        name: "my-cert-secret"
+        key: "cert-key"
+      auth:
+        tokenSecretRef:
+          name: "my-secret"
+          namespace: "secret-admin"
+          key: "vault-token"
+        appRole:
+          path: "approle"
+          roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
+          secretRef:
+            name: "my-secret"
+            namespace: "secret-admin"
+            key: "vault-token"
+        kubernetes:
+          mountPath: "kubernetes"
+          role: "demo"
+          serviceAccountRef:
+            name: "my-sa"
+            namespace: "secret-admin"
+          secretRef:
+            name: "my-secret"
+            namespace: "secret-admin"
+            key: "vault"
+    gcpsm:
+      auth:
+        secretRef:
+          secretAccessKeySecretRef:
+            name: gcpsm-secret
+            key: secret-access-credentials
+      projectID: myproject
+status:
+  conditions:
+  - type: Ready
+    status: "False"
+    reason: "ConfigError"
+    message: "SecretStore validation failed"
+    lastTransitionTime: "2019-08-12T12:33:02Z"
+```

+ 28 - 6
docs/contributing-devguide.md

@@ -8,17 +8,28 @@ git clone https://github.com/external-secrets/external-secrets.git
 cd external-secrets
 ```
 
-If you want to run controller tests you also need to install kubebuilder's `envtest`:
+If you want to run controller tests you also need to install kubebuilder's `envtest`.
+
+The recommended way to do so is to install [setup-envtest](https://pkg.go.dev/sigs.k8s.io/controller-runtime/tools/setup-envtest)
+
+Here is an example on how to set it up:
 
 ```
-export KUBEBUILDER_TOOLS_VERSION='1.20.2' # check for latest version or a version that has support to what you are testing
+go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
+
+# list available versions
+setup-envtest list --os $(go env GOOS) --arch $(go env GOARCH)
 
-curl -sSLo envtest-bins.tar.gz "https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-$KUBEBUILDER_TOOLS_VERSION-linux-amd64.tar.gz"
+# To use a specific version
+setup-envtest use -p path 1.20.2
+
+#To set environment variables
+source <(setup-envtest use 1.20.2 -p env --os $(go env GOOS) --arch $(go env GOARCH))
 
-sudo mkdir -p /usr/local/kubebuilder
-sudo tar -C /usr/local/kubebuilder --strip-components=1 -zvxf envtest-bins.tar.gz
 ```
 
+for more information, please see [setup-envtest docs](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest)
+
 ## Building & Testing
 
 The project uses the `make` build system. It'll run code generators, tests and
@@ -74,8 +85,15 @@ kind create cluster --name external-secrets
 export TAG=v2
 export IMAGE=eso-local
 
+#For building in linux
 docker build . -t $IMAGE:$TAG --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux
 
+#For building in MacOS (OSX)
+#docker build . -t $IMAGE:$TAG --build-arg TARGETARCH=amd64 --build-arg TARGETOS=darwin
+
+#For building in ARM
+#docker build . -t $IMAGE:$TAG --build-arg TARGETARCH=arm --build-arg TARGETOS=linux
+
 make helm.generate
 helm upgrade --install external-secrets ./deploy/charts/external-secrets/ --set image.repository=$IMAGE --set image.tag=$TAG
 ```
@@ -86,7 +104,7 @@ helm upgrade --install external-secrets ./deploy/charts/external-secrets/ --set
 
 ## Documentation
 
-We use [mkdocs material](https://squidfunk.github.io/mkdocs-material/) to generate this
+We use [mkdocs material](https://squidfunk.github.io/mkdocs-material/) and [mike](https://github.com/jimporter/mike) to generate this
 documentation. See `/docs` for the source code and `/hack/api-docs` for the build process.
 
 When writing documentation it is advised to run the mkdocs server with livereload:
@@ -103,3 +121,7 @@ make serve-docs
 ```
 
 Open `http://localhost:8000` in your browser.
+
+Since mike uses a branch to create/update documentation, any docs operation will create a diff on your local `gh-pages` branch.
+
+When finished writing/reviewing the docs, clean up your local docs branch changes with `git branch -D gh-pages`

+ 76 - 0
docs/contributing-process.md

@@ -31,6 +31,82 @@ for the lifecycle of the PR: review, merging, ping on inactivity, close.
 We close pull requests or issues if there is no response from the author for
 a period of time. Feel free to reopen if you want to get back on it.
 
+### Triggering e2e tests
+
+We have an extensive set of e2e tests that test the integration with *real* cloud provider APIs.
+Maintainers must trigger these kind of tests manually for PRs that come from forked repositories. These tests run inside a `kind` cluster in the GitHub Actions runner:
+
+```
+/ok-to-test sha=xxxxxx
+```
+
+#### Executing e2e tests locally
+
+You have to prepare your shell environment with the necessary variables so the e2e test
+runner knows what credentials to use. See `e2e/run.sh` for the variables that are passed in.
+If you e.g. want to test AWS integration make sure set all `AWS_*` variables mentioned
+in that file.
+
+Use [ginkgo labels](https://onsi.github.io/ginkgo/#spec-labels) to select the tests
+you want to execute. You have to specify `!managed` to ensure that you do not
+run managed tests.
+
+```
+make test.e2e GINKGO_LABELS='gcp&&!managed'
+```
+
+#### Managed Kubernetes e2e tests
+
+There's another suite of e2e tests that integrate with managed Kuberentes offerings.
+They create real infrastructure at a cloud provider and deploy the controller
+into that environment.
+This is necessary to test the authentication integration
+([GCP Worklaod Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity),
+[EKS IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)...).
+
+These tests are time intensive (~20-45min) and must be triggered manually by
+a maintainer when a particular provider or authentication mechanism was changed:
+
+```
+/ok-to-test-managed sha=xxxxxx provider=aws
+# or
+/ok-to-test-managed sha=xxxxxx provider=gcp
+```
+
+Both tests can run in parallel. Once started they add a dynamic GitHub check `integration-managed-(gcp|aws)` to the PR that triggered the test.
+
+
+### Executing Managed Kubernetes e2e tests locally
+
+You have to prepare your shell environment with the necessary variables so the e2e
+test runner knows what credentials to use. See `.github/workflows/e2e-managed.yml`
+for the variables that are passed in. If you e.g. want to test AWS integration make
+sure set all variables containing `AWS_*` and `TF_VAR_AWS_*` mentioned in that file.
+
+Then execute `tf.apply.aws` or `tf.apply.gcp` to create the infrastructure.
+
+```
+make tf.apply.aws
+```
+
+Then run the `managed` testsuite. You will need push permissions to the external-secrets ghcr repository. You can set `IMAGE_REGISTRY` to control which image registry is used to store the controller and e2e test images in.
+
+You also have to setup a proper Kubeconfig so the e2e test pod gets deployed into the managed cluster.
+
+```
+aws eks update-kubeconfig --name ${AWS_CLUSTER_NAME}
+or
+gcloud container clusters get-credentials ${GCP_GKE_CLUSTER} --region europe-west1-b
+```
+
+Use [ginkgo labels](https://onsi.github.io/ginkgo/#spec-labels) to select the tests
+you want to execute.
+
+```
+# you may have to set IMAGE_REGISTRY=docker.io/your-user/external-secrets
+make test.e2e.managed GINKGO_LABELS='gcp'
+```
+
 ## Proposal Process
 Before we introduce significant changes to the project we want to gather feedback
 from the community to ensure that we progress in the right direction before we

+ 31 - 0
docs/examples-anchore-engine-credentials.md

@@ -0,0 +1,31 @@
+# Getting started
+
+Anchore Engine is an open-source project that provides a centralized service for inspection, analysis, and certification of container images. With Kubernetes, it also brings nice features like preventing unscanned images from being deployed into your clusters
+
+## Installing with Helm
+
+There are several parts of the installation that require credentials these being :-
+
+ANCHORE_ADMIN_USERNAME
+ANCHORE_ADMIN_PASSWORD
+ANCHORE_DB_PASSWORD
+db-url
+db-user
+postgres-password
+
+
+Creating the following external secret ensure the credentials are drawn from the backend provider of choice. The example shown here works with Hashicorp Vault and AWS Secrets Manager providers.
+
+#### Hashicorp Vault
+
+``` yaml
+{% include 'vault-anchore-engine-access-credentials-external-secret.yaml' %}
+```
+
+
+#### AWS Secrets Manager
+
+``` yaml
+{% include 'aws-anchore-engine-access-credentials-external-secret.yaml' %}
+```
+

+ 0 - 0
docs/guides-gitops-using-fluxcd.md → docs/examples-gitops-using-fluxcd.md


+ 65 - 0
docs/examples-jenkins-kubernetes-credentials.md

@@ -0,0 +1,65 @@
+# Getting started
+
+Jenkins is one of the most popular automation servers for continous integration, automation, scheduling jobs and for generic pipelining. It has an extensive set of plugins that extend or provide additional functionality including the [kubernetes credentials plugin](https://github.com/jenkinsci/kubernetes-credentials-provider-plugin). This plugin takes kubernetes secrets and creates Jenkins credentials from them removing the need for manual entry of secrets, local storage and manual secret rotation.
+
+## Examples
+
+The Jenkins credentials plugin uses labels and annotations on a kubernetes secret to create a Jenkins credential.
+
+The different types of Jenkins credentials that can be created are SecretText, privateSSHKey, UsernamePassword.
+
+
+### SecretText
+
+Here are some examples of SecretText with the Hashicorp Vault and AWS External Secrets providers:
+
+
+#### Hashicorp Vault
+
+``` yaml
+{% include 'vault-jenkins-credential-sonarqube-api-token-external-secret.yaml' %}
+```
+
+#### AWS Secrets Manager
+
+``` yaml
+{% include 'aws-jenkins-credential-sonarqube-api-token-external-secret.yaml' %}
+```
+
+
+### UsernamePassword
+
+Here are some examples of UsernamePassword credentials with the Hashicorp Vault and AWS External Secrets providers:
+
+
+#### Hashicorp Vault
+
+``` yaml
+{% include 'vault-jenkins-credential-harbor-chart-robot-external-secret.yaml' %}
+```
+
+#### AWS Secrets Manager
+
+``` yaml
+{% include 'aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml' %}
+```
+
+
+
+### basicSSHUserPrivateKey
+
+Here are some examples of basicSSHUserPrivateKey credentials with the Hashicorp Vault and AWS External Secrets providers:
+
+
+#### Hashicorp Vault
+
+``` yaml
+{% include 'vault-jenkins-credential-github-ssh-access-external-secret.yaml' %}
+```
+
+#### AWS Secrets Manager
+
+``` yaml
+{% include 'aws-jenkins-credential-github-ssh-external-secret.yaml' %}
+```
+

+ 19 - 0
docs/guides-controller-class.md

@@ -0,0 +1,19 @@
+# Controller Classes
+
+> NOTE: this feature is experimental and not highly tested
+
+Controller classes are a proprierty set during the deployment that allows multiple controllers to work in a group of workloard. It works by separating which secretStores are going to be attributed to which controller. For the behavior of a single controller, no extra configuration is needed.
+
+## Setting up Controller Class
+
+In order to deploy the controller with a specific class, install the helm charts specifying the controller class, and create a `SecretStore` with the appropriate `spec.controller` values:
+```
+helm install custom-external-secrets external-secrets/external-secrets --set controllerClass=custom
+```
+``` yaml
+{% include 'controller-class-store.yaml' %}
+```
+
+Now, any `ExternalSecret` bound to this secret store will be evaluated by the operator with the controllerClass custom.
+
+> Note: Any SecretStore without `spec.controller` set will be considered as valid by any operator, regardless of their respective controllerClasses.

+ 50 - 0
docs/guides-templating-v1.md

@@ -0,0 +1,50 @@
+# Advanced Templating v1
+
+!!! warning
+
+    Templating Engine v1 is **deprecated** and will be removed in the future. Please migrate to engine v2 and take a look at our [upgrade guide](guides-templating.md#migrating-from-v1) for changes.
+
+
+With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`. Each data value is interpreted as a [golang template](https://golang.org/pkg/text/template/).
+
+## Examples
+
+You can use templates to inject your secrets into a configuration file that you mount into your pod:
+``` yaml
+{% include 'multiline-template-v1-external-secret.yaml' %}
+```
+
+You can also use pre-defined functions to extract data from your secrets. Here: extract key/cert from a pkcs12 archive and store it as PEM.
+``` yaml
+{% include 'pkcs12-template-v1-external-secret.yaml' %}
+```
+
+### TemplateFrom
+
+You do not have to define your templates inline in an ExternalSecret but you can pull `ConfigMaps` or other Secrets that contain a template. Consider the following example:
+
+``` yaml
+{% include 'template-v1-from-secret.yaml' %}
+```
+
+## Helper functions
+We provide a bunch of convenience functions that help you transform your secrets. A secret value is a `[]byte`.
+
+| Function       | Description                                                                | Input                            | Output        |
+| -------------- | -------------------------------------------------------------------------- | -------------------------------- | ------------- |
+| pkcs12key      | extracts the private key from a pkcs12 archive                             | `[]byte`                         | `[]byte`      |
+| pkcs12keyPass  | extracts the private key from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte`      |
+| pkcs12cert     | extracts the certificate from a pkcs12 archive                             | `[]byte`                         | `[]byte`      |
+| pkcs12certPass | extracts the certificate from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte`      |
+| pemPrivateKey  | PEM encodes the provided bytes as private key                              | `[]byte`                         | `string`      |
+| pemCertificate | PEM encodes the provided bytes as certificate                              | `[]byte`                         | `string`      |
+| jwkPublicKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PUBLIC KEY` that contains the public key ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey)) for details | `[]byte`                         | `string`      |
+| jwkPrivateKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey)) for details | `[]byte`                         | `string`      |
+| base64decode   | decodes the provided bytes as base64                                       | `[]byte`                         | `[]byte`      |
+| base64encode   | encodes the provided bytes as base64                                       | `[]byte`                         | `[]byte`      |
+| fromJSON       | parses the bytes as JSON so you can access individual properties           | `[]byte`                         | `interface{}` |
+| toJSON         | encodes the provided object as json string                                 | `interface{}`                    | `string`      |
+| toString       | converts bytes to string                                                   | `[]byte`                         | `string`      |
+| toBytes        | converts string to bytes                                                   | `string`                         | `[]byte`      |
+| upper          | converts all characters to their upper case                                | `string`                         | `string`      |
+| lower          | converts all character to their lower case                                 | `string`                         | `string`      |

+ 130 - 28
docs/guides-templating.md

@@ -1,43 +1,145 @@
+# Advanced Templating v2
+
 With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`. Each data value is interpreted as a [golang template](https://golang.org/pkg/text/template/).
 
 ## Examples
 
 You can use templates to inject your secrets into a configuration file that you mount into your pod:
-``` yaml
-{% include 'multiline-template-external-secret.yaml' %}
-```
 
-You can also use pre-defined functions to extract data from your secrets. Here: extract key/cert from a pkcs12 archive and store it as PEM.
-``` yaml
-{% include 'pkcs12-template-external-secret.yaml' %}
+```yaml
+{% include 'multiline-template-v2-external-secret.yaml' %}
 ```
 
 ### TemplateFrom
 
 You do not have to define your templates inline in an ExternalSecret but you can pull `ConfigMaps` or other Secrets that contain a template. Consider the following example:
 
-``` yaml
-{% include 'template-from-secret.yaml' %}
+```yaml
+{% include 'template-v2-from-secret.yaml' %}
+```
+
+### Extract Keys and Certificates from PKCS#12 Archive
+
+You can use pre-defined functions to extract data from your secrets. Here: extract keys and certificates from a PKCS#12 archive and store it as PEM.
+
+```yaml
+{% include 'pkcs12-template-v2-external-secret.yaml' %}
+```
+
+### Extract from JWK
+
+You can extract the public or private key parts of a JWK and use them as [PKCS#8](https://pkg.go.dev/crypto/x509#ParsePKCS8PrivateKey) private key or PEM-encoded [PKIX](https://pkg.go.dev/crypto/x509#MarshalPKIXPublicKey) public key.
+
+A JWK looks similar to this:
+
+```json
+{
+  "kty": "RSA",
+  "kid": "cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df",
+  "use": "sig",
+  "n": "pjdss...",
+  "e": "AQAB"
+  // ...
+}
+```
+
+And what you want may be a PEM-encoded public or private key portion of it. Take a look at this example on how to transform it into the desired format:
+
+```yaml
+{% include 'jwk-template-v2-external-secret.yaml' %}
+```
+
+### Filter PEM blocks
+
+Consider you have a secret that contains both a certificate and a private key encoded in PEM format and it is your goal to use only the certificate from that secret.
+
+```
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvxGZOW4IXvGlh
+ . . .
+m8JCpbJXDfSSVxKHgK1Siw4K6pnTsIA2e/Z+Ha2fvtocERjq7VQMAJFaIZSTKo9Q
+JwwY+vj0yxWjyzHUzZB33tg=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDMDCCAhigAwIBAgIQabPaXuZCQaCg+eQAVptGGDANBgkqhkiG9w0BAQsFADAV
+ . . .
+NtFUGA95RGN9s+pl6XY0YARPHf5O76ErC1OZtDTR5RdyQfcM+94gYZsexsXl0aQO
+9YD3Wg==
+-----END CERTIFICATE-----
+
+```
+
+You can achieve that by using the `filterPEM` function to extract a specific type of PEM block from that secret. If multiple blocks of that type (here: `CERTIFICATE`) exist then all of them are returned in the order they are specified.
+```yaml
+{% include 'pem-filter-template-v2-external-secret.yaml' %}
 ```
 
 ## Helper functions
-We provide a bunch of convenience functions that help you transform your secrets. A secret value is a `[]byte`.
-
-| Function       | Description                                                                | Input                            | Output        |
-| -------------- | -------------------------------------------------------------------------- | -------------------------------- | ------------- |
-| pkcs12key      | extracts the private key from a pkcs12 archive                             | `[]byte`                         | `[]byte`      |
-| pkcs12keyPass  | extracts the private key from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte`      |
-| pkcs12cert     | extracts the certificate from a pkcs12 archive                             | `[]byte`                         | `[]byte`      |
-| pkcs12certPass | extracts the certificate from a pkcs12 archive using the provided password | password `string`, data `[]byte` | `[]byte`      |
-| pemPrivateKey  | PEM encodes the provided bytes as private key                              | `[]byte`                         | `string`      |
-| pemCertificate | PEM encodes the provided bytes as certificate                              | `[]byte`                         | `string`      |
-| jwkPublicKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PUBLIC KEY` that contains the public key ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey)) for details | `[]byte`                         | `string`      |
-| jwkPrivateKeyPem | takes an json-serialized JWK as `[]byte` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format ([see here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey)) for details | `[]byte`                         | `string`      |
-| base64decode   | decodes the provided bytes as base64                                       | `[]byte`                         | `[]byte`      |
-| base64encode   | encodes the provided bytes as base64                                       | `[]byte`                         | `[]byte`      |
-| fromJSON       | parses the bytes as JSON so you can access individual properties           | `[]byte`                         | `interface{}` |
-| toJSON         | encodes the provided object as json string                                 | `interface{}`                    | `string`      |
-| toString       | converts bytes to string                                                   | `[]byte`                         | `string`      |
-| toBytes        | converts string to bytes                                                   | `string`                         | `[]byte`      |
-| upper          | converts all characters to their upper case                                | `string`                         | `string`      |
-| lower          | converts all character to their lower case                                 | `string`                         | `string`      |
+
+!!! info inline end
+
+    Note: we removed `env` and `expandenv` from sprig functions for security reasons.
+
+We provide a couple of convenience functions that help you transform your secrets. This is useful when dealing with PKCS#12 archives or JSON Web Keys (JWK).
+
+In addition to that you can use over 200+ [sprig functions](http://masterminds.github.io/sprig/). If you feel a function is missing or might be valuable feel free to open an issue and submit a [pull request](contributing-process.md#submitting-a-pull-request).
+
+<br/>
+
+| Function       | Description                                                                                                                                                                                               |
+| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| pkcs12key      | Extracts all private keys from a PKCS#12 archive and encodes them in **PKCS#8 PEM** format.                                                                                                               |
+| pkcs12keyPass  | Same as `pkcs12key`. Uses the provided password to decrypt the PKCS#12 archive.                                                                                                                           |
+| pkcs12cert     | Extracts all certificates from a PKCS#12 archive and orders them if possible. If disjunct or multiple leaf certs are provided they are returned as-is. <br/> Sort order: `leaf / intermediate(s) / root`. |
+| pkcs12certPass | Same as `pkcs12cert`. Uses the provided password to decrypt the PKCS#12 archive.                                                                                                                          |
+| filterPEM      | Filters PEM blocks with a specific type from a list of PEM blocks.                                                                                                                                        |
+
+| jwkPublicKeyPem | Takes an json-serialized JWK and returns an PEM block of type `PUBLIC KEY` that contains the public key. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey) for details. |
+| jwkPrivateKeyPem | Takes an json-serialized JWK as `string` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey) for details. |
+
+## Migrating from v1
+
+You have to opt-in to use the new engine version by specifying `template.engineVersion=v2`:
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: secret
+spec:
+  # ...
+  target:
+    template:
+      engineVersion: v2
+  # ...
+```
+
+The biggest change was that basically all function parameter types were changed from accepting/returning `[]byte` to `string`. This is relevant for you because now you don't need to specify `toString` all the time at the end of a template pipeline.
+
+```yaml
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+# ...
+spec:
+  target:
+    template:
+      engineVersion: v2
+      data:
+        # this used to be {{ .foobar | toString }}
+        egg: "new: {{ .foobar }}"
+{% endraw %}
+```
+
+##### Functions removed/replaced
+
+- `base64encode` was renamed to `b64enc`.
+- `base64decode` was renamed to `b64dec`. Any errors that occurr during decoding are silenced.
+- `fromJSON` was renamed to `fromJson`. Any errors that occurr during unmarshalling are silenced.
+- `toJSON` was renamed to `toJson`. Any errors that occurr during marshalling are silenced.
+- `pkcs12key` and `pkcs12keyPass` encode the PKCS#8 key directly into PEM format. There is no need to call `pemPrivateKey` anymore. Also, these functions do extract all private keys from the PKCS#12 archive not just the first one.
+- `pkcs12cert` and `pkcs12certPass` encode the certs directly into PEM format. There is no need to call `pemCertificate` anymore. These functions now **extract all certificates** from the PKCS#12 archive not just the first one.
+- `toString` implementation was replaced by the `sprig` implementation and should be api-compatible.
+- `toBytes` was removed.
+- `pemPrivateKey` was removed. It's now implemented within the `pkcs12*` functions.
+- `pemCertificate` was removed. It's now implemented within the `pkcs12*` functions.

+ 2 - 1
docs/provider-akeyless.md

@@ -40,7 +40,7 @@ Be sure the `akeyless` provider is listed in the `Kind=SecretStore` and the `ake
 ```yaml
 {% include 'akeyless-secret-store.yaml' %}
 ```
-
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `accessID`, `accessType` and `accessTypeParam` with the namespaces where the secrets reside.
 ### Creating external secret
 
 To get a secret from Akeyless and secret it on the Kubernetes cluster, a `Kind=ExternalSecret` is needed.
@@ -49,6 +49,7 @@ To get a secret from Akeyless and secret it on the Kubernetes cluster, a `Kind=E
 {% include 'akeyless-external-secret.yaml' %}
 ```
 
+
 #### Using DataFrom
 
 DataFrom can be used to get a secret as a JSON string and attempt to parse it.

+ 1 - 0
docs/provider-aws-parameter-store.md

@@ -11,6 +11,7 @@ way users of the `SecretStore` can only access the secrets necessary.
 ``` yaml
 {% include 'aws-parameter-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `accessKeyIDSecretRef` and `secretAccessKeySecretRef`  with the namespaces where the secrets reside.
 
 !!! warning "API Pricing & Throttling"
     The SSM Parameter Store API is charged by throughput and

+ 1 - 1
docs/provider-aws-secrets-manager.md

@@ -11,7 +11,7 @@ way users of the `SecretStore` can only access the secrets necessary.
 ``` yaml
 {% include 'aws-sm-store.yaml' %}
 ```
-
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `accessKeyIDSecretRef` and `secretAccessKeySecretRef`  with the namespaces where the secrets reside.
 ### IAM Policy
 
 Create a IAM Policy to pin down access to secrets matching `dev-*`.

+ 1 - 0
docs/provider-azure-key-vault.md

@@ -31,6 +31,7 @@ Be sure the `azurekv` provider is listed in the `Kind=SecretStore`
 ```yaml
 {% include 'azkv-secret-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `clientId` and `clientSecret`  with the namespaces where the secrets reside.
 
 Or in case of Managed Idenetity authentication:
 

+ 26 - 0
docs/provider-fake.md

@@ -0,0 +1,26 @@
+We provide a `fake` implementation to help with testing. This provider returns static key/value pairs and nothing else.
+To use the `fake` provider simply create a `SecretStore` or `ClusterSecretStore` and configure it like in the following example:
+
+!!! note inline end
+    The provider returns static data configured in `value` or `valueMap`. You can define a `version`, too. If set the `remoteRef` from an ExternalSecret must match otherwise no value is returned.
+
+```yaml
+{% include 'fake-provider-store.yaml' %}
+```
+
+Please note that `value` is intended for exclusive use with `data` and `valueMap` for `dataFrom`.
+Here is an example `ExternalSecret` that displays this behavior:
+
+!!! warning inline end
+    This provider supports specifying different `data[].version` configurations. However, `data[].property` is ignored.
+
+```yaml
+{% include 'fake-provider-es.yaml' %}
+```
+
+This results in the following secret:
+
+
+```yaml
+{% include 'fake-provider-secret.yaml' %}
+```

+ 1 - 0
docs/provider-gitlab-project-variables.md

@@ -27,6 +27,7 @@ Be sure the `gitlab` provider is listed in the `Kind=SecretStore` and the Projec
 ```yaml
 {% include 'gitlab-secret-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `accessToken` with the namespace where the secret resides.
 
 Your project ID can be found on your project's page.
 ![projectID](./pictures/screenshot_gitlab_projectID.png)

+ 5 - 26
docs/provider-google-secrets-manager.md

@@ -38,24 +38,7 @@ metadata:
 You can reference this particular ServiceAccount in a `SecretStore` or `ClusterSecretStore`. It's important that you also set the `projectID`, `clusterLocation` and `clusterName`. The Namespace on the `serviceAccountRef` is ignored when using a `SecretStore` resource. This is needed to isolate the namespaces properly.
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
-kind: ClusterSecretStore
-metadata:
-  name: gcp-wi
-spec:
-  provider:
-    gcpsm:
-      projectID: my-project
-      auth:
-        workloadIdentity:
-          # name of the cluster region
-          clusterLocation: europe-central2
-          # name of the GKE cluster
-          clusterName: example-workload-identity
-          # reference the sa from above
-          serviceAccountRef:
-            name: team-a
-            namespace: team-a
+{% include 'gcpsm-wi-secret-store.yaml' %}
 ```
 
 *You need to give the Google service account the `roles/iam.serviceAccountTokenCreator` role so it can generate a service account token for you (not necessary in the Pod-based Workload Identity bellow)*
@@ -90,14 +73,7 @@ The pod now has the identity. Now you need to configure the `SecretStore`.
 You just need to set the `projectID`, all other fields can be omitted.
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
-kind: SecretStore
-metadata:
-  name: example
-spec:
-  provider:
-    gcpsm:
-      projectID: pid
+{% include 'gcpsm-pod-wi-secret-store.yaml' %}
 ```
 
 ### GCP Service Account authentication
@@ -108,6 +84,7 @@ You can use [GCP Service Account](https://cloud.google.com/iam/docs/service-acco
 {% include 'gcpsm-credentials-secret.yaml' %}
 ```
 
+
 #### Update secret store
 Be sure the `gcpsm` provider is listed in the `Kind=SecretStore`
 
@@ -115,6 +92,8 @@ Be sure the `gcpsm` provider is listed in the `Kind=SecretStore`
 {% include 'gcpsm-secret-store.yaml' %}
 ```
 
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `SecretAccessKeyRef` with the namespace of the secret that we just created.
+
 #### Creating external secret
 
 To create a kubernetes secret from the GCP Secret Manager secret a `Kind=ExternalSecret` is needed.

+ 91 - 3
docs/provider-hashicorp-vault.md

@@ -26,7 +26,6 @@ spec:
         # https://www.vaultproject.io/docs/auth/token
         tokenSecretRef:
           name: "vault-token"
-          namespace: "default"
           key: "token"
 ---
 apiVersion: v1
@@ -36,6 +35,7 @@ metadata:
 data:
   token: cm9vdA== # "root"
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `tokenSecretRef` with the namespace of the secret that we just created.
 
 Then create a simple k/v pair at path `secret/foo`:
 
@@ -71,9 +71,92 @@ data:
   foobar: czNjcjN0
 ```
 
-#### Limitations
+#### Fetching Raw Values
 
-Vault supports only simple key/value pairs - nested objects are not supported. Hence specifying `gjson` properties like other providers support it is not supported.
+You can fetch all key/value pairs for a given path If you leave the `remoteRef.property` empty. This returns the json-encoded secret value for that path.
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: vault-example
+spec:
+  # ...
+  data:
+  - secretKey: foobar
+    remoteRef:
+      key: /dev/package.json
+```
+
+#### Nested Values
+
+Vault supports nested key/value pairs. You can specify a [gjson](https://github.com/tidwall/gjson) expression at `remoteRef.property` to get a nested value.
+
+Given the following secret - assume its path is `/dev/config`:
+```json
+{
+  "foo": {
+    "nested": {
+      "bar": "mysecret"
+    }
+  }
+}
+```
+
+You can set the `remoteRef.property` to point to the nested key using a [gjson](https://github.com/tidwall/gjson) expression.
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: vault-example
+spec:
+  # ...
+  data:
+  - secretKey: foobar
+    remoteRef:
+      key: /dev/config
+      property: foo.nested.bar
+---
+# creates a secret with:
+# foobar=mysecret
+```
+
+If you would set the `remoteRef.property` to just `foo` then you would get the json-encoded value of that property: `{"nested":{"bar":"mysecret"}}`.
+
+#### Multiple nested Values
+
+You can extract multiple keys from a nested secret using `dataFrom`.
+
+Given the following secret - assume its path is `/dev/config`:
+```json
+{
+  "foo": {
+    "nested": {
+      "bar": "mysecret",
+      "baz": "bang"
+    }
+  }
+}
+```
+
+You can set the `remoteRef.property` to point to the nested key using a [gjson](https://github.com/tidwall/gjson) expression.
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: vault-example
+spec:
+  # ...
+  dataFrom:
+  - key: /dev/config
+    property: foo.nested
+```
+
+That results in a secret with these values:
+```
+bar=mysecret
+baz=bang
+```
 
 ### Authentication
 
@@ -92,6 +175,7 @@ A static token is stored in a `Kind=Secret` and is used to authenticate with vau
 ```yaml
 {% include 'vault-token-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `tokenSecretRef` with the namespace where the secret resides.
 
 #### AppRole authentication example
 
@@ -101,6 +185,7 @@ A static token is stored in a `Kind=Secret` and is used to authenticate with vau
 ```yaml
 {% include 'vault-approle-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretRef` with the namespace where the secret resides.
 
 #### Kubernetes authentication
 
@@ -115,6 +200,7 @@ options of optaining credentials for vault:
 ```yaml
 {% include 'vault-kubernetes-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `serviceAccountRef` or in `secretRef`, if used.
 
 #### LDAP authentication
 
@@ -126,6 +212,7 @@ in a `Kind=Secret` referenced by the `secretRef`.
 ```yaml
 {% include 'vault-ldap-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretRef` with the namespace where the secret resides.
 
 #### JWT/OIDC authentication
 
@@ -137,6 +224,7 @@ or `Kind=ClusterSecretStore` resource.
 ```yaml
 {% include 'vault-jwt-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretRef` with the namespace where the secret resides.
 
 ### Vault Enterprise and Eventual Consistency
 

+ 1 - 0
docs/provider-ibm-secrets-manager.md

@@ -40,6 +40,7 @@ Be sure the `ibm` provider is listed in the `Kind=SecretStore`
 ```yaml
 {% include 'ibm-secret-store.yaml' %}
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `secretApiKeySecretRef` with the namespace where the secret resides.
 
 To find your serviceURL, under your Secrets Manager resource, go to "Endpoints" on the left.
 Note: Use the url without the `/api` suffix that is presented in the UI.

+ 5 - 1
docs/provider-oracle-vault.md

@@ -4,7 +4,10 @@ External Secrets Operator integrates with [OCI API](https://github.com/oracle/oc
 
 ### Authentication
 
-The API requires a userOCID, tenancyOCID, fingerprint, key file and a region. The fingerprint and key file should be supplied in the secret with the rest being provided in the secret store.
+If `auth` is not specified, the operator uses the instance principal.
+
+For using a specific user credentials, userOCID, tenancyOCID, fingerprint and private key are required.
+The fingerprint and key file should be supplied in the secret with the rest being provided in the secret store.
 
 See url for what region you you are accessing.
 ![userOCID-details](./pictures/screenshot_region.png)
@@ -38,6 +41,7 @@ Be sure the `oracle` provider is listed in the `Kind=SecretStore`.
 {% include 'oracle-secret-store.yaml' %}
 ```
 
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `privatekey` and `fingerprint` with the namespaces where the secrets reside.
 ### Creating external secret
 
 To create a kubernetes secret from the Oracle Cloud Interface secret a`Kind=ExternalSecret` is needed.

+ 4 - 2
docs/provider-webhook.md

@@ -38,6 +38,8 @@ data:
 
 NB: This is obviously not practical because it just returns the key as the result, but it shows how it works
 
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in all `secrets` references with the namespaces where the secrets reside.
+
 Now create an ExternalSecret that uses the above SecretStore:
 
 ```yaml
@@ -107,14 +109,14 @@ spec:
       # Use this name to refer to this secret in templating, above
       - name: <name>
         secretRef:
-          namespace: <namespace>
+          namespace: <namespace> # Only used in ClusterSecretStores
           name: <name>
       # Add CAs here for the TLS handshake
       caBundle: <base64 encoded cabundle>
       caProvider:
         type: Secret or COnfigMap
         name: <name of secret or configmap>
-        namespace: <namespace>
+        namespace: <namespace> # Only used in ClusterSecretStores
         key: <key inside secret>
 ```
 

+ 1 - 0
docs/provider-yandex-lockbox.md

@@ -39,6 +39,7 @@ spec:
           key: authorized-key
 ```
 
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in all `authorizedKeySecretRef` with the namespace where the secret resides.
 ### Creating external secret
 To make External Secrets Operator sync a k8s secret with a Lockbox secret:
 

+ 15 - 0
docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml

@@ -0,0 +1,15 @@
+---
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: anchore-access-credentials
+  namespace: ci
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: cluster-secrets-store
+    kind: ClusterSecretStore
+  target:
+    name: anchore-access-credentials
+  dataFrom:
+  - key: service/anchore-engine/engineAccess

+ 27 - 0
docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml

@@ -0,0 +1,27 @@
+---
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: github-ssh-access
+  namespace: ci
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: cluster-parameter-store
+    kind: ClusterSecretStore
+  target:
+    name: github-ssh-access
+    template:
+      metadata:
+        labels:
+          "jenkins.io/credentials-type": "basicSSHUserPrivateKey"
+        annotations:
+          "jenkins.io/credentials-description": "github-ssh-access key"
+  data:
+    - secretKey: username
+      remoteRef:
+        key: /service/github/sshUserPrivateKeyUserName
+    - secretKey: privateKey
+      remoteRef:
+        key: /service/github/sshUserPrivateKey
+

+ 23 - 0
docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml

@@ -0,0 +1,23 @@
+---
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: sonarqube-api-token
+  namespace: ci
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: cluster-secrets-store
+    kind: ClusterSecretStore
+  target:
+    name: sonarqube-api-token
+    template:
+      metadata:
+        labels:
+          "jenkins.io/credentials-type": "secretText"
+        annotations:
+          "jenkins.io/credentials-description": "Sonar API token"
+  data:
+    - secretKey: text
+      remoteRef:
+        key: service/sonarqube/apiToken

+ 28 - 0
docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml

@@ -0,0 +1,28 @@
+---
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: harbor-chart-robot
+  namespace: ci
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: cluster-secrets-store
+    kind: ClusterSecretStore
+  target:
+    name: harbor-chart-robot
+    template:
+      metadata:
+        labels:
+          "jenkins.io/credentials-type": "usernamePassword"
+        annotations:
+          "jenkins.io/credentials-description": "harbor chart robot access"
+  data:
+    - secretKey: password
+      remoteRef:
+        key: service/harbor/chartRobot
+        property: password
+    - secretKey: username
+      remoteRef:
+        key: service/harbor/chartRobot
+        property: username

+ 0 - 1
docs/snippets/aws-parameter-store.yaml

@@ -3,7 +3,6 @@ kind: SecretStore
 metadata:
   name: secretstore-sample
 spec:
-  controller: dev
   provider:
     aws:
       service: ParameterStore

+ 0 - 1
docs/snippets/aws-sm-store.yaml

@@ -3,7 +3,6 @@ kind: SecretStore
 metadata:
   name: secretstore-sample
 spec:
-  controller: dev
   provider:
     aws:
       service: SecretsManager

+ 0 - 4
docs/snippets/azkv-external-secret.yaml

@@ -35,7 +35,3 @@ spec:
     remoteRef:
       key: key/dev-key-test
 
-  # dataFrom , return ALL secrets saved in the referenced secretStore
-  # each secret name in the KV will be used as the secret key in the SECRET k8s target object
-  dataFrom:
-  - name: "*"

+ 1 - 1
docs/snippets/azkv-secret-store-mi.yaml

@@ -7,7 +7,7 @@ spec:
     # provider type: azure keyvault
     azurekv:
       authType: ManagedIdentity
-      # Optionally set the Id of the Managed Identity, if multiple identities is assignet to external-secrets operator
+      # Optionally set the Id of the Managed Identity, if multiple identities are assigned to external-secrets operator
       identityId: "<MI_clientId>"
       # URL of your vault instance, see: https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates
       vaultUrl: "https://my-keyvault-name.vault.azure.net"

+ 0 - 2
docs/snippets/basic-secret-store.yaml

@@ -3,11 +3,9 @@ kind: SecretStore
 metadata:
   name: secretstore-sample
 spec:
-  controller: dev
   provider:
     aws:
       service: SecretsManager
-      role: arn:aws:iam::123456789012:role/team-a-reader
       region: us-east-1
       auth:
         secretRef:

+ 17 - 0
docs/snippets/controller-class-store.yaml

@@ -0,0 +1,17 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: controller-custom-example
+spec:
+  #define the controller label to the matching value of the deployment
+  controller: custom
+  #configure provider the same way
+  provider:
+    vault:
+      server: "http://vault.default:8200"
+      path: "secret"
+      version: "v2"
+      auth:
+        kubernetes:
+          mountPath: "kubernetes"
+          role: "demo-role"

+ 18 - 0
docs/snippets/fake-provider-es.yaml

@@ -0,0 +1,18 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    name: fake
+    kind: ClusterSecretStore
+  target:
+    name: secret-to-be-created
+  data:
+  - secretKey: foo_bar
+    remoteRef:
+      key: /foo/bar
+      version: v1
+  dataFrom:
+  - key: /foo/baz

+ 9 - 0
docs/snippets/fake-provider-secret.yaml

@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: secret-to-be-created
+  namespace: default
+data:
+  foo_bar: SEVMTE8x # HELLO1  (via data)
+  foo: ZXhhbXBsZQ== # example (via dataFrom)
+  other: dGhpbmc=   # thing   (via dataFrom)

+ 20 - 0
docs/snippets/fake-provider-store.yaml

@@ -0,0 +1,20 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: ClusterSecretStore
+metadata:
+  name: fake
+spec:
+  provider:
+    fake:
+      data:
+      - key: "/foo/bar"
+        value: "HELLO1"
+        version: "v1"
+      - key: "/foo/bar"
+        value: "HELLO2"
+        version: "v2"
+      - key: "/foo/baz"
+        valueMap:
+          foo: example
+          other: thing
+
+

+ 8 - 0
docs/snippets/gcpsm-pod-wi-secret-store.yaml

@@ -0,0 +1,8 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: example
+spec:
+  provider:
+    gcpsm:
+      projectID: pid

+ 18 - 0
docs/snippets/gcpsm-wi-secret-store.yaml

@@ -0,0 +1,18 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: ClusterSecretStore
+metadata:
+  name: example
+spec:
+  provider:
+    gcpsm:
+      projectID: my-project
+      auth:
+        workloadIdentity:
+          # name of the cluster region
+          clusterLocation: europe-central2
+          # name of the GKE cluster
+          clusterName: example-workload-identity
+          # reference the sa from above
+          serviceAccountRef:
+            name: team-a
+            namespace: team-a

+ 25 - 0
docs/snippets/jwk-template-v2-external-secret.yaml

@@ -0,0 +1,25 @@
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: template
+spec:
+  # ...
+  target:
+    template:
+      engineVersion: v2
+      data:
+        # .myjwk is a json-encoded JWK string.
+        #
+        # this template will produce for jwk_pub a PEM encoded public key:
+        # -----BEGIN PUBLIC KEY-----
+        # MIIBI...
+        # ...
+        # ...AQAB
+        # -----END PUBLIC KEY-----
+        jwk_pub: "{{ .myjwk | jwkPublicKeyPem }}"
+        # private key is a pem-encoded PKCS#8 private key
+        jwk_priv: "{{ .myjwk | jwkPrivateKeyPem }}"
+
+
+{% endraw %}

+ 4 - 0
docs/snippets/multiline-template-external-secret.yaml → docs/snippets/multiline-template-v1-external-secret.yaml

@@ -10,6 +10,10 @@ spec:
     kind: SecretStore
   target:
     name: secret-to-be-created
+
+    # v1 is the default version
+    engineVersion: v1
+
     # this is how the Kind=Secret will look like
     template:
       type: kubernetes.io/tls

+ 32 - 0
docs/snippets/multiline-template-v2-external-secret.yaml

@@ -0,0 +1,32 @@
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: template
+spec:
+  # ...
+  target:
+    name: secret-to-be-created
+    # this is how the Kind=Secret will look like
+    template:
+      type: kubernetes.io/tls
+      engineVersion: v2
+      data:
+        # multiline string
+        config: |
+          datasources:
+          - name: Graphite
+            type: graphite
+            access: proxy
+            url: http://localhost:8080
+            password: "{{ .password }}"
+            user: "{{ .user }}"
+
+  data:
+  - secretKey: user
+    remoteRef:
+      key: /grafana/user
+  - secretKey: password
+    remoteRef:
+      key: /grafana/password
+{% endraw %}

+ 18 - 6
docs/snippets/oracle-secret-store.yaml

@@ -1,19 +1,31 @@
 apiVersion: external-secrets.io/v1alpha1
 kind: SecretStore
 metadata:
-  name: example
+  name: example-instance-principal
 spec:
   provider:
-    oracle: #Needs to match value in secretstore_types.go
+    oracle:
       vault: # The vault OCID
-      user:
-      tenancy:
-      region:
+      region: # The vault region
+
+---
+
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: example-auth
+spec:
+  provider:
+    oracle:
+      vault: # The vault OCID
+      region: # The vault region
       auth:
+        user: # A user OCID
+        tenancy: # A user's tenancy
         secretRef:
           privatekey:
             name: oracle-secret
-            key: privateKey #Needs to match stringData val in secret_oracle.yml
+            key: privateKey
           fingerprint:
             name: oracle-secret
             key: fingerprint

+ 0 - 0
docs/snippets/pkcs12-template-external-secret.yaml → docs/snippets/pkcs12-template-v1-external-secret.yaml


+ 19 - 0
docs/snippets/pkcs12-template-v2-external-secret.yaml

@@ -0,0 +1,19 @@
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: template
+spec:
+  # ...
+  target:
+    template:
+      type: kubernetes.io/tls
+      engineVersion: v2
+      data:
+        tls.crt: "{{ .mysecret | pkcs12cert }}"
+        tls.key: "{{ .mysecret | pkcs12key }}"
+
+        # if needed unlock the pkcs12 with the password
+        tls.crt: "{{ .mysecret | pkcs12certPass "my-password" }}"
+
+{% endraw %}

+ 2 - 0
docs/snippets/provider-aws-access.md

@@ -50,6 +50,7 @@ spec:
             name: awssm-secret
             key: secret-access-key
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `accessKeyIDSecretRef`, `secretAccessKeySecretRef`  with the namespaces where the secrets reside.
 
 ### EKS Service Account credentials
 
@@ -86,3 +87,4 @@ spec:
           serviceAccountRef:
             name: my-serviceaccount
 ```
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `serviceAccountRef` with the namespace where the service account resides.

+ 0 - 0
docs/snippets/template-from-secret.yaml → docs/snippets/template-v1-from-secret.yaml


+ 41 - 0
docs/snippets/template-v2-from-secret.yaml

@@ -0,0 +1,41 @@
+{% raw %}
+# define your template in a config map
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: grafana-config-tpl
+data:
+  config.yaml: |
+    datasources:
+      - name: Graphite
+        type: graphite
+        access: proxy
+        url: http://localhost:8080
+        password: "{{ .password }}"
+        user: "{{ .user }}"
+---
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: my-template-example
+spec:
+  # ...
+  target:
+    name: secret-to-be-created
+    template:
+      engineVersion: v2
+      templateFrom:
+      - configMap:
+          # name of the configmap to pull in
+          name: grafana-config-tpl
+          # here you define the keys that should be used as template
+          items:
+          - key: config.yaml
+  data:
+  - secretKey: user
+    remoteRef:
+      key: /grafana/user
+  - secretKey: password
+    remoteRef:
+      key: /grafana/password
+{% endraw %}

+ 55 - 0
docs/snippets/vault-anchore-engine-access-credentials-external-secret.yaml

@@ -0,0 +1,55 @@
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: anchore-access-credentials
+  namespace: security
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: vault-backend
+    kind: ClusterSecretStore
+  target:
+    name: anchore-access-credentials
+    template:
+
+      data:
+        ANCHORE_ADMIN_USERNAME: >-
+          {{ printf "{{ .username | toString }}" }}
+        ANCHORE_ADMIN_PASSWORD: >-
+          {{ printf "{{ .password | toString }}" }}
+        ANCHORE_DB_PASSWORD: >-
+          {{ printf "{{ .dbPassword | toString }}" }}
+        db-url: >-
+          {{ printf "{{ .dbUrl | toString }}" }}
+        db-user: >-
+          {{ printf "{{ .dbUser | toString }}" }}
+        postgres-password: >-
+          {{ printf "{{ .postgresPassword | toString }}" }}
+
+  data:
+    - secretKey: password
+      remoteRef:
+        key: anchore-engine
+        property: ANCHORE_ADMIN_PASSWORD
+    - secretKey: username
+      remoteRef:
+        key: anchore-engine
+        property: ANCHORE_ADMIN_USERNAME
+    - secretKey: dbPassword
+      remoteRef:
+        key: anchore-engine
+        property: ANCHORE_DB_PASSWORD
+    - secretKey: dbUrl
+      remoteRef:
+        key: anchore-engine
+        property: db-url
+    - secretKey: dbUser
+      remoteRef:
+        key: anchore-engine
+        property: db-user
+    - secretKey: postgresPassword
+      remoteRef:
+        key: anchore-engine
+        property: postgres-password
+{% endraw %}

+ 0 - 1
docs/snippets/vault-approle-store.yaml

@@ -21,5 +21,4 @@ spec:
           # Reference to a key in a K8 Secret that contains the App Role SecretId
           secretRef:
             name: "my-secret"
-            namespace: "secret-admin"
             key: "secret-id"

+ 34 - 0
docs/snippets/vault-jenkins-credential-github-ssh-access-external-secret.yaml

@@ -0,0 +1,34 @@
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: github-ssh-access
+  namespace: ci
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: vault-backend
+    kind: ClusterSecretStore
+  target:
+    name: github-ssh-access
+    template:
+      metadata:
+        labels:
+          "jenkins.io/credentials-type": "basicSSHUserPrivateKey"
+        annotations:
+          "jenkins.io/credentials-description": "github-ssh-access key"
+      data:
+        username: >-
+          {{ printf "{{ .username | toString }}" }}
+        privateKey: >-
+          {{ printf "{{ .privateKey | toString }}" }}
+  data:
+    - secretKey: username
+      remoteRef:
+        key: my-kv
+        property: github-ssh-access-username
+    - secretKey: privateKey
+      remoteRef:
+        key: my-kv
+        property: github-ssh-access-private-key
+{% endraw %}

+ 34 - 0
docs/snippets/vault-jenkins-credential-harbor-chart-robot-external-secret.yaml

@@ -0,0 +1,34 @@
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: harbor-chart-robot
+  namespace: ci
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: vault-backend
+    kind: ClusterSecretStore
+  target:
+    name: harbor-chart-robot
+    template:
+      metadata:
+        labels:
+          "jenkins.io/credentials-type": "usernamePassword"
+        annotations:
+          "jenkins.io/credentials-description": "harbor chart robot"
+      data:
+        username: >-
+          {{ printf "{{ .username | toString }}" }}
+        password: >-
+          {{ printf "{{ .password | toString }}" }}
+  data:
+    - secretKey: username
+      remoteRef:
+        key: my-kv
+        property: harbor-chart-robot-username
+    - secretKey: password
+      remoteRef:
+        key: my-kv
+        property: harbor-chart-robot-token
+{% endraw %}

+ 28 - 0
docs/snippets/vault-jenkins-credential-sonarqube-api-token-external-secret.yaml

@@ -0,0 +1,28 @@
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: sonarqube-api-token
+  namespace: ci
+spec:
+  refreshInterval: 1m
+  secretStoreRef:
+    name: vault-backend
+    kind: ClusterSecretStore
+  target:
+    name: sonarqube-api-token
+    template:
+      metadata:
+        labels:
+          "jenkins.io/credentials-type": "secretText"
+        annotations:
+          "jenkins.io/credentials-description": "sonarqube api token"
+      data:
+        text: >-
+          {{ printf "{{ .password | toString }}" }}
+  data:
+    - secretKey: password
+      remoteRef:
+        key: jenkins-credentials
+        property: sonarqube-api-token
+{% endraw %}

+ 0 - 1
docs/snippets/vault-jwt-store.yaml

@@ -19,5 +19,4 @@ spec:
           role: "vault-jwt-role"
           secretRef:
             name: "my-secret"
-            namespace: "secret-admin"
             key: "jwt-token"

+ 0 - 2
docs/snippets/vault-kubernetes-store.yaml

@@ -22,10 +22,8 @@ spec:
           # of a kubernetes ServiceAccount
           serviceAccountRef:
             name: "my-sa"
-            namespace: "secret-admin"
           # Optional secret field containing a Kubernetes ServiceAccount JWT
           #  used for authenticating with Vault
           secretRef:
             name: "my-secret"
-            namespace: "secret-admin"
             key: "vault"

+ 0 - 1
docs/snippets/vault-ldap-store.yaml

@@ -19,5 +19,4 @@ spec:
           username: "username"
           secretRef:
             name: "my-secret"
-            namespace: "secret-admin"
             key: "ldap-password"

+ 0 - 1
docs/snippets/vault-token-store.yaml

@@ -14,5 +14,4 @@ spec:
         # https://www.vaultproject.io/docs/auth/token
         tokenSecretRef:
           name: "my-secret"
-          namespace: "secret-admin"
           key: "vault-token"

+ 139 - 0
docs/spec.md

@@ -1408,6 +1408,21 @@ Kubernetes core/v1.SecretType
 </tr>
 <tr>
 <td>
+<code>engineVersion</code></br>
+<em>
+<a href="#external-secrets.io/v1alpha1.TemplateEngineVersion">
+TemplateEngineVersion
+</a>
+</em>
+</td>
+<td>
+<p>EngineVersion specifies the template engine version
+that should be used to compile/execute the
+template specified in .data and .templateFrom[].</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>metadata</code></br>
 <em>
 <a href="#external-secrets.io/v1alpha1.ExternalSecretTemplateMetadata">
@@ -1486,6 +1501,95 @@ map[string]string
 </tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1alpha1.FakeProvider">FakeProvider
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1alpha1.SecretStoreProvider">SecretStoreProvider</a>)
+</p>
+<p>
+<p>FakeProvider configures a fake provider that returns static values.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>data</code></br>
+<em>
+<a href="#external-secrets.io/v1alpha1.FakeProviderData">
+[]FakeProviderData
+</a>
+</em>
+</td>
+<td>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1alpha1.FakeProviderData">FakeProviderData
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1alpha1.FakeProvider">FakeProvider</a>)
+</p>
+<p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>key</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+</td>
+</tr>
+<tr>
+<td>
+<code>value</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+</td>
+</tr>
+<tr>
+<td>
+<code>valueMap</code></br>
+<em>
+map[string]string
+</em>
+</td>
+<td>
+</td>
+</tr>
+<tr>
+<td>
+<code>version</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="external-secrets.io/v1alpha1.GCPSMAuth">GCPSMAuth
 </h3>
 <p>
@@ -2310,6 +2414,20 @@ WebhookProvider
 <p>Webhook configures this store to sync secrets using a generic templated webhook</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>fake</code></br>
+<em>
+<a href="#external-secrets.io/v1alpha1.FakeProvider">
+FakeProvider
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Fake configures a store with static key/value pairs</p>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="external-secrets.io/v1alpha1.SecretStoreRef">SecretStoreRef
@@ -2563,6 +2681,27 @@ Kubernetes meta/v1.Time
 </tr>
 </tbody>
 </table>
+<h3 id="external-secrets.io/v1alpha1.TemplateEngineVersion">TemplateEngineVersion
+(<code>string</code> alias)</p></h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1alpha1.ExternalSecretTemplate">ExternalSecretTemplate</a>)
+</p>
+<p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Value</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody><tr><td><p>&#34;v1&#34;</p></td>
+<td></td>
+</tr><tr><td><p>&#34;v2&#34;</p></td>
+<td></td>
+</tr></tbody>
+</table>
 <h3 id="external-secrets.io/v1alpha1.TemplateFrom">TemplateFrom
 </h3>
 <p>

+ 1 - 1
e2e/Makefile

@@ -2,7 +2,7 @@ MAKEFLAGS   += --warn-undefined-variables
 SHELL       := /bin/bash
 .SHELLFLAGS := -euo pipefail -c
 
-KIND_IMG       = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6"
+KIND_IMG       = "kindest/node:v1.23.3@sha256:0df8215895129c0d3221cda19847d1296c4f29ec93487339149333bd9d899e5a"
 BUILD_ARGS     ?=
 
 export E2E_IMAGE_REGISTRY ?= ghcr.io/external-secrets/external-secrets-e2e

+ 4 - 2
e2e/framework/addon/chart.go

@@ -62,8 +62,10 @@ func (c *HelmChart) Install() error {
 	}
 
 	args := []string{"install", c.ReleaseName, c.Chart,
+		"--debug",
 		"--wait",
-		"--timeout", "120s",
+		"--timeout", "600s",
+		"-o", "yaml",
 		"--namespace", c.Namespace,
 	}
 
@@ -80,7 +82,7 @@ func (c *HelmChart) Install() error {
 	}
 
 	var sout, serr bytes.Buffer
-	log.Logf("installing chart %s", c.ReleaseName)
+	log.Logf("installing chart with args: %+q", args)
 	cmd := exec.Command("helm", args...)
 	cmd.Stdout = &sout
 	cmd.Stderr = &serr

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

@@ -86,7 +86,7 @@ func WithServiceAccount(saName string) MutationFunc {
 			},
 			{
 				Key:   "serviceAccount.name",
-				Value: "eso-e2e-test",
+				Value: saName,
 			},
 		}...)
 	}

+ 1 - 3
e2e/framework/eso.go

@@ -26,20 +26,18 @@ import (
 	"k8s.io/apimachinery/pkg/util/wait"
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
-	"github.com/external-secrets/external-secrets/e2e/framework/log"
 )
 
 // WaitForSecretValue waits until a secret comes into existence and compares the secret.Data
 // 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*5, time.Minute, func() (bool, error) {
+	err := wait.PollImmediate(time.Second*10, time.Minute, func() (bool, error) {
 		err := f.CRClient.Get(context.Background(), types.NamespacedName{
 			Namespace: namespace,
 			Name:      name,
 		}, secret)
 		if apierrors.IsNotFound(err) {
-			log.Logf("Secret Not Found. Expected: %+v, Got: %+v", expected, secret)
 			return false, nil
 		}
 		return equalSecrets(expected, secret), nil

+ 7 - 2
e2e/framework/framework.go

@@ -79,6 +79,10 @@ func (f *Framework) BeforeEach() {
 // AfterEach deletes the namespace and cleans up the registered addons.
 func (f *Framework) AfterEach() {
 	for _, a := range f.Addons {
+		if CurrentSpecReport().Failed() {
+			err := a.Logs()
+			Expect(err).ToNot(HaveOccurred())
+		}
 		err := a.Uninstall()
 		Expect(err).ToNot(HaveOccurred())
 	}
@@ -106,8 +110,9 @@ func (f *Framework) Install(a addon.Addon) {
 
 // Compose helps define multiple testcases with same/different auth methods.
 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)
+	// prepend common fn to tweaks
+	desc, cfn := fn(f)
+	tweaks = append([]func(*TestCase){cfn}, tweaks...)
 
 	// need to convert []func to []interface{}
 	ifs := make([]interface{}, len(tweaks))

+ 1 - 0
e2e/run.sh

@@ -52,6 +52,7 @@ kubectl run --rm \
   --env="GCP_SM_SA_JSON=${GCP_SM_SA_JSON:-}" \
   --env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \
   --env="GCP_GSA_NAME=${GCP_GSA_NAME:-}" \
+  --env="GCP_KSA_NAME=${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:-}" \

+ 97 - 0
e2e/suite/aws/common.go

@@ -0,0 +1,97 @@
+/*
+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 common
+
+import (
+	"context"
+
+	// nolint
+	. "github.com/onsi/gomega"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+const (
+	WithReferencedIRSA          = "with referenced IRSA"
+	WithMountedIRSA             = "with mounted IRSA"
+	StaticCredentialsSecretName = "provider-secret"
+)
+
+func ReferencedIRSAStoreName(f *framework.Framework) string {
+	return "irsa-ref-" + f.Namespace.Name
+}
+
+func MountedIRSAStoreName(f *framework.Framework) string {
+	return "irsa-mounted-" + f.Namespace.Name
+}
+
+func UseClusterSecretStore(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.ClusterSecretStoreKind
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = ReferencedIRSAStoreName(tc.Framework)
+}
+
+func UseMountedIRSAStore(tc *framework.TestCase) {
+	tc.ExternalSecret.Spec.SecretStoreRef.Kind = esv1alpha1.SecretStoreKind
+	tc.ExternalSecret.Spec.SecretStoreRef.Name = MountedIRSAStoreName(tc.Framework)
+}
+
+// StaticStore is namespaced and references
+// static credentials from a secret.
+func SetupStaticStore(f *framework.Framework, kid, sak, region string, serviceType esv1alpha1.AWSServiceType) {
+	awsCreds := &corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      StaticCredentialsSecretName,
+			Namespace: f.Namespace.Name,
+		},
+		StringData: map[string]string{
+			"kid": kid,
+			"sak": sak,
+		},
+	}
+	err := f.CRClient.Create(context.Background(), awsCreds)
+	Expect(err).ToNot(HaveOccurred())
+
+	secretStore := &esv1alpha1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      f.Namespace.Name,
+			Namespace: f.Namespace.Name,
+		},
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				AWS: &esv1alpha1.AWSProvider{
+					Service: serviceType,
+					Region:  region,
+					Auth: esv1alpha1.AWSAuth{
+						SecretRef: &esv1alpha1.AWSAuthSecretRef{
+							AccessKeyID: esmetav1.SecretKeySelector{
+								Name: StaticCredentialsSecretName,
+								Key:  "kid",
+							},
+							SecretAccessKey: esmetav1.SecretKeySelector{
+								Name: StaticCredentialsSecretName,
+								Key:  "sak",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	err = f.CRClient.Create(context.Background(), secretStore)
+	Expect(err).ToNot(HaveOccurred())
+}

+ 45 - 0
e2e/suite/aws/parameterstore/parameterstore.go

@@ -0,0 +1,45 @@
+/*
+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"
+
+	"github.com/external-secrets/external-secrets/e2e/framework"
+	"github.com/external-secrets/external-secrets/e2e/suite/common"
+)
+
+var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
+	f := framework.New("eso-aws-ps")
+	prov := NewFromEnv(f)
+
+	DescribeTable("sync secrets",
+		framework.TableFunc(f,
+			prov),
+		Entry(common.SimpleDataSync(f)),
+		Entry(common.NestedJSONWithGJSON(f)),
+		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataWithProperty(f)),
+		Entry(common.JSONDataWithTemplate(f)),
+		Entry(common.DockerJSONConfig(f)),
+		Entry(common.DataPropertyDockerconfigJSON(f)),
+		Entry(common.SSHKeySync(f)),
+		Entry(common.SSHKeySyncDataProperty(f)),
+		Entry(common.SyncWithoutTargetName(f)),
+		Entry(common.JSONDataWithoutTargetName(f)),
+	)
+})

+ 85 - 0
e2e/suite/aws/parameterstore/parameterstore_managed.go

@@ -0,0 +1,85 @@
+/*
+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"
+
+	"github.com/external-secrets/external-secrets/e2e/framework"
+	"github.com/external-secrets/external-secrets/e2e/framework/addon"
+	awscommon "github.com/external-secrets/external-secrets/e2e/suite/aws"
+	"github.com/external-secrets/external-secrets/e2e/suite/common"
+)
+
+// 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", "parameterstore", "managed"), func() {
+	f := framework.New("eso-aws-ps-managed")
+	prov := NewFromEnv(f)
+
+	// nolint
+	DescribeTable("sync secrets",
+		framework.TableFunc(f,
+			prov),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SimpleDataSync, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataFromSync, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithProperty, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithTemplate, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.DockerJSONConfig, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySync, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SyncWithoutTargetName, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseClusterSecretStore),
+	)
+})
+
+// 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", "parameterstore", "managed"), func() {
+	f := framework.New("eso-aws-ps-irsa-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"),
+		))
+	})
+
+	// nolint
+	DescribeTable("sync secrets",
+		framework.TableFunc(f,
+			prov),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SimpleDataSync, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataFromSync, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithProperty, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithTemplate, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.DockerJSONConfig, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySync, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SyncWithoutTargetName, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseMountedIRSAStore),
+	)
+})

+ 165 - 0
e2e/suite/aws/parameterstore/provider.go

@@ -0,0 +1,165 @@
+/*
+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 (
+	"context"
+	"os"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/credentials"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/ssm"
+
+	//nolint
+	. "github.com/onsi/ginkgo/v2"
+
+	// nolint
+	. "github.com/onsi/gomega"
+	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"
+	esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+	"github.com/external-secrets/external-secrets/e2e/framework/log"
+	common "github.com/external-secrets/external-secrets/e2e/suite/aws"
+)
+
+type Provider struct {
+	ServiceAccountName      string
+	ServiceAccountNamespace string
+
+	region    string
+	client    *ssm.SSM
+	framework *framework.Framework
+}
+
+func NewProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *Provider {
+	sess, err := session.NewSessionWithOptions(session.Options{
+		Config: aws.Config{
+			Credentials: credentials.NewStaticCredentials(kid, sak, ""),
+			Region:      aws.String(region),
+		},
+	})
+	if err != nil {
+		Fail(err.Error())
+	}
+	sm := ssm.New(sess)
+	prov := &Provider{
+		ServiceAccountName:      saName,
+		ServiceAccountNamespace: saNamespace,
+		region:                  region,
+		client:                  sm,
+		framework:               f,
+	}
+
+	BeforeEach(func() {
+		common.SetupStaticStore(f, kid, sak, region, esv1alpha1.AWSServiceParameterStore)
+		prov.SetupReferencedIRSAStore()
+		prov.SetupMountedIRSAStore()
+	})
+
+	AfterEach(func() {
+		// Cleanup ClusterSecretStore
+		err := prov.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: common.ReferencedIRSAStoreName(f),
+			},
+		})
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	return prov
+}
+
+func NewFromEnv(f *framework.Framework) *Provider {
+	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 NewProvider(f, kid, sak, region, saName, saNamespace)
+}
+
+// CreateSecret creates a secret at the provider.
+func (s *Provider) CreateSecret(key, val string) {
+	_, err := s.client.PutParameter(&ssm.PutParameterInput{
+		Name:     aws.String(key),
+		Value:    aws.String(val),
+		DataType: aws.String("text"),
+		Type:     aws.String("String"),
+	})
+	Expect(err).ToNot(HaveOccurred())
+}
+
+// DeleteSecret deletes a secret at the provider.
+func (s *Provider) DeleteSecret(key string) {
+	_, err := s.client.DeleteParameter(&ssm.DeleteParameterInput{
+		Name: aws.String(key),
+	})
+	Expect(err).ToNot(HaveOccurred())
+}
+
+// MountedIRSAStore is a SecretStore without auth config
+// ESO relies on the pod-mounted ServiceAccount when using this store.
+func (s *Provider) SetupMountedIRSAStore() {
+	secretStore := &esv1alpha1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      common.MountedIRSAStoreName(s.framework),
+			Namespace: s.framework.Namespace.Name,
+		},
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				AWS: &esv1alpha1.AWSProvider{
+					Service: esv1alpha1.AWSServiceParameterStore,
+					Region:  s.region,
+					Auth:    esv1alpha1.AWSAuth{},
+				},
+			},
+		},
+	}
+	err := s.framework.CRClient.Create(context.Background(), secretStore)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+// ReferncedIRSAStore is a ClusterStore
+// that references a (IRSA-) ServiceAccount in the default namespace.
+func (s *Provider) SetupReferencedIRSAStore() {
+	log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
+	secretStore := &esv1alpha1.ClusterSecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: common.ReferencedIRSAStoreName(s.framework),
+		},
+	}
+	_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
+		secretStore.Spec.Provider = &esv1alpha1.SecretStoreProvider{
+			AWS: &esv1alpha1.AWSProvider{
+				Service: esv1alpha1.AWSServiceParameterStore,
+				Region:  s.region,
+				Auth: esv1alpha1.AWSAuth{
+					JWTAuth: &esv1alpha1.AWSJWTAuth{
+						ServiceAccountRef: &esmetav1.ServiceAccountSelector{
+							Name:      s.ServiceAccountName,
+							Namespace: &s.ServiceAccountNamespace,
+						},
+					},
+				},
+			},
+		}
+		return nil
+	})
+	Expect(err).ToNot(HaveOccurred())
+}

+ 14 - 76
e2e/suite/aws/provider.go → e2e/suite/aws/secretsmanager/provider.go

@@ -29,7 +29,6 @@ import (
 
 	// 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"
 
@@ -37,24 +36,19 @@ import (
 	esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/e2e/framework"
 	"github.com/external-secrets/external-secrets/e2e/framework/log"
+	common "github.com/external-secrets/external-secrets/e2e/suite/aws"
 )
 
-type SMProvider struct {
+type Provider struct {
 	ServiceAccountName      string
 	ServiceAccountNamespace string
 
-	kid       string
-	sak       string
 	region    string
 	client    *secretsmanager.SecretsManager
 	framework *framework.Framework
 }
 
-const (
-	staticCredentialsSecretName = "provider-secret"
-)
-
-func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *SMProvider {
+func NewProvider(f *framework.Framework, kid, sak, region, saName, saNamespace string) *Provider {
 	sess, err := session.NewSessionWithOptions(session.Options{
 		Config: aws.Config{
 			Credentials: credentials.NewStaticCredentials(kid, sak, ""),
@@ -65,18 +59,16 @@ func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace
 		Fail(err.Error())
 	}
 	sm := secretsmanager.New(sess)
-	prov := &SMProvider{
+	prov := &Provider{
 		ServiceAccountName:      saName,
 		ServiceAccountNamespace: saNamespace,
-		kid:                     kid,
-		sak:                     sak,
 		region:                  region,
 		client:                  sm,
 		framework:               f,
 	}
 
 	BeforeEach(func() {
-		prov.SetupStaticStore()
+		common.SetupStaticStore(f, kid, sak, region, esv1alpha1.AWSServiceSecretsManager)
 		prov.SetupReferencedIRSAStore()
 		prov.SetupMountedIRSAStore()
 	})
@@ -85,7 +77,7 @@ func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace
 		// Cleanup ClusterSecretStore
 		err := prov.framework.CRClient.Delete(context.Background(), &esv1alpha1.ClusterSecretStore{
 			ObjectMeta: metav1.ObjectMeta{
-				Name: prov.ReferencedIRSAStoreName(),
+				Name: common.ReferencedIRSAStoreName(f),
 			},
 		})
 		Expect(err).ToNot(HaveOccurred())
@@ -94,17 +86,17 @@ func NewSMProvider(f *framework.Framework, kid, sak, region, saName, saNamespace
 	return prov
 }
 
-func NewFromEnv(f *framework.Framework) *SMProvider {
+func NewFromEnv(f *framework.Framework) *Provider {
 	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)
+	return NewProvider(f, kid, sak, region, saName, saNamespace)
 }
 
 // CreateSecret creates a secret at the provider.
-func (s *SMProvider) CreateSecret(key, val string) {
+func (s *Provider) CreateSecret(key, val string) {
 	// 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
@@ -129,7 +121,7 @@ func (s *SMProvider) CreateSecret(key, val string) {
 // DeleteSecret deletes a secret at the provider.
 // There may be a short delay between calling this function
 // and the removal of the secret on the provider side.
-func (s *SMProvider) DeleteSecret(key string) {
+func (s *Provider) DeleteSecret(key string) {
 	log.Logf("deleting secret %s", key)
 	_, err := s.client.DeleteSecret(&secretsmanager.DeleteSecretInput{
 		SecretId:                   aws.String(key),
@@ -140,10 +132,10 @@ func (s *SMProvider) DeleteSecret(key string) {
 
 // MountedIRSAStore is a SecretStore without auth config
 // ESO relies on the pod-mounted ServiceAccount when using this store.
-func (s *SMProvider) SetupMountedIRSAStore() {
+func (s *Provider) SetupMountedIRSAStore() {
 	secretStore := &esv1alpha1.SecretStore{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      s.MountedIRSAStoreName(),
+			Name:      common.MountedIRSAStoreName(s.framework),
 			Namespace: s.framework.Namespace.Name,
 		},
 		Spec: esv1alpha1.SecretStoreSpec{
@@ -160,17 +152,13 @@ func (s *SMProvider) SetupMountedIRSAStore() {
 	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() {
+func (s *Provider) SetupReferencedIRSAStore() {
 	log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
 	secretStore := &esv1alpha1.ClusterSecretStore{
 		ObjectMeta: metav1.ObjectMeta{
-			Name: s.ReferencedIRSAStoreName(),
+			Name: common.ReferencedIRSAStoreName(s.framework),
 		},
 	}
 	_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
@@ -192,53 +180,3 @@ func (s *SMProvider) SetupReferencedIRSAStore() {
 	})
 	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:      staticCredentialsSecretName,
-			Namespace: s.framework.Namespace.Name,
-		},
-		StringData: map[string]string{
-			"kid": s.kid,
-			"sak": s.sak,
-		},
-	}
-	err := s.framework.CRClient.Create(context.Background(), awsCreds)
-	Expect(err).ToNot(HaveOccurred())
-
-	secretStore := &esv1alpha1.SecretStore{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      s.framework.Namespace.Name,
-			Namespace: s.framework.Namespace.Name,
-		},
-		Spec: esv1alpha1.SecretStoreSpec{
-			Provider: &esv1alpha1.SecretStoreProvider{
-				AWS: &esv1alpha1.AWSProvider{
-					Service: esv1alpha1.AWSServiceSecretsManager,
-					Region:  s.region,
-					Auth: esv1alpha1.AWSAuth{
-						SecretRef: &esv1alpha1.AWSAuthSecretRef{
-							AccessKeyID: esmetav1.SecretKeySelector{
-								Name: staticCredentialsSecretName,
-								Key:  "kid",
-							},
-							SecretAccessKey: esmetav1.SecretKeySelector{
-								Name: staticCredentialsSecretName,
-								Key:  "sak",
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-	err = s.framework.CRClient.Create(context.Background(), secretStore)
-	Expect(err).ToNot(HaveOccurred())
-}

+ 0 - 0
e2e/suite/aws/secretsmanager.go → e2e/suite/aws/secretsmanager/secretsmanager.go


+ 85 - 0
e2e/suite/aws/secretsmanager/secretsmanager_managed.go

@@ -0,0 +1,85 @@
+/*
+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"
+
+	"github.com/external-secrets/external-secrets/e2e/framework"
+	"github.com/external-secrets/external-secrets/e2e/framework/addon"
+	awscommon "github.com/external-secrets/external-secrets/e2e/suite/aws"
+	"github.com/external-secrets/external-secrets/e2e/suite/common"
+)
+
+// 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)
+
+	// nolint
+	DescribeTable("sync secretsmanager secrets",
+		framework.TableFunc(f,
+			prov),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SimpleDataSync, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataFromSync, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithProperty, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithTemplate, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.DockerJSONConfig, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySync, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.SyncWithoutTargetName, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseClusterSecretStore),
+	)
+})
+
+// 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"),
+		))
+	})
+
+	// nolint
+	DescribeTable("sync secretsmanager secrets",
+		framework.TableFunc(f,
+			prov),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SimpleDataSync, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.NestedJSONWithGJSON, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataFromSync, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithProperty, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithTemplate, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.DockerJSONConfig, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.DataPropertyDockerconfigJSON, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySync, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SSHKeySyncDataProperty, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.SyncWithoutTargetName, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.JSONDataWithoutTargetName, awscommon.UseMountedIRSAStore),
+	)
+})

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

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

+ 66 - 0
e2e/suite/azure/azure_cert.go

@@ -0,0 +1,66 @@
+/*
+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 azure
+
+import (
+	"fmt"
+
+	// nolint
+	. "github.com/onsi/ginkgo/v2"
+	v1 "k8s.io/api/core/v1"
+
+	// nolint
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+// azure keyvault type=cert should get a certificate from the api.
+var _ = Describe("[azure]", Label("azure", "keyvault", "cert"), func() {
+	f := framework.New("eso-azure-certtype")
+	prov := newFromEnv(f)
+	var certBytes []byte
+	var certName string
+
+	BeforeEach(func() {
+		certName = fmt.Sprintf("%s-%s", f.Namespace.Name, "certtest")
+		prov.CreateCertificate(certName)
+		certBytes = prov.GetCertificate(certName)
+	})
+
+	AfterEach(func() {
+		prov.DeleteCertificate(certName)
+	})
+
+	ff := framework.TableFunc(f, prov)
+	It("should sync keyvault objects with type=cert", func() {
+		ff(func(tc *framework.TestCase) {
+			secretKey := "azkv-cert"
+
+			tc.ExpectedSecret = &v1.Secret{
+				Type: v1.SecretTypeOpaque,
+				Data: map[string][]byte{
+					secretKey: certBytes,
+				},
+			}
+			tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
+				{
+					SecretKey: secretKey,
+					RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+						Key: "cert/" + certName,
+					},
+				},
+			}
+		})
+	})
+
+})

+ 69 - 0
e2e/suite/azure/azure_key.go

@@ -0,0 +1,69 @@
+/*
+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 azure
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
+
+	// nolint
+	. "github.com/onsi/ginkgo/v2"
+	v1 "k8s.io/api/core/v1"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+// azure keyvault type=key should retrieve a jwk from the api.
+var _ = Describe("[azure]", Label("azure", "keyvault", "key"), func() {
+	f := framework.New("eso-azure-keytype")
+	prov := newFromEnv(f)
+	var jwk *keyvault.JSONWebKey
+	var keyName string
+
+	BeforeEach(func() {
+		keyName = fmt.Sprintf("%s-%s", f.Namespace.Name, "keytest")
+		jwk = prov.CreateKey(keyName)
+	})
+
+	AfterEach(func() {
+		prov.DeleteKey(keyName)
+	})
+
+	ff := framework.TableFunc(f, prov)
+
+	It("should sync keyvault objects with type=key", func() {
+		ff(func(tc *framework.TestCase) {
+			secretKey := "azkv-key"
+			keyBytes, _ := json.Marshal(jwk)
+
+			tc.ExpectedSecret = &v1.Secret{
+				Type: v1.SecretTypeOpaque,
+				Data: map[string][]byte{
+					secretKey: keyBytes,
+				},
+			}
+			tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
+				{
+					SecretKey: secretKey,
+					RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+						Key: "key/" + keyName,
+					},
+				},
+			}
+		})
+	})
+
+})

+ 2 - 1
e2e/suite/azure/azure.go → e2e/suite/azure/azure_secret.go

@@ -21,7 +21,8 @@ import (
 	"github.com/external-secrets/external-secrets/e2e/suite/common"
 )
 
-var _ = Describe("[azure]", Label("azure", "keyvault"), func() {
+// keyvault type=secret should behave just like any other secret store.
+var _ = Describe("[azure]", Label("azure", "keyvault", "secret"), func() {
 	f := framework.New("eso-azure")
 	prov := newFromEnv(f)
 

+ 80 - 4
e2e/suite/azure/provider.go

@@ -15,16 +15,15 @@ package azure
 import (
 	"context"
 	"os"
+	"time"
 
 	"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
 	kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
 
-	// nolint
-	. "github.com/onsi/gomega"
-
 	// nolint
 	. "github.com/onsi/ginkgo/v2"
-
+	// nolint
+	. "github.com/onsi/gomega"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	utilpointer "k8s.io/utils/pointer"
@@ -100,6 +99,83 @@ func (s *azureProvider) DeleteSecret(key string) {
 	Expect(err).ToNot(HaveOccurred())
 }
 
+func (s *azureProvider) CreateKey(key string) *keyvault.JSONWebKey {
+	out, err := s.client.CreateKey(
+		context.Background(),
+		s.vaultURL,
+		key,
+		keyvault.KeyCreateParameters{
+			Kty: keyvault.RSA,
+			KeyAttributes: &keyvault.KeyAttributes{
+				RecoveryLevel: keyvault.Purgeable,
+				Enabled:       utilpointer.BoolPtr(true),
+			},
+		},
+	)
+	Expect(err).ToNot(HaveOccurred())
+	return out.Key
+}
+
+func (s *azureProvider) DeleteKey(key string) {
+	_, err := s.client.DeleteKey(context.Background(), s.vaultURL, key)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *azureProvider) CreateCertificate(key string) {
+	_, err := s.client.CreateCertificate(
+		context.Background(),
+		s.vaultURL,
+		key,
+		keyvault.CertificateCreateParameters{
+			CertificatePolicy: &keyvault.CertificatePolicy{
+				X509CertificateProperties: &keyvault.X509CertificateProperties{
+					Subject:          utilpointer.String("CN=e2e.test"),
+					ValidityInMonths: utilpointer.Int32(42),
+				},
+				IssuerParameters: &keyvault.IssuerParameters{
+					Name: utilpointer.String("Self"),
+				},
+				Attributes: &keyvault.CertificateAttributes{
+					RecoveryLevel: keyvault.Purgeable,
+					Enabled:       utilpointer.BoolPtr(true),
+				},
+			},
+			CertificateAttributes: &keyvault.CertificateAttributes{
+				RecoveryLevel: keyvault.Purgeable,
+				Enabled:       utilpointer.BoolPtr(true),
+			},
+		},
+	)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *azureProvider) GetCertificate(key string) []byte {
+	attempts := 20
+	for {
+		out, err := s.client.GetCertificate(
+			context.Background(),
+			s.vaultURL,
+			key,
+			"",
+		)
+		Expect(err).ToNot(HaveOccurred())
+		if out.Cer != nil {
+			return *out.Cer
+		}
+
+		attempts--
+		if attempts <= 0 {
+			Fail("failed fetching azkv certificate")
+		}
+		<-time.After(time.Second * 5)
+	}
+}
+
+func (s *azureProvider) DeleteCertificate(key string) {
+	_, err := s.client.DeleteCertificate(context.Background(), s.vaultURL, key)
+	Expect(err).ToNot(HaveOccurred())
+}
+
 func (s *azureProvider) CreateSecretStore() {
 	azureCreds := &v1.Secret{
 		ObjectMeta: metav1.ObjectMeta{

+ 3 - 1
e2e/suite/import.go

@@ -16,8 +16,10 @@ package suite
 import (
 
 	// import different e2e test suites.
-	_ "github.com/external-secrets/external-secrets/e2e/suite/aws"
+	_ "github.com/external-secrets/external-secrets/e2e/suite/aws/parameterstore"
+	_ "github.com/external-secrets/external-secrets/e2e/suite/aws/secretsmanager"
 	_ "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/template"
 	_ "github.com/external-secrets/external-secrets/e2e/suite/vault"
 )

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است