Browse Source

fix(e2e): refactor e2e tests

Moritz Johner 4 years ago
parent
commit
ea46ec1911

+ 1 - 0
.github/workflows/e2e.yml

@@ -15,6 +15,7 @@ env:
   # credentials have been provided before trying to run steps that need them.
   GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
   GCP_SM_SA_JSON: ${{ secrets.GCP_SM_SA_JSON}}
+  GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID}}
   AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID}}
   AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET}}
   TENANT_ID: ${{ secrets.TENANT_ID}}

+ 1 - 1
Makefile

@@ -78,7 +78,7 @@ check-diff: reviewable
 .PHONY: test
 test: generate ## Run tests
 	@$(INFO) go test unit-tests
-	go test -v $(shell go list ./... | grep -v e2e) -coverprofile cover.out
+	go test -race -v $(shell go list ./... | grep -v e2e) -coverprofile cover.out
 	@$(OK) go test unit-tests
 
 .PHONY: test.e2e

+ 69 - 0
docs/provider-hashicorp-vault.md

@@ -6,6 +6,75 @@ External Secrets Operator integrates with [HashiCorp Vault](https://www.vaultpro
 management. Vault itself implements lots of different secret engines, as of now we only support the
 [KV Secrets Engine](https://www.vaultproject.io/docs/secrets/kv).
 
+### Example
+
+First, create a SecretStore with a vault backend. For the sake of simplicity we'll use a static token `root`:
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: vault-backend
+spec:
+  provider:
+    vault:
+      server: "http://my.vault.server:8200"
+      path: "secret"
+      version: "v2"
+      auth:
+        # points to a secret that contains a vault token
+        # https://www.vaultproject.io/docs/auth/token
+        tokenSecretRef:
+          name: "vault-token"
+          namespace: "default"
+          key: "token"
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: vault-token
+data:
+  token: cm9vdA== # "root"
+```
+
+Then create a simple k/v pair at path `secret/foo`:
+
+```
+vault kv put secret/foo my-value=s3cr3t
+```
+
+Now create a ExternalSecret that uses the above SecretStore:
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: ExternalSecret
+metadata:
+  name: vault-example
+spec:
+  refreshInterval: "15s"
+  secretStoreRef:
+    name: vault-backend
+    kind: ClusterSecretStore
+  target:
+    name: example-sync
+  data:
+  - secretKey: foobar
+    remoteRef:
+      key: secret/foo
+      property: my-value
+---
+# will create a secret with:
+kind: Secret
+metadata:
+  name: example-sync
+data:
+  foobar: czNjcjN0
+```
+
+#### Limitations
+
+Vault supports only simple key/value pairs - nested objects are not supported. Hence specifying `gjson` properties like other providers support it is not supported.
+
 ### Authentication
 
 We support three different modes for authentication:

+ 1 - 0
e2e/Makefile

@@ -5,6 +5,7 @@ SHELL       := /bin/bash
 IMG_TAG     = test
 IMG         = local/external-secrets-e2e:$(IMG_TAG)
 K8S_VERSION = "1.19.1"
+export FOCUS := $(FOCUS)
 
 start-kind: ## Start kind cluster
 	kind create cluster \

+ 24 - 8
e2e/framework/eso.go

@@ -16,6 +16,7 @@ package framework
 import (
 	"bytes"
 	"context"
+	"encoding/json"
 	"time"
 
 	v1 "k8s.io/api/core/v1"
@@ -26,7 +27,7 @@ import (
 
 // WaitForSecretValue waits until a secret comes into existence and compares the secret.Data
 // with the provided values.
-func (f *Framework) WaitForSecretValue(namespace, name string, values map[string][]byte) (*v1.Secret, error) {
+func (f *Framework) WaitForSecretValue(namespace, name string, expected *v1.Secret) (*v1.Secret, error) {
 	secret := &v1.Secret{}
 	err := wait.PollImmediate(time.Second*2, time.Minute*2, func() (bool, error) {
 		err := f.CRClient.Get(context.Background(), types.NamespacedName{
@@ -36,13 +37,28 @@ func (f *Framework) WaitForSecretValue(namespace, name string, values map[string
 		if apierrors.IsNotFound(err) {
 			return false, nil
 		}
-
-		for k, exp := range values {
-			if actual, ok := secret.Data[k]; ok && !bytes.Equal(actual, exp) {
-				return false, nil
-			}
-		}
-		return true, nil
+		return equalSecrets(expected, secret), nil
 	})
 	return secret, err
 }
+
+func equalSecrets(exp, ts *v1.Secret) bool {
+	if exp.Type != ts.Type {
+		return false
+	}
+	expLabels, _ := json.Marshal(exp.ObjectMeta.Labels)
+	tsLabels, _ := json.Marshal(ts.ObjectMeta.Labels)
+	if !bytes.Equal(expLabels, tsLabels) {
+		return false
+	}
+
+	expAnnotations, _ := json.Marshal(exp.ObjectMeta.Annotations)
+	tsAnnotations, _ := json.Marshal(ts.ObjectMeta.Annotations)
+	if !bytes.Equal(expAnnotations, tsAnnotations) {
+		return false
+	}
+
+	expData, _ := json.Marshal(exp.Data)
+	tsData, _ := json.Marshal(ts.Data)
+	return bytes.Equal(expData, tsData)
+}

+ 91 - 0
e2e/framework/testcase.go

@@ -0,0 +1,91 @@
+/*
+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 framework
+
+import (
+	"context"
+
+	//nolint
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+)
+
+const TargetSecretName = "target-secret"
+
+// TestCase contains the test infra to run a table driven test.
+type TestCase struct {
+	Framework      *Framework
+	ExternalSecret *esv1alpha1.ExternalSecret
+	Secrets        map[string]string
+	ExpectedSecret *v1.Secret
+}
+
+// SecretStoreProvider is a interface that must be implemented
+// by a provider that runs the e2e test.
+type SecretStoreProvider interface {
+	CreateSecret(key string, val string)
+	DeleteSecret(key string)
+}
+
+// TableFunc returns the main func that runs a TestCase in a table driven test.
+func TableFunc(f *Framework, prov SecretStoreProvider) func(func(*TestCase)) {
+	return func(customize func(*TestCase)) {
+		var err error
+
+		// make default test case
+		// and apply customization to it
+		tc := makeDefaultTestCase(f)
+		customize(tc)
+
+		// create secrets & defer delete
+		for k, v := range tc.Secrets {
+			key := k
+			prov.CreateSecret(key, v)
+			defer func() {
+				prov.DeleteSecret(key)
+			}()
+		}
+
+		// create external secret
+		err = tc.Framework.CRClient.Create(context.Background(), tc.ExternalSecret)
+		Expect(err).ToNot(HaveOccurred())
+
+		// wait for Kind=Secret to have the expected data
+		_, err = tc.Framework.WaitForSecretValue(tc.Framework.Namespace.Name, TargetSecretName, tc.ExpectedSecret)
+		Expect(err).ToNot(HaveOccurred())
+	}
+}
+
+func makeDefaultTestCase(f *Framework) *TestCase {
+	return &TestCase{
+		Framework: f,
+		ExternalSecret: &esv1alpha1.ExternalSecret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "e2e-es",
+				Namespace: f.Namespace.Name,
+			},
+			Spec: esv1alpha1.ExternalSecretSpec{
+				SecretStoreRef: esv1alpha1.SecretStoreRef{
+					Name: f.Namespace.Name,
+				},
+				Target: esv1alpha1.ExternalSecretTarget{
+					Name: TargetSecretName,
+				},
+			},
+		},
+	}
+}

+ 7 - 8
e2e/run.sh

@@ -46,18 +46,17 @@ done
 kubectl apply -f ${DIR}/k8s/deploy/crds
 
 echo -e "Starting the e2e test pod"
-FOCUS=${FOCUS:-.*}
-export FOCUS
 
 kubectl run --rm \
   --attach \
   --restart=Never \
   --pod-running-timeout=10m \
-  --env="FOCUS=${FOCUS}" \
-  --env="GCP_SM_SA_JSON=${GCP_SM_SA_JSON}" \
-  --env="AZURE_CLIENT_ID=${AZURE_CLIENT_ID}" \
-  --env="AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET}" \
-  --env="TENANT_ID=${TENANT_ID}" \
-  --env="VAULT_URL=${VAULT_URL}" \
+  --env="FOCUS=${FOCUS:-.*}" \
+  --env="GCP_SM_SA_JSON=${GCP_SM_SA_JSON:-}" \
+  --env="GCP_PROJECT_ID=${GCP_PROJECT_ID:-}" \
+  --env="AZURE_CLIENT_ID=${AZURE_CLIENT_ID:-}" \
+  --env="AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET:-}" \
+  --env="TENANT_ID=${TENANT_ID:-}" \
+  --env="VAULT_URL=${VAULT_URL:-}" \
   --overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \
   e2e --image=local/external-secrets-e2e:test

+ 124 - 0
e2e/suite/aws/provider.go

@@ -0,0 +1,124 @@
+/*
+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"
+
+	"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/secretsmanager"
+
+	//nolint
+	. "github.com/onsi/ginkgo"
+
+	// nolint
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+	prov "github.com/external-secrets/external-secrets/pkg/provider/aws"
+)
+
+type SMProvider struct {
+	url       string
+	client    *secretsmanager.SecretsManager
+	framework *framework.Framework
+}
+
+func newSMProvider(f *framework.Framework, url string) *SMProvider {
+	sess, err := session.NewSessionWithOptions(session.Options{
+		Config: aws.Config{
+			Credentials: credentials.NewStaticCredentials("foobar", "foobar", "secret-manager"),
+			EndpointResolver: prov.ResolveEndpointWithServiceMap(map[string]string{
+				"secretsmanager": url,
+			}),
+			Region: aws.String("eu-east-1"),
+		},
+	})
+	Expect(err).ToNot(HaveOccurred())
+	sm := secretsmanager.New(sess)
+	prov := &SMProvider{
+		url:       url,
+		client:    sm,
+		framework: f,
+	}
+	BeforeEach(prov.BeforeEach)
+	return prov
+}
+
+func (s *SMProvider) CreateSecret(key, val string) {
+	_, err := s.client.CreateSecret(&secretsmanager.CreateSecretInput{
+		Name:         aws.String(key),
+		SecretString: aws.String(val),
+	})
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *SMProvider) DeleteSecret(key string) {
+	_, err := s.client.DeleteSecret(&secretsmanager.DeleteSecretInput{
+		SecretId: aws.String(key),
+	})
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *SMProvider) BeforeEach() {
+	By("creating a AWS SM credentials secret")
+	awsCreds := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "provider-secret",
+			Namespace: s.framework.Namespace.Name,
+		},
+		StringData: map[string]string{
+			"kid": "foobar",
+			"sak": "foobar",
+		},
+	}
+	err := s.framework.CRClient.Create(context.Background(), awsCreds)
+	Expect(err).ToNot(HaveOccurred())
+
+	By("creating a AWS SM secret store")
+	secretStore := &esv1alpha1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      s.framework.Namespace.Name,
+			Namespace: s.framework.Namespace.Name,
+		},
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				AWS: &esv1alpha1.AWSProvider{
+					Service: esv1alpha1.AWSServiceSecretsManager,
+					Region:  "us-east-1",
+					Auth: &esv1alpha1.AWSAuth{
+						SecretRef: esv1alpha1.AWSAuthSecretRef{
+							AccessKeyID: esmeta.SecretKeySelector{
+								Name: "provider-secret",
+								Key:  "kid",
+							},
+							SecretAccessKey: esmeta.SecretKeySelector{
+								Name: "provider-secret",
+								Key:  "sak",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	err = s.framework.CRClient.Create(context.Background(), secretStore)
+	Expect(err).ToNot(HaveOccurred())
+}

+ 11 - 207
e2e/suite/aws/secretsmanager.go

@@ -14,222 +14,26 @@ limitations under the License.
 package aws
 
 import (
-	"context"
-	"fmt"
 
 	// nolint
 	. "github.com/onsi/ginkgo"
 	// nolint
-	. "github.com/onsi/gomega"
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	. "github.com/onsi/ginkgo/extensions/table"
 
-	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/e2e/framework"
-)
-
-const (
-	targetSecret = "target-secret"
+	"github.com/external-secrets/external-secrets/e2e/suite/common"
 )
 
 var _ = Describe("[aws] ", func() {
 	f := framework.New("eso-aws")
-	var secretStore *esv1alpha1.SecretStore
-	localstackURL := "http://localstack.default"
-
-	BeforeEach(func() {
-		By("creating an secret store for localstack")
-		awsCreds := &v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      f.Namespace.Name,
-				Namespace: f.Namespace.Name,
-			},
-			StringData: map[string]string{
-				"kid": "foobar",
-				"sak": "foobar",
-			},
-		}
-		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: esv1alpha1.AWSServiceSecretsManager,
-						Region:  "us-east-1",
-						Auth: &esv1alpha1.AWSAuth{
-							SecretRef: esv1alpha1.AWSAuthSecretRef{
-								AccessKeyID: esmeta.SecretKeySelector{
-									Name: f.Namespace.Name,
-									Key:  "kid",
-								},
-								SecretAccessKey: esmeta.SecretKeySelector{
-									Name: f.Namespace.Name,
-									Key:  "sak",
-								},
-							},
-						},
-					},
-				},
-			},
-		}
-		err = f.CRClient.Create(context.Background(), secretStore)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync multiple secrets", func() {
-		By("creating a AWS SM Secret")
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "other")
-		secretValue := "bar"
-		err := CreateAWSSecretsManagerSecret(
-			localstackURL,
-			secretKey1, secretValue)
-		Expect(err).ToNot(HaveOccurred())
-		err = CreateAWSSecretsManagerSecret(
-			localstackURL,
-			secretKey2, secretValue)
-		Expect(err).ToNot(HaveOccurred())
-
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "simple-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: f.Namespace.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				Data: []esv1alpha1.ExternalSecretData{
-					{
-						SecretKey: secretKey1,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key: secretKey1,
-						},
-					},
-					{
-						SecretKey: secretKey2,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key: secretKey2,
-						},
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			secretKey1: []byte(secretValue),
-			secretKey2: []byte(secretValue),
-		})
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync secrets with dataFrom", func() {
-		By("creating a AWS SM Secret")
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		targetSecretKey1 := "name"
-		targetSecretValue1 := "great-name"
-		targetSecretKey2 := "surname"
-		targetSecretValue2 := "great-surname"
-		secretValue := fmt.Sprintf("{ \"%s\": \"%s\", \"%s\": \"%s\" }", targetSecretKey1, targetSecretValue1, targetSecretKey2, targetSecretValue2)
-		err := CreateAWSSecretsManagerSecret(
-			localstackURL,
-			secretKey1, secretValue)
-		Expect(err).ToNot(HaveOccurred())
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "datafrom-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: f.Namespace.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				DataFrom: []esv1alpha1.ExternalSecretDataRemoteRef{
-					{
-						Key: secretKey1,
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			targetSecretKey1: []byte(targetSecretValue1),
-			targetSecretKey2: []byte(targetSecretValue2),
-		})
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync secrets and get inner keys", func() {
-		By("creating a AWS SM Secret")
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		targetSecretKey1 := "firstname"
-		targetSecretValue1 := "Tom"
-		targetSecretKey2 := "first_friend"
-		targetSecretValue2 := "Roger"
-		secretValue := fmt.Sprintf(
-			`{
-				"name": {"first": "%s", "last": "Anderson"},
-				"friends": 
-				[ 
-					{"first": "Dale", "last": "Murphy"}, 
-					{"first": "%s", "last": "Craig"}, 
-					{"first": "Jane", "last": "Murphy"} 
-				]
-			}`, targetSecretValue1, targetSecretValue2)
-		err := CreateAWSSecretsManagerSecret(
-			localstackURL,
-			secretKey1, secretValue)
-		Expect(err).ToNot(HaveOccurred())
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "datafrom-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: f.Namespace.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				Data: []esv1alpha1.ExternalSecretData{
-					{
-						SecretKey: targetSecretKey1,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key:      secretKey1,
-							Property: "name.first",
-						},
-					},
-					{
-						SecretKey: targetSecretKey2,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key:      secretKey1,
-							Property: "friends.1.first",
-						},
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
 
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			targetSecretKey1: []byte(targetSecretValue1),
-			targetSecretKey2: []byte(targetSecretValue2),
-		})
-		Expect(err).ToNot(HaveOccurred())
-	})
+	DescribeTable("sync secrets",
+		framework.TableFunc(f,
+			newSMProvider(f, "http://localstack.default")),
+		Entry(common.SimpleDataSync(f)),
+		Entry(common.NestedJSONWithGJSON(f)),
+		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataWithProperty(f)),
+		Entry(common.JSONDataWithTemplate(f)),
+	)
 })

+ 0 - 44
e2e/suite/aws/util.go

@@ -1,44 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-limitations under the License.
-*/
-package aws
-
-import (
-	"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/secretsmanager"
-
-	prov "github.com/external-secrets/external-secrets/pkg/provider/aws"
-)
-
-// CreateAWSSecretsManagerSecret creates a sm secret with the given value.
-func CreateAWSSecretsManagerSecret(endpoint, secretName, secretValue string) error {
-	sess, err := session.NewSessionWithOptions(session.Options{
-		Config: aws.Config{
-			Credentials: credentials.NewStaticCredentials("foobar", "foobar", "secret-manager"),
-			EndpointResolver: prov.ResolveEndpointWithServiceMap(map[string]string{
-				"secretsmanager": endpoint,
-			}),
-			Region: aws.String("eu-east-1"),
-		},
-	})
-	if err != nil {
-		return err
-	}
-	sm := secretsmanager.New(sess)
-	_, err = sm.CreateSecret(&secretsmanager.CreateSecretInput{
-		Name:         aws.String(secretName),
-		SecretString: aws.String(secretValue),
-	})
-	return err
-}

+ 13 - 99
e2e/suite/azure/azure.go

@@ -13,118 +13,32 @@ limitations under the License.
 package azure
 
 import (
-	"context"
-	"fmt"
 	"os"
 
 	// nolint
 	. "github.com/onsi/ginkgo"
 	// nolint
-	. "github.com/onsi/gomega"
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	. "github.com/onsi/ginkgo/extensions/table"
 
-	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/e2e/framework"
-)
-
-const (
-	targetSecret = "target-secret"
+	"github.com/external-secrets/external-secrets/e2e/suite/common"
 )
 
 var _ = Describe("[azure] ", func() {
 	f := framework.New("eso-azure")
-	var secretStore *esv1alpha1.SecretStore
 	vaultURL := os.Getenv("VAULT_URL")
 	tenantID := os.Getenv("TENANT_ID")
 	clientID := os.Getenv("AZURE_CLIENT_ID")
 	clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
-	BeforeEach(func() {
-		By("creating a secret in AzureKV")
-		azureCreds := &v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      f.Namespace.Name,
-				Namespace: f.Namespace.Name,
-			},
-			StringData: map[string]string{
-				"ClientID":     clientID,
-				"ClientSecret": clientSecret,
-			},
-		}
-		err := f.CRClient.Create(context.Background(), azureCreds)
-		Expect(err).ToNot(HaveOccurred())
-		secretStore = &esv1alpha1.SecretStore{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      f.Namespace.Name,
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.SecretStoreSpec{
-				Provider: &esv1alpha1.SecretStoreProvider{
-					AzureKV: &esv1alpha1.AzureKVProvider{
-						TenantID: &tenantID,
-						VaultURL: &vaultURL,
-						AuthSecretRef: &esv1alpha1.AzureKVAuth{
-							ClientID: &esmeta.SecretKeySelector{
-								Name: f.Namespace.Name,
-								Key:  "ClientID",
-							},
-							ClientSecret: &esmeta.SecretKeySelector{
-								Name: f.Namespace.Name,
-								Key:  "ClientSecret",
-							},
-						},
-					},
-				},
-			},
-		}
-		err = f.CRClient.Create(context.Background(), secretStore)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync secrets", func() {
-		By("creating a AzureKV Secret")
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		secretValue := "great-value-test"
-		_, err := createAzureKVSecret(
-			secretKey1,
-			secretValue,
-			clientID,
-			clientSecret,
-			tenantID,
-			vaultURL)
-		Expect(err).ToNot(HaveOccurred())
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "simple-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: f.Namespace.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				Data: []esv1alpha1.ExternalSecretData{
-					{
-						SecretKey: secretKey1,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key: secretKey1,
-						},
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			secretKey1: []byte(secretValue),
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		err = deleteAzureKVSecret(secretKey1, clientID, clientSecret, tenantID, vaultURL)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
+	prov := newazureProvider(f, clientID, clientSecret, tenantID, vaultURL)
+
+	DescribeTable("sync secrets", framework.TableFunc(f, prov),
+		Entry(common.SimpleDataSync(f)),
+		Entry(common.NestedJSONWithGJSON(f)),
+		// TODO: dataFrom is not working as expected RN
+		// see: https://github.com/external-secrets/external-secrets/issues/263
+		// Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataWithProperty(f)),
+		Entry(common.JSONDataWithTemplate(f)),
+	)
 })

+ 127 - 0
e2e/suite/azure/provider.go

@@ -0,0 +1,127 @@
+/*
+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 (
+	"context"
+
+	"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
+	kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
+
+	// nolint
+	. "github.com/onsi/ginkgo"
+
+	// nolint
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	utilpointer "k8s.io/utils/pointer"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+type azureProvider struct {
+	clientID     string
+	clientSecret string
+	tenantID     string
+	vaultURL     string
+	client       *keyvault.BaseClient
+	framework    *framework.Framework
+}
+
+func newazureProvider(f *framework.Framework, clientID, clientSecret, tenantID, vaultURL string) *azureProvider {
+	clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
+	clientCredentialsConfig.Resource = "https://vault.azure.net"
+	authorizer, err := clientCredentialsConfig.Authorizer()
+	Expect(err).ToNot(HaveOccurred())
+	basicClient := keyvault.New()
+	basicClient.Authorizer = authorizer
+
+	prov := &azureProvider{
+		framework:    f,
+		clientID:     clientID,
+		clientSecret: clientSecret,
+		tenantID:     tenantID,
+		vaultURL:     vaultURL,
+		client:       &basicClient,
+	}
+	BeforeEach(prov.BeforeEach)
+	return prov
+}
+
+func (s *azureProvider) CreateSecret(key, val string) {
+	_, err := s.client.SetSecret(
+		context.Background(),
+		s.vaultURL,
+		key,
+		keyvault.SecretSetParameters{
+			Value: &val,
+			SecretAttributes: &keyvault.SecretAttributes{
+				RecoveryLevel: keyvault.Purgeable,
+				Enabled:       utilpointer.BoolPtr(true),
+			},
+		})
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *azureProvider) DeleteSecret(key string) {
+	_, err := s.client.DeleteSecret(
+		context.Background(),
+		s.vaultURL,
+		key)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *azureProvider) BeforeEach() {
+	azureCreds := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "provider-secret",
+			Namespace: s.framework.Namespace.Name,
+		},
+		StringData: map[string]string{
+			"client-id":     s.clientID,
+			"client-secret": s.clientSecret,
+		},
+	}
+	err := s.framework.CRClient.Create(context.Background(), azureCreds)
+	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{
+				AzureKV: &esv1alpha1.AzureKVProvider{
+					TenantID: &s.tenantID,
+					VaultURL: &s.vaultURL,
+					AuthSecretRef: &esv1alpha1.AzureKVAuth{
+						ClientID: &esmeta.SecretKeySelector{
+							Name: "provider-secret",
+							Key:  "client-id",
+						},
+						ClientSecret: &esmeta.SecretKeySelector{
+							Name: "provider-secret",
+							Key:  "client-secret",
+						},
+					},
+				},
+			},
+		},
+	}
+	err = s.framework.CRClient.Create(context.Background(), secretStore)
+	Expect(err).ToNot(HaveOccurred())
+}

+ 0 - 79
e2e/suite/azure/util.go

@@ -1,79 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-limitations under the License.
-*/
-package azure
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
-	kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
-	utilpointer "k8s.io/utils/pointer"
-)
-
-// CreateAWSSecretsManagerSecret creates a sm secret with the given value.
-func createAzureKVSecret(secretName, secretValue, clientID, clientSecret, tenantID, vaultURL string) (result keyvault.SecretBundle, err error) {
-	ctx := context.Background()
-
-	clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
-	clientCredentialsConfig.Resource = "https://vault.azure.net"
-	authorizer, err := clientCredentialsConfig.Authorizer()
-	if err != nil {
-		return keyvault.SecretBundle{}, fmt.Errorf("could not configure azure authorizer: %w", err)
-	}
-
-	basicClient := keyvault.New()
-	basicClient.Authorizer = authorizer
-	deletionRecoveryLevel := keyvault.Purgeable
-	result, err = basicClient.SetSecret(
-		ctx,
-		vaultURL,
-		secretName,
-		keyvault.SecretSetParameters{
-			Value: &secretValue,
-			SecretAttributes: &keyvault.SecretAttributes{
-				RecoveryLevel: deletionRecoveryLevel,
-				Enabled:       utilpointer.BoolPtr(true),
-			},
-		})
-	if err != nil {
-		return keyvault.SecretBundle{}, fmt.Errorf("could not create secret key %s: %w", secretName, err)
-	}
-
-	return result, err
-}
-
-// deleteSecret deletes the secret with the given name and all of its versions.
-func deleteAzureKVSecret(secretName, clientID, clientSecret, tenantID, vaultURL string) error {
-	ctx := context.Background()
-
-	clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
-	clientCredentialsConfig.Resource = "https://vault.azure.net"
-	authorizer, err := clientCredentialsConfig.Authorizer()
-	if err != nil {
-		return fmt.Errorf("could not configure azure authorizer: %w", err)
-	}
-
-	basicClient := keyvault.New()
-	basicClient.Authorizer = authorizer
-
-	_, err = basicClient.DeleteSecret(
-		ctx,
-		vaultURL,
-		secretName)
-	if err != nil {
-		return fmt.Errorf("could not delete secret: %w", err)
-	}
-
-	return err
-}

+ 230 - 0
e2e/suite/common/common.go

@@ -0,0 +1,230 @@
+/*
+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 common
+
+import (
+	"fmt"
+
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+// This case creates multiple secrets with simple key/value pairs and syncs them using multiple .Spec.Data blocks.
+// Not supported by: vault.
+func SimpleDataSync(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should sync simple secrets from .Data[]", func(tc *framework.TestCase) {
+		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
+		secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "other")
+		secretValue := "bar"
+		tc.Secrets = map[string]string{
+			secretKey1: secretValue,
+			secretKey2: secretValue,
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				secretKey1: []byte(secretValue),
+				secretKey2: []byte(secretValue),
+			},
+		}
+		tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
+			{
+				SecretKey: secretKey1,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key: secretKey1,
+				},
+			},
+			{
+				SecretKey: secretKey2,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key: secretKey2,
+				},
+			},
+		}
+	}
+}
+
+// This case creates multiple secrets with json values and syncs them using multiple .Spec.Data blocks.
+// The data is extracted from the JSON key using ref.Property.
+func JSONDataWithProperty(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should sync multiple secrets from .Data[]", func(tc *framework.TestCase) {
+		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
+		secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "two")
+		secretValue1 := "{\"foo1\":\"foo1-val\",\"bar1\":\"bar1-val\"}"
+		secretValue2 := "{\"foo2\":\"foo2-val\",\"bar2\":\"bar2-val\"}"
+		tc.Secrets = map[string]string{
+			secretKey1: secretValue1,
+			secretKey2: secretValue2,
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				secretKey1: []byte("foo1-val"),
+				secretKey2: []byte("bar2-val"),
+			},
+		}
+		tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
+			{
+				SecretKey: secretKey1,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key:      secretKey1,
+					Property: "foo1",
+				},
+			},
+			{
+				SecretKey: secretKey2,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key:      secretKey2,
+					Property: "bar2",
+				},
+			},
+		}
+	}
+}
+
+// This case creates multiple secrets with json values and renders a template.
+// The data is extracted from the JSON key using ref.Property.
+func JSONDataWithTemplate(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should sync json secrets with template", func(tc *framework.TestCase) {
+		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
+		secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "other")
+		secretValue1 := "{\"foo1\":\"foo1-val\",\"bar1\":\"bar1-val\"}"
+		secretValue2 := "{\"foo2\":\"foo2-val\",\"bar2\":\"bar2-val\"}"
+		tc.Secrets = map[string]string{
+			secretKey1: secretValue1,
+			secretKey2: secretValue2,
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			ObjectMeta: metav1.ObjectMeta{
+				Annotations: map[string]string{
+					"example": "annotation",
+				},
+				Labels: map[string]string{
+					"example": "label",
+				},
+			},
+			Data: map[string][]byte{
+				"my-data": []byte(`executed: foo1-val|bar2-val`),
+			},
+		}
+		tc.ExternalSecret.Spec.Target.Template = &esv1alpha1.ExternalSecretTemplate{
+			Metadata: esv1alpha1.ExternalSecretTemplateMetadata{
+				Annotations: map[string]string{
+					"example": "annotation",
+				},
+				Labels: map[string]string{
+					"example": "label",
+				},
+			},
+			Data: map[string]string{
+				"my-data": "executed: {{ .one | toString }}|{{ .two | toString }}",
+			},
+		}
+		tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
+			{
+				SecretKey: "one",
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key:      secretKey1,
+					Property: "foo1",
+				},
+			},
+			{
+				SecretKey: "two",
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key:      secretKey2,
+					Property: "bar2",
+				},
+			},
+		}
+	}
+}
+
+// This case creates one secret with json values and syncs them using a single .Spec.DataFrom block.
+func JSONDataFromSync(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should sync secrets with dataFrom", func(tc *framework.TestCase) {
+		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
+		targetSecretKey1 := "name"
+		targetSecretValue1 := "great-name"
+		targetSecretKey2 := "surname"
+		targetSecretValue2 := "great-surname"
+		secretValue := fmt.Sprintf("{ \"%s\": \"%s\", \"%s\": \"%s\" }", targetSecretKey1, targetSecretValue1, targetSecretKey2, targetSecretValue2)
+		tc.Secrets = map[string]string{
+			secretKey1: secretValue,
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				targetSecretKey1: []byte(targetSecretValue1),
+				targetSecretKey2: []byte(targetSecretValue2),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esv1alpha1.ExternalSecretDataRemoteRef{
+			{
+				Key: secretKey1,
+			},
+		}
+	}
+}
+
+// This case creates a secret with a nested json value. It is synced into two secrets.
+// The values from the nested data are extracted using gjson.
+// not supported by: vault.
+func NestedJSONWithGJSON(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should sync nested json secrets and get inner keys", func(tc *framework.TestCase) {
+		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
+		targetSecretKey1 := "firstname"
+		targetSecretValue1 := "Tom"
+		targetSecretKey2 := "first_friend"
+		targetSecretValue2 := "Roger"
+		secretValue := fmt.Sprintf(
+			`{
+				"name": {"first": "%s", "last": "Anderson"},
+				"friends":
+				[
+					{"first": "Dale", "last": "Murphy"},
+					{"first": "%s", "last": "Craig"},
+					{"first": "Jane", "last": "Murphy"}
+				]
+			}`, targetSecretValue1, targetSecretValue2)
+		tc.Secrets = map[string]string{
+			secretKey1: secretValue,
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				targetSecretKey1: []byte(targetSecretValue1),
+				targetSecretKey2: []byte(targetSecretValue2),
+			},
+		}
+		tc.ExternalSecret.Spec.Data = []esv1alpha1.ExternalSecretData{
+			{
+				SecretKey: targetSecretKey1,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key:      secretKey1,
+					Property: "name.first",
+				},
+			},
+			{
+				SecretKey: targetSecretKey2,
+				RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
+					Key:      secretKey1,
+					Property: "friends.1.first",
+				},
+			},
+		}
+	}
+}

+ 12 - 199
e2e/suite/gcp/gcp.go

@@ -13,215 +13,28 @@ limitations under the License.
 package gcp
 
 import (
-	"context"
-	"fmt"
 	"os"
 
 	// nolint
 	. "github.com/onsi/ginkgo"
 	// nolint
-	. "github.com/onsi/gomega"
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	. "github.com/onsi/ginkgo/extensions/table"
 
-	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/e2e/framework"
-)
-
-const (
-	targetSecret = "target-secret"
+	"github.com/external-secrets/external-secrets/e2e/suite/common"
 )
 
 var _ = Describe("[gcp] ", func() {
 	f := framework.New("eso-gcp")
-	var secretStore *esv1alpha1.SecretStore
-	projectID := "external-secrets-operator"
 	credentials := os.Getenv("GCP_SM_SA_JSON")
-
-	BeforeEach(func() {
-		By("creating a secret in GCP SM")
-		gcpCred := &v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      f.Namespace.Name,
-				Namespace: f.Namespace.Name,
-			},
-			StringData: map[string]string{
-				"secret-access-credentials": credentials,
-			},
-		}
-		err := f.CRClient.Create(context.Background(), gcpCred)
-		Expect(err).ToNot(HaveOccurred())
-		secretStore = &esv1alpha1.SecretStore{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      f.Namespace.Name,
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.SecretStoreSpec{
-				Provider: &esv1alpha1.SecretStoreProvider{
-					GCPSM: &esv1alpha1.GCPSMProvider{
-						ProjectID: projectID,
-						Auth: esv1alpha1.GCPSMAuth{
-							SecretRef: esv1alpha1.GCPSMAuthSecretRef{
-								SecretAccessKey: esmeta.SecretKeySelector{
-									Name: f.Namespace.Name,
-									Key:  "secret-access-credentials",
-								},
-							},
-						},
-					},
-				},
-			},
-		}
-		err = f.CRClient.Create(context.Background(), secretStore)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync secrets", func() {
-		By("creating a GCP SM Secret")
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		secretValue := "great-value-test"
-		secret, err := createGCPSecretsManagerSecret(
-			projectID,
-			secretKey1, secretValue, []byte(credentials))
-		Expect(err).ToNot(HaveOccurred())
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "simple-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: f.Namespace.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				Data: []esv1alpha1.ExternalSecretData{
-					{
-						SecretKey: secretKey1,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key: secretKey1,
-						},
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			secretKey1: []byte(secretValue),
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		err = deleteGCPSecretsManagerSecret(secret.Name, []byte(credentials))
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync secrets with dataFrom", func() {
-		By("creating a GCP SM Secret with JSON string")
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		targetSecretKey1 := "name"
-		targetSecretValue1 := "great-name"
-		targetSecretKey2 := "surname"
-		targetSecretValue2 := "great-surname"
-		secretValue := fmt.Sprintf("{ \"%s\": \"%s\", \"%s\": \"%s\" }", targetSecretKey1, targetSecretValue1, targetSecretKey2, targetSecretValue2)
-		secret, err := createGCPSecretsManagerSecret(
-			projectID,
-			secretKey1, secretValue, []byte(credentials))
-		Expect(err).ToNot(HaveOccurred())
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "datafrom-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: f.Namespace.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				DataFrom: []esv1alpha1.ExternalSecretDataRemoteRef{
-					{
-						Key: secretKey1,
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			targetSecretKey1: []byte(targetSecretValue1),
-			targetSecretKey2: []byte(targetSecretValue2),
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		err = deleteGCPSecretsManagerSecret(secret.Name, []byte(credentials))
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync secrets and get inner keys", func() {
-		By("creating a GCP SM Secret")
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		targetSecretKey1 := "firstname"
-		targetSecretValue1 := "Tom"
-		targetSecretKey2 := "first_friend"
-		targetSecretValue2 := "Roger"
-		secretValue := fmt.Sprintf(
-			`{
-				"name": {"first": "%s", "last": "Anderson"},
-				"friends": 
-				[ 
-					{"first": "Dale", "last": "Murphy"}, 
-					{"first": "%s", "last": "Craig"}, 
-					{"first": "Jane", "last": "Murphy"} 
-				]
-			}`, targetSecretValue1, targetSecretValue2)
-		secret, err := createGCPSecretsManagerSecret(
-			projectID,
-			secretKey1, secretValue, []byte(credentials))
-		Expect(err).ToNot(HaveOccurred())
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "datafrom-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: f.Namespace.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				Data: []esv1alpha1.ExternalSecretData{
-					{
-						SecretKey: targetSecretKey1,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key:      secretKey1,
-							Property: "name.first",
-						},
-					},
-					{
-						SecretKey: targetSecretKey2,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key:      secretKey1,
-							Property: "friends.1.first",
-						},
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			targetSecretKey1: []byte(targetSecretValue1),
-			targetSecretKey2: []byte(targetSecretValue2),
-		})
-		Expect(err).ToNot(HaveOccurred())
-
-		err = deleteGCPSecretsManagerSecret(secret.Name, []byte(credentials))
-		Expect(err).ToNot(HaveOccurred())
-	})
-
+	projectID := os.Getenv("GCP_PROJECT_ID")
+	prov := newgcpProvider(f, credentials, projectID)
+
+	DescribeTable("sync secrets", framework.TableFunc(f, prov),
+		Entry(common.SimpleDataSync(f)),
+		Entry(common.JSONDataWithProperty(f)),
+		Entry(common.JSONDataFromSync(f)),
+		Entry(common.NestedJSONWithGJSON(f)),
+		Entry(common.JSONDataWithTemplate(f)),
+	)
 })

+ 139 - 0
e2e/suite/gcp/provider.go

@@ -0,0 +1,139 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+limitations under the License.
+*/
+package gcp
+
+import (
+	"context"
+	"fmt"
+
+	secretmanager "cloud.google.com/go/secretmanager/apiv1"
+
+	// nolint
+	. "github.com/onsi/ginkgo"
+
+	// nolint
+	. "github.com/onsi/gomega"
+	"golang.org/x/oauth2/google"
+	"google.golang.org/api/option"
+	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+	gcpsm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
+)
+
+type gcpProvider struct {
+	credentials string
+	projectID   string
+	framework   *framework.Framework
+}
+
+func newgcpProvider(f *framework.Framework, credentials, projectID string) *gcpProvider {
+	prov := &gcpProvider{
+		credentials: credentials,
+		projectID:   projectID,
+		framework:   f,
+	}
+	BeforeEach(prov.BeforeEach)
+	return prov
+}
+
+func (s *gcpProvider) CreateSecret(key, val string) {
+	ctx := context.Background()
+	config, err := google.JWTConfigFromJSON([]byte(s.credentials), gcpsm.CloudPlatformRole)
+	Expect(err).ToNot(HaveOccurred())
+	ts := config.TokenSource(ctx)
+	client, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
+	Expect(err).ToNot(HaveOccurred())
+	defer client.Close()
+	// Create the request to create the secret.
+	createSecretReq := &secretmanagerpb.CreateSecretRequest{
+		Parent:   fmt.Sprintf("projects/%s", s.projectID),
+		SecretId: key,
+		Secret: &secretmanagerpb.Secret{
+			Replication: &secretmanagerpb.Replication{
+				Replication: &secretmanagerpb.Replication_Automatic_{
+					Automatic: &secretmanagerpb.Replication_Automatic{},
+				},
+			},
+		},
+	}
+	secret, err := client.CreateSecret(ctx, createSecretReq)
+	Expect(err).ToNot(HaveOccurred())
+	addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
+		Parent: secret.Name,
+		Payload: &secretmanagerpb.SecretPayload{
+			Data: []byte(val),
+		},
+	}
+	_, err = client.AddSecretVersion(ctx, addSecretVersionReq)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *gcpProvider) DeleteSecret(key string) {
+	ctx := context.Background()
+	config, err := google.JWTConfigFromJSON([]byte(s.credentials), gcpsm.CloudPlatformRole)
+	Expect(err).ToNot(HaveOccurred())
+	ts := config.TokenSource(ctx)
+	client, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
+	Expect(err).ToNot(HaveOccurred())
+	defer client.Close()
+	req := &secretmanagerpb.DeleteSecretRequest{
+		Name: fmt.Sprintf("projects/%s/secrets/%s", s.projectID, key),
+	}
+	err = client.DeleteSecret(ctx, req)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *gcpProvider) BeforeEach() {
+	By("creating a gcp secret")
+	gcpCreds := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "provider-secret",
+			Namespace: s.framework.Namespace.Name,
+		},
+		StringData: map[string]string{
+			"secret-access-credentials": s.credentials,
+		},
+	}
+	err := s.framework.CRClient.Create(context.Background(), gcpCreds)
+	Expect(err).ToNot(HaveOccurred())
+
+	By("creating an secret store for vault")
+	secretStore := &esv1alpha1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      s.framework.Namespace.Name,
+			Namespace: s.framework.Namespace.Name,
+		},
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				GCPSM: &esv1alpha1.GCPSMProvider{
+					ProjectID: s.projectID,
+					Auth: esv1alpha1.GCPSMAuth{
+						SecretRef: esv1alpha1.GCPSMAuthSecretRef{
+							SecretAccessKey: esmeta.SecretKeySelector{
+								Name: "provider-secret",
+								Key:  "secret-access-credentials",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	err = s.framework.CRClient.Create(context.Background(), secretStore)
+	Expect(err).ToNot(HaveOccurred())
+}

+ 0 - 101
e2e/suite/gcp/util.go

@@ -1,101 +0,0 @@
-/*
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-limitations under the License.
-*/
-package gcp
-
-import (
-	"context"
-	"fmt"
-
-	secretmanager "cloud.google.com/go/secretmanager/apiv1"
-	"golang.org/x/oauth2/google"
-	"google.golang.org/api/option"
-	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
-
-	gcpsm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
-)
-
-// CreateAWSSecretsManagerSecret creates a sm secret with the given value.
-func createGCPSecretsManagerSecret(projectID, secretName, secretValue string, credentials []byte) (*secretmanagerpb.Secret, error) {
-	ctx := context.Background()
-
-	config, err := google.JWTConfigFromJSON(credentials, gcpsm.CloudPlatformRole)
-	if err != nil {
-		return nil, fmt.Errorf("unable to procces JSON credentials: %w", err)
-	}
-	ts := config.TokenSource(ctx)
-
-	client, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
-	if err != nil {
-		return nil, fmt.Errorf("failed to setup client: %w", err)
-	}
-	defer client.Close()
-	// Create the request to create the secret.
-	createSecretReq := &secretmanagerpb.CreateSecretRequest{
-		Parent:   fmt.Sprintf("projects/%s", projectID),
-		SecretId: secretName,
-		Secret: &secretmanagerpb.Secret{
-			Replication: &secretmanagerpb.Replication{
-				Replication: &secretmanagerpb.Replication_Automatic_{
-					Automatic: &secretmanagerpb.Replication_Automatic{},
-				},
-			},
-		},
-	}
-	secret, err := client.CreateSecret(ctx, createSecretReq)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create secret: %w", err)
-	}
-	// Declare the payload to store.
-	payload := []byte(secretValue)
-	// Build the request.
-	addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
-		Parent: secret.Name,
-		Payload: &secretmanagerpb.SecretPayload{
-			Data: payload,
-		},
-	}
-	// Call the API.
-	_, err = client.AddSecretVersion(ctx, addSecretVersionReq)
-	if err != nil {
-		return nil, fmt.Errorf("failed to add secret version: %w", err)
-	}
-
-	return secret, err
-}
-
-// deleteSecret deletes the secret with the given name and all of its versions.
-func deleteGCPSecretsManagerSecret(secretName string, credentials []byte) error {
-	ctx := context.Background()
-	config, err := google.JWTConfigFromJSON(credentials, gcpsm.CloudPlatformRole)
-	if err != nil {
-		return fmt.Errorf("unable to procces JSON credentials: %w", err)
-	}
-	ts := config.TokenSource(ctx)
-
-	client, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
-	if err != nil {
-		return fmt.Errorf("failed to setup client: %w", err)
-	}
-	defer client.Close()
-
-	// Build the request.
-	req := &secretmanagerpb.DeleteSecretRequest{
-		Name: secretName,
-	}
-
-	// Call the API.
-	if err := client.DeleteSecret(ctx, req); err != nil {
-		return fmt.Errorf("failed to delete secret: %w", err)
-	}
-	return nil
-}

+ 111 - 0
e2e/suite/vault/provider.go

@@ -0,0 +1,111 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package vault
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	vault "github.com/hashicorp/vault/api"
+
+	//nolint
+	. "github.com/onsi/ginkgo"
+
+	//nolint
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+type vaultProvider struct {
+	url       string
+	token     string
+	client    *vault.Client
+	framework *framework.Framework
+}
+
+func newVaultProvider(f *framework.Framework, url, token string) *vaultProvider {
+	vc, err := vault.NewClient(&vault.Config{
+		Address: url,
+	})
+	Expect(err).ToNot(HaveOccurred())
+	vc.SetToken(token)
+
+	prov := &vaultProvider{
+		framework: f,
+		url:       url,
+		token:     token,
+		client:    vc,
+	}
+	BeforeEach(prov.BeforeEach)
+	return prov
+}
+
+func (s *vaultProvider) CreateSecret(key, val string) {
+	req := s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", key))
+	req.BodyBytes = []byte(fmt.Sprintf(`{"data": %s}`, val))
+	_, err := s.client.RawRequestWithContext(context.Background(), req)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *vaultProvider) DeleteSecret(key string) {
+	req := s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret/data/%s", key))
+	_, err := s.client.RawRequestWithContext(context.Background(), req)
+	Expect(err).ToNot(HaveOccurred())
+}
+
+func (s *vaultProvider) BeforeEach() {
+	By("creating a vault secret")
+	vaultCreds := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "provider-secret",
+			Namespace: s.framework.Namespace.Name,
+		},
+		StringData: map[string]string{
+			"token": s.token, // vault dev-mode default token
+		},
+	}
+	err := s.framework.CRClient.Create(context.Background(), vaultCreds)
+	Expect(err).ToNot(HaveOccurred())
+
+	By("creating an secret store for vault")
+	secretStore := &esv1alpha1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      s.framework.Namespace.Name,
+			Namespace: s.framework.Namespace.Name,
+		},
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				Vault: &esv1alpha1.VaultProvider{
+					Version: esv1alpha1.VaultKVStoreV2,
+					Path:    "secret",
+					Server:  s.url,
+					Auth: esv1alpha1.VaultAuth{
+						TokenSecretRef: &esmeta.SecretKeySelector{
+							Name: "provider-secret",
+							Key:  "token",
+						},
+					},
+				},
+			},
+		},
+	}
+	err = s.framework.CRClient.Create(context.Background(), secretStore)
+	Expect(err).ToNot(HaveOccurred())
+}

+ 9 - 101
e2e/suite/vault/vault.go

@@ -13,116 +13,24 @@ limitations under the License.
 package vault
 
 import (
-	"context"
-	"fmt"
-	"net/http"
-
-	vault "github.com/hashicorp/vault/api"
 
 	// nolint
 	. "github.com/onsi/ginkgo"
 	// nolint
-	. "github.com/onsi/gomega"
-	v1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	. "github.com/onsi/ginkgo/extensions/table"
 
-	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/e2e/framework"
+	"github.com/external-secrets/external-secrets/e2e/suite/common"
 )
 
 var _ = Describe("[vault] ", func() {
 	f := framework.New("eso-vault")
-	var secretStore *esv1alpha1.SecretStore
-
-	BeforeEach(func() {
-		By("creating an secret store for vault")
-		vaultCreds := &v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      f.Namespace.Name,
-				Namespace: f.Namespace.Name,
-			},
-			StringData: map[string]string{
-				"token": "root", // vault dev-mode default token
-			},
-		}
-		err := f.CRClient.Create(context.Background(), vaultCreds)
-		Expect(err).ToNot(HaveOccurred())
-		secretStore = &esv1alpha1.SecretStore{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      f.Namespace.Name,
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.SecretStoreSpec{
-				Provider: &esv1alpha1.SecretStoreProvider{
-					Vault: &esv1alpha1.VaultProvider{
-						Version: esv1alpha1.VaultKVStoreV2,
-						Path:    "secret",
-						Server:  "http://vault.default:8200",
-						Auth: esv1alpha1.VaultAuth{
-							TokenSecretRef: &esmeta.SecretKeySelector{
-								Name: f.Namespace.Name,
-								Key:  "token",
-							},
-						},
-					},
-				},
-			},
-		}
-		err = f.CRClient.Create(context.Background(), secretStore)
-		Expect(err).ToNot(HaveOccurred())
-	})
-
-	It("should sync secrets", func() {
-		secretKey := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		secretProp := "example"
-		secretValue := "bar"
-		targetSecret := "target-secret"
-
-		By("creating a vault secret")
-		vc, err := vault.NewClient(&vault.Config{
-			Address: "http://vault.default:8200",
-		})
-		Expect(err).ToNot(HaveOccurred())
-		vc.SetToken("root") // dev-mode default token
-		req := vc.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", secretKey))
-		err = req.SetJSONBody(map[string]interface{}{
-			"data": map[string]string{
-				secretProp: secretValue,
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-		_, err = vc.RawRequestWithContext(context.Background(), req)
-		Expect(err).ToNot(HaveOccurred())
 
-		By("creating ExternalSecret")
-		err = f.CRClient.Create(context.Background(), &esv1alpha1.ExternalSecret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "simple-sync",
-				Namespace: f.Namespace.Name,
-			},
-			Spec: esv1alpha1.ExternalSecretSpec{
-				SecretStoreRef: esv1alpha1.SecretStoreRef{
-					Name: secretStore.Name,
-				},
-				Target: esv1alpha1.ExternalSecretTarget{
-					Name: targetSecret,
-				},
-				Data: []esv1alpha1.ExternalSecretData{
-					{
-						SecretKey: secretKey,
-						RemoteRef: esv1alpha1.ExternalSecretDataRemoteRef{
-							Key:      secretKey,
-							Property: secretProp,
-						},
-					},
-				},
-			},
-		})
-		Expect(err).ToNot(HaveOccurred())
-		_, err = f.WaitForSecretValue(f.Namespace.Name, targetSecret, map[string][]byte{
-			secretKey: []byte(secretValue),
-		})
-		Expect(err).ToNot(HaveOccurred())
-	})
+	DescribeTable("sync secrets",
+		framework.TableFunc(f,
+			newVaultProvider(f, "http://vault.default:8200", "root")),
+		Entry(common.JSONDataFromSync(f)),
+		Entry(common.JSONDataWithProperty(f)),
+		Entry(common.JSONDataWithTemplate(f)),
+	)
 })

+ 9 - 1
pkg/provider/azure/keyvault/keyvault.go

@@ -23,6 +23,7 @@ import (
 
 	"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
 	kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
+	"github.com/tidwall/gjson"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -110,7 +111,14 @@ func (a *Azure) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretData
 		if err != nil {
 			return nil, err
 		}
-		return []byte(*secretResp.Value), nil
+		if ref.Property == "" {
+			return []byte(*secretResp.Value), nil
+		}
+		res := gjson.Get(*secretResp.Value, ref.Property)
+		if !res.Exists() {
+			return nil, fmt.Errorf("property %s does not exist in key %s", ref.Property, ref.Key)
+		}
+		return []byte(res.String()), err
 	case "cert":
 		// returns a CertBundle. We return CER contents of x509 certificate
 		// see: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault#CertificateBundle

+ 3 - 1
pkg/provider/gcp/secretmanager/secretsmanager.go

@@ -167,7 +167,9 @@ func (sm *ProviderGCP) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSec
 	}
 
 	val := gjson.Get(payload, ref.Property)
-
+	if !val.Exists() {
+		return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
+	}
 	return []byte(val.String()), nil
 }