Browse Source

feature: aws getallsecrets (#820)

* feature: aws getallsecrets

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>

* fix: e2e test / find by name

* feat: add get-by-tags tests, consolidate with existing ones

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>

* feat: add path tests

* fix: revert azure

* fix: secretsmanager prealloc

* feat: aws sm path tests

* feat: implement secretsmanager path filter

* fix: use low refresh interval due to eventual consistency

* revert makefile changes

* fix: add path test cases to managed

Co-authored-by: Docs <docs@external-secrets.io>
Moritz Johner 4 years ago
parent
commit
56c69a1063

+ 7 - 2
e2e/framework/testcase.go

@@ -31,14 +31,19 @@ var TargetSecretName = "target-secret"
 type TestCase struct {
 	Framework      *Framework
 	ExternalSecret *esv1beta1.ExternalSecret
-	Secrets        map[string]string
+	Secrets        map[string]SecretEntry
 	ExpectedSecret *v1.Secret
 }
 
+type SecretEntry struct {
+	Value string
+	Tags  map[string]string
+}
+
 // SecretStoreProvider is a interface that must be implemented
 // by a provider that runs the e2e test.
 type SecretStoreProvider interface {
-	CreateSecret(key string, val string)
+	CreateSecret(key string, val SecretEntry)
 	DeleteSecret(key string)
 }
 

+ 2 - 2
e2e/suite/akeyless/provider.go

@@ -83,14 +83,14 @@ func newFromEnv(f *framework.Framework) *akeylessProvider {
 }
 
 // CreateSecret creates a secret.
-func (a *akeylessProvider) CreateSecret(key, val string) {
+func (a *akeylessProvider) CreateSecret(key string, val framework.SecretEntry) {
 	token, err := a.GetToken()
 	Expect(err).ToNot(HaveOccurred())
 
 	ctx := context.Background()
 	gsvBody := akeyless.CreateSecret{
 		Name:  key,
-		Value: val,
+		Value: val.Value,
 		Token: &token,
 	}
 

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

@@ -63,7 +63,7 @@ func newFromEnv(f *framework.Framework) *alibabaProvider {
 }
 
 // CreateSecret creates a secret in both kv v1 and v2 provider.
-func (s *alibabaProvider) CreateSecret(key, val string) {
+func (s *alibabaProvider) CreateSecret(key string, val framework.SecretEntry) {
 	client, err := kms.NewClientWithAccessKey(s.regionID, s.accessKeyID, s.accessKeySecret)
 	Expect(err).ToNot(HaveOccurred())
 	kmssecretrequest := kms.CreateCreateSecretRequest()

+ 91 - 0
e2e/suite/aws/parameterstore/find_by_name.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.
+limitations under the License.
+*/
+package aws
+
+import (
+	"fmt"
+
+	v1 "k8s.io/api/core/v1"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/e2e/framework"
+)
+
+// This case creates multiple secrets with simple key/value pairs
+// this is special because parameter store requires a leading "/" in the name.
+func FindByName(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by name using .DataFrom[]", func(tc *framework.TestCase) {
+		const namePrefix = "/e2e/find/name/%s/%s"
+		secretKeyOne := fmt.Sprintf(namePrefix, f.Namespace.Name, "one")
+		secretKeyTwo := fmt.Sprintf(namePrefix, f.Namespace.Name, "two")
+		secretKeyThree := fmt.Sprintf(namePrefix, f.Namespace.Name, "three")
+		secretValue := "{\"foo1\":\"foo1-val\"}"
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne:   {Value: secretValue},
+			secretKeyTwo:   {Value: secretValue},
+			secretKeyThree: {Value: secretValue},
+		}
+		const secNamePrefix = "_e2e_find_name_%s_%s"
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf(secNamePrefix, f.Namespace.Name, "one"):   []byte(secretValue),
+				fmt.Sprintf(secNamePrefix, f.Namespace.Name, "two"):   []byte(secretValue),
+				fmt.Sprintf(secNamePrefix, f.Namespace.Name, "three"): []byte(secretValue),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Name: &esapi.FindName{
+						RegExp: fmt.Sprintf("/e2e/find/name/%s.+", f.Namespace.Name),
+					},
+				},
+			},
+		}
+	}
+}
+
+// This case creates multiple secrets with simple key/value pairs
+// this is special because parameter store requires a leading "/" in the name.
+func FindByNameWithPath(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by name using .DataFrom[]", func(tc *framework.TestCase) {
+		secretKeyOne := fmt.Sprintf("/e2e/find/name/%s/one", f.Namespace.Name)
+		secretKeyTwo := fmt.Sprintf("/%s/two", f.Namespace.Name)
+		secretKeythree := fmt.Sprintf("/%s/three", f.Namespace.Name)
+		secretValue := "{\"foo1\":\"foo1-val\"}"
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne:   {Value: secretValue},
+			secretKeyTwo:   {Value: secretValue},
+			secretKeythree: {Value: secretValue},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf("_%s_two", f.Namespace.Name):   []byte(secretValue),
+				fmt.Sprintf("_%s_three", f.Namespace.Name): []byte(secretValue),
+			},
+		}
+		pathPrefix := fmt.Sprintf("/%s", f.Namespace.Name)
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Path: &pathPrefix,
+					Name: &esapi.FindName{
+						RegExp: ".+",
+					},
+				},
+			},
+		}
+	}
+}

+ 115 - 0
e2e/suite/aws/parameterstore/find_by_tags.go

@@ -0,0 +1,115 @@
+/*
+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 (
+	"fmt"
+
+	v1 "k8s.io/api/core/v1"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"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.
+// this is special because parameter store requires a leading "/" in the name.
+func FindByTag(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by tags using .DataFrom[]", func(tc *framework.TestCase) {
+		const namePrefix = "/e2e/find/name/%s/%s"
+		secretKeyOne := fmt.Sprintf(namePrefix, f.Namespace.Name, "one")
+		secretKeyTwo := fmt.Sprintf(namePrefix, f.Namespace.Name, "two")
+		secretKeyThree := fmt.Sprintf(namePrefix, f.Namespace.Name, "three")
+		secretValue := "{\"foo1\":\"foo1-val\",\"bar1\":\"bar1-val\"}"
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyTwo: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyThree: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf("_e2e_find_name_%s_one", f.Namespace.Name):   []byte(secretValue),
+				fmt.Sprintf("_e2e_find_name_%s_two", f.Namespace.Name):   []byte(secretValue),
+				fmt.Sprintf("_e2e_find_name_%s_three", f.Namespace.Name): []byte(secretValue),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Tags: map[string]string{
+						"test": f.Namespace.Name,
+					},
+				},
+			},
+		}
+	}
+}
+
+// This case creates multiple secrets with simple key/value pairs
+// this is special because parameter store requires a leading "/" in the name.
+func FindByTagWithPath(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by tags with path prefix", func(tc *framework.TestCase) {
+		const namePrefix = "/e2e/find/name/%s/%s"
+		pathPrefix := "/foobar"
+		secretKeyOne := fmt.Sprintf(namePrefix, f.Namespace.Name, "one")
+		secretKeyTwo := fmt.Sprintf("%s/%s/%s", pathPrefix, f.Namespace.Name, "two")
+		secretKeyThree := fmt.Sprintf("%s/%s/%s", pathPrefix, f.Namespace.Name, "three")
+		secretValue := "{\"foo1\":\"foo1-val\",\"bar1\":\"bar1-val\"}"
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyTwo: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyThree: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf("_foobar_%s_two", f.Namespace.Name):   []byte(secretValue),
+				fmt.Sprintf("_foobar_%s_three", f.Namespace.Name): []byte(secretValue),
+			},
+		}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Path: &pathPrefix,
+					Tags: map[string]string{
+						"test": f.Namespace.Name,
+					},
+				},
+			},
+		}
+	}
+}

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

@@ -41,5 +41,11 @@ var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
 		Entry(common.SSHKeySyncDataProperty(f)),
 		Entry(common.SyncWithoutTargetName(f)),
 		Entry(common.JSONDataWithoutTargetName(f)),
+
+		// These are specific to parameterstore
+		Entry(FindByName(f)),
+		Entry(FindByNameWithPath(f)),
+		Entry(FindByTag(f)),
+		Entry(FindByTagWithPath(f)),
 	)
 })

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

@@ -47,6 +47,10 @@ var _ = Describe("[awsmanaged] IRSA via referenced service account", Label("aws"
 		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),
+		framework.Compose(awscommon.WithReferencedIRSA, f, FindByName, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, FindByNameWithPath, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, FindByTag, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, FindByTagWithPath, awscommon.UseClusterSecretStore),
 	)
 })
 
@@ -83,5 +87,9 @@ var _ = Describe("[awsmanaged] with mounted IRSA", Label("aws", "parameterstore"
 		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),
+		framework.Compose(awscommon.WithMountedIRSA, f, FindByName, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, FindByNameWithPath, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, FindByTag, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, FindByTagWithPath, awscommon.UseMountedIRSAStore),
 	)
 })

+ 10 - 2
e2e/suite/aws/parameterstore/provider.go

@@ -95,12 +95,20 @@ func NewFromEnv(f *framework.Framework) *Provider {
 }
 
 // CreateSecret creates a secret at the provider.
-func (s *Provider) CreateSecret(key, val string) {
+func (s *Provider) CreateSecret(key string, val framework.SecretEntry) {
+	pmTags := make([]*ssm.Tag, 0)
+	for k, v := range val.Tags {
+		pmTags = append(pmTags, &ssm.Tag{
+			Key:   aws.String(k),
+			Value: aws.String(v),
+		})
+	}
 	_, err := s.client.PutParameter(&ssm.PutParameterInput{
 		Name:     aws.String(key),
-		Value:    aws.String(val),
+		Value:    aws.String(val.Value),
 		DataType: aws.String("text"),
 		Type:     aws.String("String"),
+		Tags:     pmTags,
 	})
 	Expect(err).ToNot(HaveOccurred())
 }

+ 11 - 2
e2e/suite/aws/secretsmanager/provider.go

@@ -96,7 +96,15 @@ func NewFromEnv(f *framework.Framework) *Provider {
 }
 
 // CreateSecret creates a secret at the provider.
-func (s *Provider) CreateSecret(key, val string) {
+func (s *Provider) CreateSecret(key string, val framework.SecretEntry) {
+	smTags := make([]*secretsmanager.Tag, 0)
+	for k, v := range val.Tags {
+		smTags = append(smTags, &secretsmanager.Tag{
+			Key:   aws.String(k),
+			Value: aws.String(v),
+		})
+	}
+
 	// 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
@@ -105,7 +113,8 @@ func (s *Provider) CreateSecret(key, val string) {
 		log.Logf("creating secret %s / attempts left: %d", key, attempts)
 		_, err := s.client.CreateSecret(&secretsmanager.CreateSecretInput{
 			Name:         aws.String(key),
-			SecretString: aws.String(val),
+			SecretString: aws.String(val.Value),
+			Tags:         smTags,
 		})
 		if err == nil {
 			return

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

@@ -41,5 +41,9 @@ var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
 		Entry(common.SSHKeySyncDataProperty(f)),
 		Entry(common.SyncWithoutTargetName(f)),
 		Entry(common.JSONDataWithoutTargetName(f)),
+		Entry(common.FindByName(f)),
+		Entry(common.FindByNameWithPath(f)),
+		Entry(common.FindByTag(f)),
+		Entry(common.FindByTagWithPath(f)),
 	)
 })

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

@@ -47,6 +47,10 @@ var _ = Describe("[awsmanaged] IRSA via referenced service account", Label("aws"
 		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),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.FindByName, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.FindByNameWithPath, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.FindByTag, awscommon.UseClusterSecretStore),
+		framework.Compose(awscommon.WithReferencedIRSA, f, common.FindByTagWithPath, awscommon.UseClusterSecretStore),
 	)
 })
 
@@ -83,5 +87,9 @@ var _ = Describe("[awsmanaged] with mounted IRSA", Label("aws", "secretsmanager"
 		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),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.FindByName, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.FindByNameWithPath, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.FindByTag, awscommon.UseMountedIRSAStore),
+		framework.Compose(awscommon.WithMountedIRSA, f, common.FindByTagWithPath, awscommon.UseMountedIRSAStore),
 	)
 })

+ 2 - 2
e2e/suite/azure/provider.go

@@ -76,13 +76,13 @@ func newFromEnv(f *framework.Framework) *azureProvider {
 	return newazureProvider(f, clientID, clientSecret, tenantID, vaultURL)
 }
 
-func (s *azureProvider) CreateSecret(key, val string) {
+func (s *azureProvider) CreateSecret(key string, val framework.SecretEntry) {
 	_, err := s.client.SetSecret(
 		context.Background(),
 		s.vaultURL,
 		key,
 		keyvault.SecretSetParameters{
-			Value: &val,
+			Value: &val.Value,
 			SecretAttributes: &keyvault.SecretAttributes{
 				RecoveryLevel: keyvault.Purgeable,
 				Enabled:       utilpointer.BoolPtr(true),

+ 30 - 65
e2e/suite/common/common.go

@@ -26,8 +26,11 @@ const (
 	// Constants.
 	dockerConfigExampleName    = "docker-config-example"
 	dockerConfigJSONKey        = ".dockerconfigjson"
-	mysecretToStringTemplating = "{{ .mysecret | toString }}"
+	mysecretToStringTemplating = "{{ .mysecret }}"
 	sshPrivateKey              = "ssh-privatekey"
+
+	secretValue1 = "{\"foo1\":\"foo1-val\",\"bar1\":\"bar1-val\"}"
+	secretValue2 = "{\"foo2\":\"foo2-val\",\"bar2\":\"bar2-val\"}"
 )
 
 // This case creates multiple secrets with simple key/value pairs and syncs them using multiple .Spec.Data blocks.
@@ -37,9 +40,9 @@ func SimpleDataSync(f *framework.Framework) (string, func(*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.Secrets = map[string]framework.SecretEntry{
+			secretKey1: {Value: secretValue},
+			secretKey2: {Value: secretValue},
 		}
 		tc.ExpectedSecret = &v1.Secret{
 			Type: v1.SecretTypeOpaque,
@@ -71,8 +74,8 @@ func SyncWithoutTargetName(f *framework.Framework) (string, func(*framework.Test
 	return "[common] should sync with empty target name.", func(tc *framework.TestCase) {
 		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
 		secretValue := "bar"
-		tc.Secrets = map[string]string{
-			secretKey1: secretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKey1: {Value: secretValue},
 		}
 		tc.ExpectedSecret = &v1.Secret{
 			Type: v1.SecretTypeOpaque,
@@ -98,11 +101,9 @@ func JSONDataWithProperty(f *framework.Framework) (string, func(*framework.TestC
 	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.Secrets = map[string]framework.SecretEntry{
+			secretKey1: {Value: secretValue1},
+			secretKey2: {Value: secretValue2},
 		}
 		tc.ExpectedSecret = &v1.Secret{
 			Type: v1.SecretTypeOpaque,
@@ -136,8 +137,8 @@ func JSONDataWithoutTargetName(f *framework.Framework) (string, func(*framework.
 	return "[common] should sync with empty target name, using json.", func(tc *framework.TestCase) {
 		secretKey := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
 		secretValue := "{\"foo\":\"foo-val\",\"bar\":\"bar-val\"}"
-		tc.Secrets = map[string]string{
-			secretKey: secretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKey: {Value: secretValue},
 		}
 		tc.ExpectedSecret = &v1.Secret{
 			Type: v1.SecretTypeOpaque,
@@ -164,11 +165,9 @@ func JSONDataWithTemplate(f *framework.Framework) (string, func(*framework.TestC
 	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.Secrets = map[string]framework.SecretEntry{
+			secretKey1: {Value: secretValue1},
+			secretKey2: {Value: secretValue2},
 		}
 		tc.ExpectedSecret = &v1.Secret{
 			Type: v1.SecretTypeOpaque,
@@ -194,7 +193,7 @@ func JSONDataWithTemplate(f *framework.Framework) (string, func(*framework.TestC
 				},
 			},
 			Data: map[string]string{
-				"my-data": "executed: {{ .one | toString }}|{{ .two | toString }}",
+				"my-data": "executed: {{ .one }}|{{ .two }}",
 			},
 		}
 		tc.ExternalSecret.Spec.Data = []esv1beta1.ExternalSecretData{
@@ -216,40 +215,6 @@ func JSONDataWithTemplate(f *framework.Framework) (string, func(*framework.TestC
 	}
 }
 
-// This case creates two secrets with json values and syncs them using a single .Spec.DataFrom.Find block.
-func JSONFindSync(f *framework.Framework) (string, func(*framework.TestCase)) {
-	return "[common] should sync secrets with dataFrom.Find.Name", func(tc *framework.TestCase) {
-		secretKey1 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one")
-		secretKey2 := fmt.Sprintf("%s-%s", f.Namespace.Name, "one-too")
-		targetSecretKey1 := "name"
-		targetSecretValue1 := "great-name"
-		targetSecretKey2 := "surname"
-		targetSecretValue2 := "great-surname"
-		secretValue1 := fmt.Sprintf("{\"%s\":\"%s\"}", targetSecretKey1, targetSecretValue1)
-		secretValue2 := fmt.Sprintf("{\"%s\":\"%s\"}", targetSecretKey2, targetSecretValue2)
-		tc.Secrets = map[string]string{
-			secretKey1: secretValue1,
-			secretKey2: secretValue2,
-		}
-		tc.ExpectedSecret = &v1.Secret{
-			Type: v1.SecretTypeOpaque,
-			Data: map[string][]byte{
-				secretKey1: []byte(secretValue1),
-				secretKey2: []byte(secretValue2),
-			},
-		}
-		tc.ExternalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
-			{
-				Find: &esv1beta1.ExternalSecretFind{
-					Name: &esv1beta1.FindName{
-						RegExp: "one",
-					},
-				},
-			},
-		}
-	}
-}
-
 // 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) {
@@ -259,8 +224,8 @@ func JSONDataFromSync(f *framework.Framework) (string, func(*framework.TestCase)
 		targetSecretKey2 := "surname"
 		targetSecretValue2 := "great-surname"
 		secretValue := fmt.Sprintf("{ %q: %q, %q: %q }", targetSecretKey1, targetSecretValue1, targetSecretKey2, targetSecretValue2)
-		tc.Secrets = map[string]string{
-			secretKey1: secretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKey1: {Value: secretValue},
 		}
 		tc.ExpectedSecret = &v1.Secret{
 			Type: v1.SecretTypeOpaque,
@@ -299,8 +264,8 @@ func NestedJSONWithGJSON(f *framework.Framework) (string, func(*framework.TestCa
 					{"first": "Jane", "last": "Murphy"}
 				]
 			}`, targetSecretValue1, targetSecretValue2)
-		tc.Secrets = map[string]string{
-			secretKey1: secretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKey1: {Value: secretValue},
 		}
 		tc.ExpectedSecret = &v1.Secret{
 			Type: v1.SecretTypeOpaque,
@@ -336,8 +301,8 @@ func DockerJSONConfig(f *framework.Framework) (string, func(*framework.TestCase)
 		cloudSecretName := fmt.Sprintf("%s-%s", f.Namespace.Name, dockerConfigExampleName)
 		dockerconfig := `{"auths":{"https://index.docker.io/v1/": {"auth": "c3R...zE2"}}}`
 		cloudSecretValue := fmt.Sprintf(`{"dockerconfig": %s}`, dockerconfig)
-		tc.Secrets = map[string]string{
-			cloudSecretName: cloudSecretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			cloudSecretName: {Value: cloudSecretValue},
 		}
 
 		tc.ExpectedSecret = &v1.Secret{
@@ -374,8 +339,8 @@ func DataPropertyDockerconfigJSON(f *framework.Framework) (string, func(*framewo
 		dockerconfigString := `"{\"auths\":{\"https://index.docker.io/v1/\": {\"auth\": \"c3R...zE2\"}}}"`
 		dockerconfig := `{"auths":{"https://index.docker.io/v1/": {"auth": "c3R...zE2"}}}`
 		cloudSecretValue := fmt.Sprintf(`{"dockerconfig": %s}`, dockerconfigString)
-		tc.Secrets = map[string]string{
-			cloudSecretName: cloudSecretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			cloudSecretName: {Value: cloudSecretValue},
 		}
 
 		tc.ExpectedSecret = &v1.Secret{
@@ -448,8 +413,8 @@ func SSHKeySync(f *framework.Framework) (string, func(*framework.TestCase)) {
 		OkcGfqTaOoz2KVAAAAFGtpYW5AREVTS1RPUC1TNFI5S1JQAQIDBAUG
 		-----END OPENSSH PRIVATE KEY-----`
 
-		tc.Secrets = map[string]string{
-			sshSecretName: sshSecretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			sshSecretName: {Value: sshSecretValue},
 		}
 
 		tc.ExpectedSecret = &v1.Secret{
@@ -520,8 +485,8 @@ func SSHKeySyncDataProperty(f *framework.Framework) (string, func(*framework.Tes
 		OkcGfqTaOoz2KVAAAAFGtpYW5AREVTS1RPUC1TNFI5S1JQAQIDBAUG
 		-----END OPENSSH PRIVATE KEY-----`
 		cloudSecretValue := fmt.Sprintf(`{"ssh-auth": %q}`, SSHKey)
-		tc.Secrets = map[string]string{
-			cloudSecretName: cloudSecretValue,
+		tc.Secrets = map[string]framework.SecretEntry{
+			cloudSecretName: {Value: cloudSecretValue},
 		}
 
 		tc.ExpectedSecret = &v1.Secret{

+ 94 - 0
e2e/suite/common/find_by_name.go

@@ -0,0 +1,94 @@
+/*
+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"
+	"time"
+
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"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.
+func FindByName(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by name using .DataFrom[]", func(tc *framework.TestCase) {
+		const namePrefix = "e2e_find_name_%s_%s"
+		secretKeyOne := fmt.Sprintf(namePrefix, f.Namespace.Name, "one")
+		secretKeyTwo := fmt.Sprintf(namePrefix, f.Namespace.Name, "two")
+		secretKeyThree := fmt.Sprintf(namePrefix, f.Namespace.Name, "three")
+		secretValue := "{\"foo1\":\"foo1-val\"}"
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne:   {Value: secretValue},
+			secretKeyTwo:   {Value: secretValue},
+			secretKeyThree: {Value: secretValue},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				secretKeyOne:   []byte(secretValue),
+				secretKeyTwo:   []byte(secretValue),
+				secretKeyThree: []byte(secretValue),
+			},
+		}
+		// AWS Secrets Manager is eventually consistent
+		// specifying a low refresh interval avoids flaky tests
+		tc.ExternalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second * 5}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Name: &esapi.FindName{
+						RegExp: fmt.Sprintf("e2e_find_name_%s.+", f.Namespace.Name),
+					},
+				},
+			},
+		}
+	}
+}
+
+func FindByNameWithPath(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by name with path", func(tc *framework.TestCase) {
+		secretKeyOne := fmt.Sprintf("e2e-find-name-%s-one", f.Namespace.Name)
+		secretKeyTwo := fmt.Sprintf("%s-two", f.Namespace.Name)
+		secretKeythree := fmt.Sprintf("%s-three", f.Namespace.Name)
+		secretValue := "{\"foo1\":\"foo1-val\"}"
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne:   {Value: secretValue},
+			secretKeyTwo:   {Value: secretValue},
+			secretKeythree: {Value: secretValue},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf("%s-two", f.Namespace.Name):   []byte(secretValue),
+				fmt.Sprintf("%s-three", f.Namespace.Name): []byte(secretValue),
+			},
+		}
+		// AWS Secrets Manager is eventually consistent
+		// specifying a low refresh interval avoids flaky tests
+		tc.ExternalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second * 5}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Path: &f.Namespace.Name,
+					Name: &esapi.FindName{
+						RegExp: ".+",
+					},
+				},
+			},
+		}
+	}
+}

+ 120 - 0
e2e/suite/common/find_by_tags.go

@@ -0,0 +1,120 @@
+/*
+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"
+	"time"
+
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"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.
+func FindByTag(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by tags using .DataFrom[]", func(tc *framework.TestCase) {
+		const namePrefix = "e2e-find-name-%s-%s"
+		secretKeyOne := fmt.Sprintf(namePrefix, f.Namespace.Name, "one")
+		secretKeyTwo := fmt.Sprintf(namePrefix, f.Namespace.Name, "two")
+		secretKeyThree := fmt.Sprintf(namePrefix, f.Namespace.Name, "three")
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne: {
+				Value: secretValue1,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyTwo: {
+				Value: secretValue1,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyThree: {
+				Value: secretValue1,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf("e2e-find-name-%s-one", f.Namespace.Name):   []byte(secretValue1),
+				fmt.Sprintf("e2e-find-name-%s-two", f.Namespace.Name):   []byte(secretValue1),
+				fmt.Sprintf("e2e-find-name-%s-three", f.Namespace.Name): []byte(secretValue1),
+			},
+		}
+		// AWS Secrets Manager is eventually consistent
+		// specifying a low refresh interval avoids flaky tests
+		tc.ExternalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second * 5}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Tags: map[string]string{
+						"test": f.Namespace.Name,
+					},
+				},
+			},
+		}
+	}
+}
+
+// This case creates multiple secrets with simple key/value pairs
+// this is special because parameter store requires a leading "/" in the name.
+func FindByTagWithPath(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[common] should find secrets by tags with path prefix", func(tc *framework.TestCase) {
+		pathPrefix := "foobar"
+		secretKeyOne := fmt.Sprintf("e2e-find-name-%s-%s", f.Namespace.Name, "one")
+		secretKeyTwo := fmt.Sprintf("%s-%s-%s", pathPrefix, f.Namespace.Name, "two")
+		secretKeyThree := fmt.Sprintf("%s-%s-%s", pathPrefix, f.Namespace.Name, "three")
+		secretValue := "{\"foo1\":\"foo1-val\",\"bar1\":\"bar1-val\"}"
+		tc.Secrets = map[string]framework.SecretEntry{
+			secretKeyOne: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyTwo: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+			secretKeyThree: {
+				Value: secretValue,
+				Tags: map[string]string{
+					"test": f.Namespace.Name,
+				}},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				fmt.Sprintf("foobar-%s-two", f.Namespace.Name):   []byte(secretValue),
+				fmt.Sprintf("foobar-%s-three", f.Namespace.Name): []byte(secretValue),
+			},
+		}
+		// AWS Secrets Manager is eventually consistent
+		// specifying a low refresh interval avoids flaky tests
+		tc.ExternalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second * 5}
+		tc.ExternalSecret.Spec.DataFrom = []esapi.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esapi.ExternalSecretFind{
+					Path: &pathPrefix,
+					Tags: map[string]string{
+						"test": f.Namespace.Name,
+					},
+				},
+			},
+		}
+	}
+}

+ 2 - 2
e2e/suite/gcp/gcp.go

@@ -122,8 +122,8 @@ x6HaRh+EUwU51von6M9lEF9/p5Q=
 	emptyCACerts := []*x509.Certificate{}
 	p12Cert, _ := p12.Encode(rand.Reader, privkey, cert, emptyCACerts, "")
 
-	tc.Secrets = map[string]string{
-		cloudSecretName: string(p12Cert),
+	tc.Secrets = map[string]framework.SecretEntry{
+		cloudSecretName: {Value: string(p12Cert)},
 	}
 
 	tc.ExpectedSecret = &v1.Secret{

+ 2 - 2
e2e/suite/gcp/provider.go

@@ -103,7 +103,7 @@ func (s *GcpProvider) getClient(ctx context.Context) (client *secretmanager.Clie
 	return client, err
 }
 
-func (s *GcpProvider) CreateSecret(key, val string) {
+func (s *GcpProvider) CreateSecret(key string, val framework.SecretEntry) {
 	ctx := context.Background()
 	client, err := s.getClient(ctx)
 	Expect(err).ToNot(HaveOccurred())
@@ -125,7 +125,7 @@ func (s *GcpProvider) CreateSecret(key, val string) {
 	addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
 		Parent: secret.Name,
 		Payload: &secretmanagerpb.SecretPayload{
-			Data: []byte(val),
+			Data: []byte(val.Value),
 		},
 	}
 	_, err = client.AddSecretVersion(ctx, addSecretVersionReq)

+ 2 - 2
e2e/suite/gitlab/provider.go

@@ -54,7 +54,7 @@ func newFromEnv(f *framework.Framework) *gitlabProvider {
 	return newGitlabProvider(f, credentials, projectID)
 }
 
-func (s *gitlabProvider) CreateSecret(key, val string) {
+func (s *gitlabProvider) CreateSecret(key string, val framework.SecretEntry) {
 	// **Open the client
 	client, err := gitlab.NewClient(s.credentials)
 	Expect(err).ToNot(HaveOccurred())
@@ -66,7 +66,7 @@ func (s *gitlabProvider) CreateSecret(key, val string) {
 
 	opt := gitlab.CreateProjectVariableOptions{
 		Key:              &variableKey,
-		Value:            &variableValue,
+		Value:            &variableValue.Value,
 		VariableType:     nil,
 		Protected:        nil,
 		Masked:           nil,

+ 2 - 2
e2e/suite/oracle/provider.go

@@ -68,7 +68,7 @@ func newFromEnv(f *framework.Framework) *oracleProvider {
 	return newOracleProvider(f, tenancy, user, region, fingerprint, privateKey)
 }
 
-func (p *oracleProvider) CreateSecret(key, val string) {
+func (p *oracleProvider) CreateSecret(key string, val framework.SecretEntry) {
 	configurationProvider := common.NewRawConfigurationProvider(p.tenancy, p.user, p.region, p.fingerprint, p.privateKey, nil)
 	client, err := vault.NewVaultsClientWithConfigurationProvider(configurationProvider)
 	Expect(err).ToNot(HaveOccurred())
@@ -76,7 +76,7 @@ func (p *oracleProvider) CreateSecret(key, val string) {
 	vmssecretrequest.SecretName = utilpointer.StringPtr(secretName)
 	vmssecretrequest.SecretContent = vault.Base64SecretContentDetails{
 		Name:    utilpointer.StringPtr(key),
-		Content: utilpointer.StringPtr(val),
+		Content: utilpointer.StringPtr(val.Value),
 	}
 	_, err = client.CreateSecret(p.ctx, vmssecretrequest)
 	Expect(err).ToNot(HaveOccurred())

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

@@ -39,7 +39,7 @@ func newProvider(f *framework.Framework) *templateProvider {
 	return prov
 }
 
-func (s *templateProvider) CreateSecret(key, val string) {
+func (s *templateProvider) CreateSecret(key string, val framework.SecretEntry) {
 	// noop: this provider implements static key/value pairs
 }
 

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

@@ -61,14 +61,14 @@ func newVaultProvider(f *framework.Framework) *vaultProvider {
 }
 
 // CreateSecret creates a secret in both kv v1 and v2 provider.
-func (s *vaultProvider) CreateSecret(key, val string) {
+func (s *vaultProvider) CreateSecret(key string, val framework.SecretEntry) {
 	req := s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", key))
-	req.BodyBytes = []byte(fmt.Sprintf(`{"data": %s}`, val))
+	req.BodyBytes = []byte(fmt.Sprintf(`{"data": %s}`, val.Value))
 	_, err := s.client.RawRequestWithContext(context.Background(), req)
 	Expect(err).ToNot(HaveOccurred())
 
 	req = s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret_v1/%s", key))
-	req.BodyBytes = []byte(val)
+	req.BodyBytes = []byte(val.Value)
 	_, err = s.client.RawRequestWithContext(context.Background(), req)
 	Expect(err).ToNot(HaveOccurred())
 }

+ 13 - 13
e2e/suite/vault/vault.go

@@ -40,21 +40,21 @@ var _ = Describe("[vault]", Label("vault"), func() {
 	DescribeTable("sync secrets",
 		framework.TableFunc(f, prov),
 		// uses token auth
-		framework.Compose(withTokenAuth, f, common.JSONFindSync, useTokenAuth),
+		framework.Compose(withTokenAuth, f, common.FindByName, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.JSONDataFromSync, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.JSONDataWithProperty, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.JSONDataWithTemplate, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.DataPropertyDockerconfigJSON, useTokenAuth),
 		framework.Compose(withTokenAuth, f, common.JSONDataWithoutTargetName, useTokenAuth),
 		// use cert auth
-		framework.Compose(withCertAuth, f, common.JSONFindSync, useCertAuth),
+		framework.Compose(withCertAuth, f, common.FindByName, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataFromSync, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataWithProperty, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataWithTemplate, useCertAuth),
 		framework.Compose(withCertAuth, f, common.DataPropertyDockerconfigJSON, useCertAuth),
 		framework.Compose(withCertAuth, f, common.JSONDataWithoutTargetName, useCertAuth),
 		// use approle auth
-		framework.Compose(withApprole, f, common.JSONFindSync, useApproleAuth),
+		framework.Compose(withApprole, f, common.FindByName, useApproleAuth),
 		framework.Compose(withApprole, f, common.JSONDataFromSync, useApproleAuth),
 		framework.Compose(withApprole, f, common.JSONDataWithProperty, useApproleAuth),
 		framework.Compose(withApprole, f, common.JSONDataWithTemplate, useApproleAuth),
@@ -67,14 +67,14 @@ var _ = Describe("[vault]", Label("vault"), func() {
 		framework.Compose(withV1, f, common.DataPropertyDockerconfigJSON, useV1Provider),
 		framework.Compose(withV1, f, common.JSONDataWithoutTargetName, useV1Provider),
 		// use jwt provider
-		framework.Compose(withJWT, f, common.JSONFindSync, useJWTProvider),
+		framework.Compose(withJWT, f, common.FindByName, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataFromSync, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataWithProperty, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataWithTemplate, useJWTProvider),
 		framework.Compose(withJWT, f, common.DataPropertyDockerconfigJSON, useJWTProvider),
 		framework.Compose(withJWT, f, common.JSONDataWithoutTargetName, useJWTProvider),
 		// use kubernetes provider
-		framework.Compose(withK8s, f, common.JSONFindSync, useKubernetesProvider),
+		framework.Compose(withK8s, f, common.FindByName, useKubernetesProvider),
 		framework.Compose(withK8s, f, common.JSONDataFromSync, useKubernetesProvider),
 		framework.Compose(withK8s, f, common.JSONDataWithProperty, useKubernetesProvider),
 		framework.Compose(withK8s, f, common.JSONDataWithTemplate, useKubernetesProvider),
@@ -117,8 +117,8 @@ const jsonVal = `{"foo":{"nested":{"bar":"mysecret","baz":"bang"}}}`
 // when no property is set it should return the json-encoded at path.
 func testJSONWithoutProperty(tc *framework.TestCase) {
 	secretKey := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "json")
-	tc.Secrets = map[string]string{
-		secretKey: jsonVal,
+	tc.Secrets = map[string]framework.SecretEntry{
+		secretKey: {Value: jsonVal},
 	}
 	tc.ExpectedSecret = &v1.Secret{
 		Type: v1.SecretTypeOpaque,
@@ -140,8 +140,8 @@ func testJSONWithoutProperty(tc *framework.TestCase) {
 func testJSONWithProperty(tc *framework.TestCase) {
 	secretKey := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "json")
 	expectedVal := `{"bar":"mysecret","baz":"bang"}`
-	tc.Secrets = map[string]string{
-		secretKey: jsonVal,
+	tc.Secrets = map[string]framework.SecretEntry{
+		secretKey: {Value: jsonVal},
 	}
 	tc.ExpectedSecret = &v1.Secret{
 		Type: v1.SecretTypeOpaque,
@@ -164,8 +164,8 @@ func testJSONWithProperty(tc *framework.TestCase) {
 // note: it should json-encode if a value contains nested data
 func testDataFromJSONWithoutProperty(tc *framework.TestCase) {
 	secretKey := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "json")
-	tc.Secrets = map[string]string{
-		secretKey: jsonVal,
+	tc.Secrets = map[string]framework.SecretEntry{
+		secretKey: {Value: jsonVal},
 	}
 	tc.ExpectedSecret = &v1.Secret{
 		Type: v1.SecretTypeOpaque,
@@ -185,8 +185,8 @@ func testDataFromJSONWithoutProperty(tc *framework.TestCase) {
 // when property is set it should extract values with dataFrom at the given path.
 func testDataFromJSONWithProperty(tc *framework.TestCase) {
 	secretKey := fmt.Sprintf("%s-%s", tc.Framework.Namespace.Name, "json")
-	tc.Secrets = map[string]string{
-		secretKey: jsonVal,
+	tc.Secrets = map[string]framework.SecretEntry{
+		secretKey: {Value: jsonVal},
 	}
 	tc.ExpectedSecret = &v1.Secret{
 		Type: v1.SecretTypeOpaque,

+ 40 - 0
pkg/find/find.go

@@ -0,0 +1,40 @@
+/*
+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 find
+
+import (
+	"fmt"
+	"regexp"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+type Matcher struct {
+	re *regexp.Regexp
+}
+
+func New(findName esv1beta1.FindName) (*Matcher, error) {
+	cmp, err := regexp.Compile(findName.RegExp)
+	if err != nil {
+		return nil, fmt.Errorf("could not compile find.name.regexp [%s]: %w", findName.RegExp, err)
+	}
+	return &Matcher{
+		re: cmp,
+	}, nil
+}
+
+func (m *Matcher) MatchName(name string) bool {
+	return m.re.MatchString(name)
+}

+ 4 - 0
pkg/provider/aws/parameterstore/fake/fake.go

@@ -29,6 +29,10 @@ func (sm *Client) GetParameter(in *ssm.GetParameterInput) (*ssm.GetParameterOutp
 	return sm.valFn(in)
 }
 
+func (sm *Client) DescribeParameters(*ssm.DescribeParametersInput) (*ssm.DescribeParametersOutput, error) {
+	return nil, nil
+}
+
 func (sm *Client) WithValue(in *ssm.GetParameterInput, val *ssm.GetParameterOutput, err error) {
 	sm.valFn = func(paramIn *ssm.GetParameterInput) (*ssm.GetParameterOutput, error) {
 		if !cmp.Equal(paramIn, in) {

+ 112 - 6
pkg/provider/aws/parameterstore/parameterstore.go

@@ -16,16 +16,19 @@ package parameterstore
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/ssm"
 	"github.com/tidwall/gjson"
-	ctrl "sigs.k8s.io/controller-runtime"
+	utilpointer "k8s.io/utils/pointer"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/pkg/find"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
+	"github.com/external-secrets/external-secrets/pkg/utils"
 )
 
 // ParameterStore is a provider for AWS ParameterStore.
@@ -38,9 +41,12 @@ type ParameterStore struct {
 // see: https://docs.aws.amazon.com/sdk-for-go/api/service/ssm/ssmiface/
 type PMInterface interface {
 	GetParameter(*ssm.GetParameterInput) (*ssm.GetParameterOutput, error)
+	DescribeParameters(*ssm.DescribeParametersInput) (*ssm.DescribeParametersOutput, error)
 }
 
-var log = ctrl.Log.WithName("provider").WithName("aws").WithName("parameterstore")
+const (
+	errUnexpectedFindOperator = "unexpected find operator"
+)
 
 // New constructs a ParameterStore Provider that is specific to a store.
 func New(sess *session.Session) (*ParameterStore, error) {
@@ -52,13 +58,114 @@ func New(sess *session.Session) (*ParameterStore, error) {
 
 // Empty GetAllSecrets.
 func (pm *ParameterStore) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
-	// TO be implemented
-	return nil, fmt.Errorf("GetAllSecrets not implemented")
+	if ref.Name != nil {
+		return pm.findByName(ref)
+	}
+	if ref.Tags != nil {
+		return pm.findByTags(ref)
+	}
+	return nil, errors.New(errUnexpectedFindOperator)
+}
+
+func (pm *ParameterStore) findByName(ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	matcher, err := find.New(*ref.Name)
+	if err != nil {
+		return nil, err
+	}
+	pathFilter := make([]*ssm.ParameterStringFilter, 0)
+	if ref.Path != nil {
+		pathFilter = append(pathFilter, &ssm.ParameterStringFilter{
+			Key:    aws.String("Path"),
+			Option: aws.String("Recursive"),
+			Values: []*string{ref.Path},
+		})
+	}
+	data := make(map[string][]byte)
+	var nextToken *string
+	for {
+		it, err := pm.client.DescribeParameters(&ssm.DescribeParametersInput{
+			NextToken:        nextToken,
+			ParameterFilters: pathFilter,
+		})
+		if err != nil {
+			return nil, err
+		}
+		for _, param := range it.Parameters {
+			if !matcher.MatchName(*param.Name) {
+				continue
+			}
+			err = pm.fetchAndSet(data, *param.Name)
+			if err != nil {
+				return nil, err
+			}
+		}
+		nextToken = it.NextToken
+		if nextToken == nil {
+			break
+		}
+	}
+
+	return utils.ConvertKeys(ref.ConversionStrategy, data)
+}
+
+func (pm *ParameterStore) findByTags(ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	filters := make([]*ssm.ParameterStringFilter, 0)
+	for k, v := range ref.Tags {
+		filters = append(filters, &ssm.ParameterStringFilter{
+			Key:    utilpointer.StringPtr(fmt.Sprintf("tag:%s", k)),
+			Values: []*string{utilpointer.StringPtr(v)},
+			Option: utilpointer.StringPtr("Equals"),
+		})
+	}
+
+	if ref.Path != nil {
+		filters = append(filters, &ssm.ParameterStringFilter{
+			Key:    aws.String("Path"),
+			Option: aws.String("Recursive"),
+			Values: []*string{ref.Path},
+		})
+	}
+
+	data := make(map[string][]byte)
+	var nextToken *string
+	for {
+		it, err := pm.client.DescribeParameters(&ssm.DescribeParametersInput{
+			ParameterFilters: filters,
+			NextToken:        nextToken,
+		})
+		if err != nil {
+			return nil, err
+		}
+		for _, param := range it.Parameters {
+			err = pm.fetchAndSet(data, *param.Name)
+			if err != nil {
+				return nil, err
+			}
+		}
+		nextToken = it.NextToken
+		if nextToken == nil {
+			break
+		}
+	}
+
+	return utils.ConvertKeys(ref.ConversionStrategy, data)
+}
+
+func (pm *ParameterStore) fetchAndSet(data map[string][]byte, name string) error {
+	out, err := pm.client.GetParameter(&ssm.GetParameterInput{
+		Name:           utilpointer.StringPtr(name),
+		WithDecryption: aws.Bool(true),
+	})
+	if err != nil {
+		return util.SanitizeErr(err)
+	}
+
+	data[name] = []byte(*out.Parameter.Value)
+	return nil
 }
 
 // GetSecret returns a single secret from the provider.
 func (pm *ParameterStore) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
-	log.Info("fetching secret value", "key", ref.Key)
 	out, err := pm.client.GetParameter(&ssm.GetParameterInput{
 		Name:           &ref.Key,
 		WithDecryption: aws.Bool(true),
@@ -81,7 +188,6 @@ func (pm *ParameterStore) GetSecret(ctx context.Context, ref esv1beta1.ExternalS
 
 // GetSecretMap returns multiple k/v pairs from the provider.
 func (pm *ParameterStore) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
-	log.Info("fetching secret map", "key", ref.Key)
 	data, err := pm.GetSecret(ctx, ref)
 	if err != nil {
 		return nil, err

+ 129 - 2
pkg/provider/aws/secretsmanager/secretsmanager.go

@@ -17,15 +17,19 @@ package secretsmanager
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 
 	"github.com/aws/aws-sdk-go/aws/session"
 	awssm "github.com/aws/aws-sdk-go/service/secretsmanager"
 	"github.com/tidwall/gjson"
+	utilpointer "k8s.io/utils/pointer"
 	ctrl "sigs.k8s.io/controller-runtime"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/pkg/find"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
+	"github.com/external-secrets/external-secrets/pkg/utils"
 )
 
 // SecretsManager is a provider for AWS SecretsManager.
@@ -39,8 +43,13 @@ type SecretsManager struct {
 // see: https://docs.aws.amazon.com/sdk-for-go/api/service/secretsmanager/secretsmanageriface/
 type SMInterface interface {
 	GetSecretValue(*awssm.GetSecretValueInput) (*awssm.GetSecretValueOutput, error)
+	ListSecrets(*awssm.ListSecretsInput) (*awssm.ListSecretsOutput, error)
 }
 
+const (
+	errUnexpectedFindOperator = "unexpected find operator"
+)
+
 var log = ctrl.Log.WithName("provider").WithName("aws").WithName("secretsmanager")
 
 // New creates a new SecretsManager client.
@@ -78,8 +87,126 @@ func (sm *SecretsManager) fetch(_ context.Context, ref esv1beta1.ExternalSecretD
 
 // Empty GetAllSecrets.
 func (sm *SecretsManager) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
-	// TO be implemented
-	return nil, fmt.Errorf("GetAllSecrets not implemented")
+	if ref.Name != nil {
+		return sm.findByName(ctx, ref)
+	}
+	if len(ref.Tags) > 0 {
+		return sm.findByTags(ctx, ref)
+	}
+	return nil, errors.New(errUnexpectedFindOperator)
+}
+
+func (sm *SecretsManager) findByName(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	matcher, err := find.New(*ref.Name)
+	if err != nil {
+		return nil, err
+	}
+
+	filters := make([]*awssm.Filter, 0)
+	if ref.Path != nil {
+		filters = append(filters, &awssm.Filter{
+			Key: utilpointer.StringPtr(awssm.FilterNameStringTypeName),
+			Values: []*string{
+				ref.Path,
+			},
+		})
+	}
+
+	data := make(map[string][]byte)
+	var nextToken *string
+
+	for {
+		it, err := sm.client.ListSecrets(&awssm.ListSecretsInput{
+			Filters:   filters,
+			NextToken: nextToken,
+		})
+		if err != nil {
+			return nil, err
+		}
+		log.V(1).Info("aws sm findByName found", "secrets", len(it.SecretList))
+		for _, secret := range it.SecretList {
+			if !matcher.MatchName(*secret.Name) {
+				continue
+			}
+			log.V(1).Info("aws sm findByName matches", "name", *secret.Name)
+			err = sm.fetchAndSet(ctx, data, *secret.Name)
+			if err != nil {
+				return nil, err
+			}
+		}
+		nextToken = it.NextToken
+		if nextToken == nil {
+			break
+		}
+	}
+	return utils.ConvertKeys(ref.ConversionStrategy, data)
+}
+
+func (sm *SecretsManager) findByTags(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	filters := make([]*awssm.Filter, 0)
+	for k, v := range ref.Tags {
+		filters = append(filters, &awssm.Filter{
+			Key: utilpointer.StringPtr(awssm.FilterNameStringTypeTagKey),
+			Values: []*string{
+				utilpointer.StringPtr(k),
+			},
+		}, &awssm.Filter{
+			Key: utilpointer.StringPtr(awssm.FilterNameStringTypeTagValue),
+			Values: []*string{
+				utilpointer.StringPtr(v),
+			},
+		})
+	}
+
+	if ref.Path != nil {
+		filters = append(filters, &awssm.Filter{
+			Key: utilpointer.StringPtr(awssm.FilterNameStringTypeName),
+			Values: []*string{
+				ref.Path,
+			},
+		})
+	}
+
+	data := make(map[string][]byte)
+	var nextToken *string
+	for {
+		log.V(1).Info("aws sm findByTag", "nextToken", nextToken)
+		it, err := sm.client.ListSecrets(&awssm.ListSecretsInput{
+			Filters:   filters,
+			NextToken: nextToken,
+		})
+		if err != nil {
+			return nil, err
+		}
+		log.V(1).Info("aws sm findByTag found", "secrets", len(it.SecretList))
+		for _, secret := range it.SecretList {
+			err = sm.fetchAndSet(ctx, data, *secret.Name)
+			if err != nil {
+				return nil, err
+			}
+		}
+		nextToken = it.NextToken
+		if nextToken == nil {
+			break
+		}
+	}
+	return utils.ConvertKeys(ref.ConversionStrategy, data)
+}
+
+func (sm *SecretsManager) fetchAndSet(ctx context.Context, data map[string][]byte, name string) error {
+	sec, err := sm.fetch(ctx, esv1beta1.ExternalSecretDataRemoteRef{
+		Key: name,
+	})
+	if err != nil {
+		return err
+	}
+	if sec.SecretString != nil {
+		data[name] = []byte(*sec.SecretString)
+	}
+	if sec.SecretBinary != nil {
+		data[name] = sec.SecretBinary
+	}
+	return nil
 }
 
 // GetSecret returns a single secret from the provider.

+ 6 - 5
pkg/provider/vault/vault.go

@@ -23,7 +23,6 @@ import (
 	"fmt"
 	"net/http"
 	"os"
-	"regexp"
 	"strconv"
 	"strings"
 
@@ -37,6 +36,7 @@ import (
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/pkg/find"
 	"github.com/external-secrets/external-secrets/pkg/utils"
 )
 
@@ -282,11 +282,12 @@ func (v *client) findSecretsFromTags(ctx context.Context, candidates []string, t
 
 func (v *client) findSecretsFromName(ctx context.Context, candidates []string, ref esv1beta1.FindName, removeFromName string) (map[string][]byte, error) {
 	secrets := make(map[string][]byte)
+	matcher, err := find.New(ref)
+	if err != nil {
+		return nil, err
+	}
 	for _, name := range candidates {
-		ok, err := regexp.MatchString(ref.RegExp, name)
-		if err != nil {
-			return nil, err
-		}
+		ok := matcher.MatchName(name)
 		if ok {
 			secret, err := v.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: name})
 			if err != nil {

+ 24 - 22
pkg/utils/utils.go

@@ -38,29 +38,9 @@ func MergeByteMap(dst, src map[string][]byte) map[string][]byte {
 // ConvertKeys converts a secret map into a valid key.
 // Replaces any non-alphanumeric characters depending on convert strategy.
 func ConvertKeys(strategy esv1beta1.ExternalSecretConversionStrategy, in map[string][]byte) (map[string][]byte, error) {
-	out := make(map[string][]byte)
+	out := make(map[string][]byte, len(in))
 	for k, v := range in {
-		rs := []rune(k)
-		newName := make([]string, len(rs))
-		for rk, rv := range rs {
-			if !unicode.IsNumber(rv) &&
-				!unicode.IsLetter(rv) &&
-				rv != '-' &&
-				rv != '.' &&
-				rv != '_' {
-				switch strategy {
-				case esv1beta1.ExternalSecretConversionDefault:
-					newName[rk] = "_"
-				case esv1beta1.ExternalSecretConversionUnicode:
-					newName[rk] = fmt.Sprintf("_U%04x_", rv)
-				default:
-					return nil, fmt.Errorf("unknown conversion strategy: %s", strategy)
-				}
-			} else {
-				newName[rk] = string(rv)
-			}
-		}
-		key := strings.Join(newName, "")
+		key := convert(strategy, k)
 		if _, exists := out[key]; exists {
 			return nil, fmt.Errorf("secret name collision during conversion: %s", key)
 		}
@@ -69,6 +49,28 @@ func ConvertKeys(strategy esv1beta1.ExternalSecretConversionStrategy, in map[str
 	return out, nil
 }
 
+func convert(strategy esv1beta1.ExternalSecretConversionStrategy, str string) string {
+	rs := []rune(str)
+	newName := make([]string, len(rs))
+	for rk, rv := range rs {
+		if !unicode.IsNumber(rv) &&
+			!unicode.IsLetter(rv) &&
+			rv != '-' &&
+			rv != '.' &&
+			rv != '_' {
+			switch strategy {
+			case esv1beta1.ExternalSecretConversionDefault:
+				newName[rk] = "_"
+			case esv1beta1.ExternalSecretConversionUnicode:
+				newName[rk] = fmt.Sprintf("_U%04x_", rv)
+			}
+		} else {
+			newName[rk] = string(rv)
+		}
+	}
+	return strings.Join(newName, "")
+}
+
 // MergeStringMap performs a deep clone from src to dest.
 func MergeStringMap(dest, src map[string]string) {
 	for k, v := range src {

+ 78 - 0
pkg/utils/utils_test.go

@@ -15,10 +15,13 @@ limitations under the License.
 package utils
 
 import (
+	"reflect"
 	"testing"
 
 	vault "github.com/oracle/oci-go-sdk/v56/vault"
 	v1 "k8s.io/api/core/v1"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 )
 
 func TestObjectHash(t *testing.T) {
@@ -146,3 +149,78 @@ func TestIsNil(t *testing.T) {
 		})
 	}
 }
+
+func TestConvertKeys(t *testing.T) {
+	type args struct {
+		strategy esv1beta1.ExternalSecretConversionStrategy
+		in       map[string][]byte
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    map[string][]byte
+		wantErr bool
+	}{
+		{
+			name: "convert with special chars",
+			args: args{
+				strategy: esv1beta1.ExternalSecretConversionDefault,
+				in: map[string][]byte{
+					"foo$bar%baz*bing": []byte(`noop`),
+				},
+			},
+			want: map[string][]byte{
+				"foo_bar_baz_bing": []byte(`noop`),
+			},
+		},
+		{
+			name: "error on collision",
+			args: args{
+				strategy: esv1beta1.ExternalSecretConversionDefault,
+				in: map[string][]byte{
+					"foo$bar%baz*bing": []byte(`noop`),
+					"foo_bar_baz$bing": []byte(`noop`),
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "convert path",
+			args: args{
+				strategy: esv1beta1.ExternalSecretConversionDefault,
+				in: map[string][]byte{
+					"/foo/bar/baz/bing": []byte(`noop`),
+					"foo/bar/baz/bing/": []byte(`noop`),
+				},
+			},
+			want: map[string][]byte{
+				"_foo_bar_baz_bing": []byte(`noop`),
+				"foo_bar_baz_bing_": []byte(`noop`),
+			},
+		},
+		{
+			name: "convert unicode",
+			args: args{
+				strategy: esv1beta1.ExternalSecretConversionUnicode,
+				in: map[string][]byte{
+					"😀foo😁bar😂baz😈bing": []byte(`noop`),
+				},
+			},
+			want: map[string][]byte{
+				"_U1f600_foo_U1f601_bar_U1f602_baz_U1f608_bing": []byte(`noop`),
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := ConvertKeys(tt.args.strategy, tt.args.in)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ConvertKeys() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ConvertKeys() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}