Browse Source

Added unit tests for crd controller methods.
Added simple GetAllSecrets logic test
Starting (and failing to) test on controller level

Signed-off-by: Gustavo Carvalho <gustavo.carvalho@container-solutions.com>

Gustavo Carvalho 4 years ago
parent
commit
a85e487d1d

+ 4 - 2
go.mod

@@ -85,7 +85,10 @@ require (
 	software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
 	software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
 )
 )
 
 
-require cloud.google.com/go/iam v0.1.1
+require (
+	cloud.google.com/go/iam v0.1.1
+	k8s.io/apiextensions-apiserver v0.23.0
+)
 
 
 require (
 require (
 	cloud.google.com/go/compute v0.1.0 // indirect
 	cloud.google.com/go/compute v0.1.0 // indirect
@@ -206,7 +209,6 @@ require (
 	gopkg.in/ini.v1 v1.66.2 // indirect
 	gopkg.in/ini.v1 v1.66.2 // indirect
 	gopkg.in/square/go-jose.v2 v2.6.0 // indirect
 	gopkg.in/square/go-jose.v2 v2.6.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
-	k8s.io/apiextensions-apiserver v0.23.0 // indirect
 	k8s.io/component-base v0.23.0 // indirect
 	k8s.io/component-base v0.23.0 // indirect
 	k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c // indirect
 	k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c // indirect
 	k8s.io/klog v0.3.0 // indirect
 	k8s.io/klog v0.3.0 // indirect

+ 155 - 0
pkg/controllers/crds/common_test.go

@@ -0,0 +1,155 @@
+/*
+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 crds
+
+// import (
+// 	"context"
+// 	"time"
+
+// 	. "github.com/onsi/ginkgo/v2"
+// 	. "github.com/onsi/gomega"
+// 	corev1 "k8s.io/api/core/v1"
+// 	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+// 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+// 	"k8s.io/apimachinery/pkg/types"
+
+// 	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+// )
+
+// type testCase struct {
+// 	crd    apiextensions.CustomResourceDefinition
+// 	assert func()
+// }
+
+// var _ = Describe("CRD reconcile", func() {
+// 	var test *testCase
+
+// 	BeforeEach(func() {
+// 		test = makeDefaultTestcase()
+// 	})
+
+// 	AfterEach(func() {
+// 		Expect(k8sClient.Delete(context.Background(), &test.crd)).ToNot(HaveOccurred())
+// 	})
+
+// 	// a invalid provider config should be reflected
+// 	// in the store status condition
+// 	PatchesCRD := func(tc *testCase) {
+// 		tc.assert = func() {
+// 			Eventually(func() bool {
+// 				ss := apiextensions.CustomResourceDefinition{}
+// 				err := k8sClient.Get(context.Background(), types.NamespacedName{
+// 					Name: "secrestores.external-secrets.io",
+// 				}, &ss)
+// 				if err != nil {
+// 					return false
+// 				}
+// 				return ss.Spec.Conversion.Strategy == "Webhook"
+// 			}).
+// 				WithTimeout(time.Second * 10).
+// 				WithPolling(time.Second).
+// 				Should(BeTrue())
+// 		}
+// 	}
+
+// 	// if controllerClass does not match the controller
+// 	// should not touch this store
+// 	ignoreNonTargetCRDs := func(tc *testCase) {
+// 		tc.assert = func() {
+// 			Consistently(func() bool {
+// 				ss := apiextensions.CustomResourceDefinition{}
+// 				err := k8sClient.Get(context.Background(), types.NamespacedName{
+// 					Name: defaultStoreName,
+// 				}, &ss)
+// 				if err != nil {
+// 					return false
+// 				}
+// 				return ss.Spec.Conversion == tc.crd.Spec.Conversion
+// 			}).
+// 				WithTimeout(time.Second * 3).
+// 				WithPolling(time.Millisecond * 500).
+// 				Should(BeTrue())
+// 		}
+// 	}
+
+// 	DescribeTable("Controller Reconcile logic", func(muts ...func(tc *testCase)) {
+// 		for _, mut := range muts {
+// 			mut(test)
+// 		}
+// 		err := k8sClient.Create(context.Background(), test.store.Copy())
+// 		Expect(err).ToNot(HaveOccurred())
+// 		test.assert()
+// 	},
+// 		// namespaced store
+// 		Entry("[namespace] invalid provider with secretStore should set InvalidStore condition", invalidProvider),
+// 		Entry("[namespace] ignore stores with non-matching class", ignoreControllerClass),
+// 		Entry("[namespace] valid provider has status=ready", validProvider),
+
+// 		// cluster store
+// 		Entry("[cluster] invalid provider with secretStore should set InvalidStore condition", invalidProvider, useClusterStore),
+// 		Entry("[cluster] ignore stores with non-matching class", ignoreControllerClass, useClusterStore),
+// 		Entry("[cluster] valid provider has status=ready", validProvider, useClusterStore),
+// 	)
+
+// })
+
+// const (
+// 	defaultStoreName       = "default-store"
+// 	defaultControllerClass = "test-ctrl"
+// )
+
+// func makeDefaultTestcase() *testCase {
+// 	return &testCase{
+// 		assert: func() {
+// 			// this is a noop by default
+// 		},
+// 		crd: &apiextensions.CustomResourceDefinition{
+// 			TypeMeta: metav1.TypeMeta{
+// 				Kind:       esapi.SecretStoreKind,
+// 				APIVersion: esapi.SecretStoreKindAPIVersion,
+// 			},
+// 			ObjectMeta: metav1.ObjectMeta{
+// 				Name:      defaultStoreName,
+// 				Namespace: "default",
+// 			},
+// 			Spec: esapi.SecretStoreSpec{
+// 				Controller: defaultControllerClass,
+// 				// empty provider
+// 				// a testCase mutator must fill in the concrete provider
+// 				Provider: &esapi.SecretStoreProvider{
+// 					Vault: &esapi.VaultProvider{
+// 						Version: esapi.VaultKVStoreV1,
+// 					},
+// 				},
+// 			},
+// 		},
+// 	}
+// }
+
+// func hasEvent(involvedKind, name, reason string) bool {
+// 	el := &corev1.EventList{}
+// 	err := k8sClient.List(context.Background(), el)
+// 	if err != nil {
+// 		return false
+// 	}
+// 	for i := range el.Items {
+// 		ev := el.Items[i]
+// 		if ev.InvolvedObject.Kind == involvedKind && ev.InvolvedObject.Name == name {
+// 			if ev.Reason == reason {
+// 				return true
+// 			}
+// 		}
+// 	}
+// 	return false
+// }

+ 289 - 0
pkg/controllers/crds/crds_controller_test.go

@@ -0,0 +1,289 @@
+/*
+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 crds
+
+import (
+	"context"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/json"
+	"testing"
+	"time"
+
+	corev1 "k8s.io/api/core/v1"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/types"
+	ctrl "sigs.k8s.io/controller-runtime"
+	client "sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+func newReconciler() Reconciler {
+	return Reconciler{
+		CrdResources: []string{"one", "two", "three"},
+		SvcLabels:    map[string]string{"foo": "bar"},
+		SecretLabels: map[string]string{"foo": "bar"},
+	}
+}
+
+func newService() corev1.Service {
+	return corev1.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "foo",
+			Namespace: "default",
+			Labels:    map[string]string{"foo": "bar"},
+		},
+	}
+}
+func newSecret() corev1.Secret {
+	return corev1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "foo",
+			Namespace: "default",
+			Labels:    map[string]string{"foo": "bar"},
+		},
+	}
+}
+
+func newCRD() apiextensionsv1.CustomResourceDefinition {
+	return apiextensionsv1.CustomResourceDefinition{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "one",
+		},
+		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
+			Conversion: &apiextensionsv1.CustomResourceConversion{
+				Strategy: "Webhook",
+				Webhook: &apiextensionsv1.WebhookConversion{
+					ConversionReviewVersions: []string{"v1"},
+					ClientConfig: &apiextensionsv1.WebhookClientConfig{
+						CABundle: []byte("test"),
+						Service: &apiextensionsv1.ServiceReference{
+							Name:      "wrong",
+							Namespace: "wrong",
+						},
+					},
+				},
+			},
+		},
+	}
+}
+func TestConvertToWebhookInfo(t *testing.T) {
+	rec := newReconciler()
+	info := rec.ConvertToWebhookInfo()
+	if len(info) != 3 {
+		t.Errorf("Convert to WebhookInfo failed. Total resources:%d", len(info))
+	}
+	for _, v := range info {
+		if v.Type != CRDConversion {
+			t.Errorf("Convert to WebhookInfo failed. wrong type:%v", v.Type)
+		}
+		if v.Name != "one" && v.Name != "two" && v.Name != "three" {
+			t.Errorf("Convert to WebhookInfo failed. wrong name:%v", v.Name)
+		}
+	}
+}
+
+func TestUpdateCRD(t *testing.T) {
+	rec := newReconciler()
+	svc := newService()
+	secret := newSecret()
+	crd := newCRD()
+	c := client.NewClientBuilder().WithObjects(&svc, &secret, &crd).Build()
+	rec.Client = c
+	ctx := context.Background()
+	req := ctrl.Request{
+		NamespacedName: types.NamespacedName{
+			Name: "one",
+		},
+	}
+	err := rec.updateCRD(ctx, req)
+	if err != nil {
+		t.Errorf("Failed updating CRD:%v", err)
+	}
+}
+
+func TestInjectSvcToConversionWebhook(t *testing.T) {
+	svc := newService()
+	crd := newCRD()
+	crdunmarshalled := make(map[string]interface{})
+	crdJSON, err := json.Marshal(crd)
+	if err != nil {
+		t.Fatal("Could not setup test")
+	}
+	err = json.Unmarshal(crdJSON, &crdunmarshalled)
+	if err != nil {
+		t.Fatal("Could not setup test")
+	}
+	u := unstructured.Unstructured{
+		Object: crdunmarshalled,
+	}
+	err = injectSvcToConversionWebhook(&u, &svc)
+	if err != nil {
+		t.Errorf("Failed: error when injecting: %v", err)
+	}
+	val, found, err := unstructured.NestedString(u.Object, "spec", "conversion", "webhook", "clientConfig", "service", "name")
+	if err != nil {
+		t.Error("Error when searching for field")
+	}
+	if !found {
+		t.Error("Field not found, mutation failed")
+	}
+	if val != "foo" {
+		t.Errorf("Wrong service name injected: %v", val)
+	}
+
+	val, found, err = unstructured.NestedString(u.Object, "spec", "conversion", "webhook", "clientConfig", "service", "namespace")
+	if err != nil {
+		t.Error("Error when searching for field")
+	}
+	if !found {
+		t.Error("Field not found, mutation failed")
+	}
+	if val != "default" {
+		t.Errorf("Wrong service namespace injected: %v", val)
+	}
+}
+
+func TestInjectCertToConversionWebhook(t *testing.T) {
+	certPEM := []byte("foobar")
+	crd := newCRD()
+	crdunmarshalled := make(map[string]interface{})
+	crdJson, err := json.Marshal(crd)
+	if err != nil {
+		t.Fatal("Could not setup test")
+	}
+	err = json.Unmarshal(crdJson, &crdunmarshalled)
+	if err != nil {
+		t.Fatal("Could not setup test")
+	}
+	u := unstructured.Unstructured{
+		Object: crdunmarshalled,
+	}
+	err = injectCertToConversionWebhook(&u, certPEM)
+	if err != nil {
+		t.Errorf("Failed: error when injecting: %v", err)
+	}
+	val, found, err := unstructured.NestedString(u.Object, "spec", "conversion", "webhook", "clientConfig", "caBundle")
+	if err != nil {
+		t.Error("Error when searching for field")
+	}
+	if !found {
+		t.Error("Field not found, mutation failed")
+	}
+	if val != "Zm9vYmFy" {
+		t.Errorf("Wrong certificate name injected: %v", val)
+	}
+}
+func TestPopulateSecret(t *testing.T) {
+	secret := newSecret()
+	caArtifacts := KeyPairArtifacts{
+		Cert:    &x509.Certificate{},
+		Key:     &rsa.PrivateKey{},
+		CertPEM: []byte("foobarca"),
+		KeyPEM:  []byte("foobarcakey"),
+	}
+	cert := []byte("foobarcert")
+	key := []byte("foobarkey")
+	populateSecret(cert, key, &caArtifacts, &secret)
+	if string(secret.Data["tls.crt"]) != string(cert) {
+		t.Errorf("secret value for tls.crt is wrong:%v", cert)
+	}
+	if string(secret.Data["tls.key"]) != string(key) {
+		t.Errorf("secret value for tls.key is wrong:%v", cert)
+	}
+	if string(secret.Data["ca.crt"]) != string(caArtifacts.CertPEM) {
+		t.Errorf("secret value for ca.crt is wrong:%v", cert)
+	}
+	if string(secret.Data["ca.key"]) != string(caArtifacts.KeyPEM) {
+		t.Errorf("secret value for ca.key is wrong:%v", cert)
+	}
+}
+
+func TestCreateCACert(t *testing.T) {
+	rec := newReconciler()
+	caArtifacts, err := rec.CreateCACert(time.Now(), time.Now().AddDate(1, 0, 0))
+	if err != nil {
+		t.Errorf("could not create certificates:%v", err)
+	}
+	if !rec.validCACert(caArtifacts.CertPEM, caArtifacts.KeyPEM) {
+		t.Errorf("generated certificates are invalid:%v", caArtifacts)
+	}
+}
+
+func TestCreateCertPEM(t *testing.T) {
+	rec := newReconciler()
+	caArtifacts, err := rec.CreateCACert(time.Now(), time.Now().AddDate(1, 0, 0))
+	if err != nil {
+		t.Fatalf("could not create ca certificates:%v", err)
+	}
+	certPEM, keyPEM, err := rec.CreateCertPEM(caArtifacts, time.Now(), time.Now().AddDate(1, 0, 0))
+	if err != nil {
+		t.Errorf("could not create server certificates: %v", err)
+	}
+	if !rec.validServerCert(caArtifacts.CertPEM, certPEM, keyPEM) {
+		t.Errorf("generated certificates are invalid:%v,%v", certPEM, keyPEM)
+	}
+}
+func TestValidCert(t *testing.T) {
+	rec := newReconciler()
+	rec.dnsName = "foobar"
+	caArtifacts, err := rec.CreateCACert(time.Now(), time.Now().AddDate(1, 0, 0))
+	if err != nil {
+		t.Fatalf("could not create ca certificates:%v", err)
+	}
+	certPEM, keyPEM, err := rec.CreateCertPEM(caArtifacts, time.Now(), time.Now().AddDate(1, 0, 0))
+	if err != nil {
+		t.Errorf("could not create server certificates: %v", err)
+	}
+	ok, err := ValidCert(caArtifacts.CertPEM, certPEM, keyPEM, "foobar", time.Now())
+	if err != nil {
+		t.Errorf("error validating cert: %v", err)
+	}
+	if !ok {
+		t.Errorf("certificate is invalid")
+	}
+}
+
+func TestRefreshCertIfNeeded(t *testing.T) {
+	rec := newReconciler()
+	secret := newSecret()
+	c := client.NewClientBuilder().WithObjects(&secret).Build()
+	rec.Client = c
+	rec.dnsName = "foobar"
+	caArtifacts, err := rec.CreateCACert(time.Now().AddDate(-1, 0, 0), time.Now().AddDate(0, -1, 0))
+	if err != nil {
+		t.Fatalf("could not create ca certificates:%v", err)
+	}
+	certPEM, keyPEM, err := rec.CreateCertPEM(caArtifacts, time.Now(), time.Now().AddDate(1, 0, 0))
+	if err != nil {
+		t.Errorf("could not create server certificates: %v", err)
+	}
+	populateSecret(certPEM, keyPEM, caArtifacts, &secret)
+	ok, err := rec.refreshCertIfNeeded(&secret)
+	if err != nil {
+		t.Errorf("could not verify refresh need: %v", err)
+	}
+	if !ok {
+		t.Error("expected refresh true. got false")
+	}
+	ok, err = rec.refreshCertIfNeeded(&secret)
+	if err != nil {
+		t.Errorf("could not verify refresh need: %v", err)
+	}
+	if !ok {
+		t.Error("expected refresh false. got true")
+	}
+}

+ 101 - 0
pkg/controllers/crds/suite_test.go

@@ -0,0 +1,101 @@
+/*
+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 crds
+
+import (
+	"context"
+	"path/filepath"
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+	"k8s.io/client-go/kubernetes/scheme"
+	"k8s.io/client-go/rest"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/controller"
+	"sigs.k8s.io/controller-runtime/pkg/envtest"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+	esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+var cfg *rest.Config
+var k8sClient client.Client
+var testEnv *envtest.Environment
+var cancel context.CancelFunc
+
+func TestAPIs(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Controller Suite")
+}
+
+var _ = BeforeSuite(func() {
+	log := zap.New(zap.WriteTo(GinkgoWriter))
+	logf.SetLogger(log)
+
+	By("bootstrapping test environment")
+	testEnv = &envtest.Environment{
+		CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "deploy", "crds")},
+	}
+
+	var ctx context.Context
+	ctx, cancel = context.WithCancel(context.Background())
+
+	var err error
+	cfg, err = testEnv.Start()
+	Expect(err).ToNot(HaveOccurred())
+	Expect(cfg).ToNot(BeNil())
+
+	err = esapi.AddToScheme(scheme.Scheme)
+	Expect(err).NotTo(HaveOccurred())
+
+	k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
+		Scheme:             scheme.Scheme,
+		MetricsBindAddress: "0", // avoid port collision when testing
+	})
+	Expect(err).ToNot(HaveOccurred())
+
+	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+	Expect(err).ToNot(HaveOccurred())
+	Expect(k8sClient).ToNot(BeNil())
+
+	err = (&Reconciler{
+		Client:                 k8sClient,
+		Scheme:                 k8sManager.GetScheme(),
+		Log:                    ctrl.Log.WithName("controllers").WithName("CustomResourceDefinition"),
+		SvcLabels:              map[string]string{"foo": "bar"},
+		SecretLabels:           map[string]string{"foo": "bar"},
+		CrdResources:           []string{"externalsecrets.external-secrets.io", "secretstores.external-secrets.io", "clustersecretstores.external-secrets.io"},
+		CertDir:                "my/cert/dir",
+		CAName:                 "external-secrets",
+		CAOrganization:         "external-secrets",
+		RestartOnSecretRefresh: false,
+	}).SetupWithManager(k8sManager, controller.Options{})
+	Expect(err).ToNot(HaveOccurred())
+
+	go func() {
+		defer GinkgoRecover()
+		Expect(k8sManager.Start(ctx)).ToNot(HaveOccurred())
+	}()
+})
+
+var _ = AfterSuite(func() {
+	By("tearing down the test environment")
+	cancel() // stop manager
+	err := testEnv.Stop()
+	Expect(err).ToNot(HaveOccurred())
+})

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

@@ -236,7 +236,6 @@ var _ = Describe("ExternalSecret controller", func() {
 			Expect(secret.ObjectMeta.Name).To(Equal(ExternalSecretName))
 			Expect(secret.ObjectMeta.Name).To(Equal(ExternalSecretName))
 		}
 		}
 	}
 	}
-
 	// labels and annotations from the Kind=ExternalSecret
 	// labels and annotations from the Kind=ExternalSecret
 	// should be copied over to the Kind=Secret
 	// should be copied over to the Kind=Secret
 	syncLabelsAnnotations := func(tc *testCase) {
 	syncLabelsAnnotations := func(tc *testCase) {
@@ -789,6 +788,30 @@ var _ = Describe("ExternalSecret controller", func() {
 		}
 		}
 	}
 	}
 
 
+	// with dataFrom.Find the change is on the called method GetAllSecrets
+	// all keys should be put into the secret
+	syncDataFromFind := func(tc *testCase) {
+		tc.externalSecret.Spec.Data = nil
+		tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+			{
+				Find: esv1beta1.ExternalSecretFind{
+					Name: esv1beta1.FindName{
+						RegExp: "foobar",
+					},
+				},
+			},
+		}
+		fakeProvider.WithGetAllSecrets(map[string][]byte{
+			"foo": []byte(FooValue),
+			"bar": []byte(BarValue),
+		}, nil)
+		tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
+			// check values
+			Expect(string(secret.Data["foo"])).To(Equal(FooValue))
+			Expect(string(secret.Data["bar"])).To(Equal(BarValue))
+		}
+	}
+
 	// with dataFrom and using a template
 	// with dataFrom and using a template
 	// should be put into the secret
 	// should be put into the secret
 	syncWithDataFromTemplate := func(tc *testCase) {
 	syncWithDataFromTemplate := func(tc *testCase) {
@@ -1064,6 +1087,7 @@ var _ = Describe("ExternalSecret controller", func() {
 		Entry("should refresh secret map when provider secret changes when using a template", refreshSecretValueMapTemplate),
 		Entry("should refresh secret map when provider secret changes when using a template", refreshSecretValueMapTemplate),
 		Entry("should not refresh secret value when provider secret changes but refreshInterval is zero", refreshintervalZero),
 		Entry("should not refresh secret value when provider secret changes but refreshInterval is zero", refreshintervalZero),
 		Entry("should fetch secret using dataFrom", syncWithDataFrom),
 		Entry("should fetch secret using dataFrom", syncWithDataFrom),
+		Entry("should fetch secret using dataFrom.find", syncDataFromFind),
 		Entry("should fetch secret using dataFrom and a template", syncWithDataFromTemplate),
 		Entry("should fetch secret using dataFrom and a template", syncWithDataFromTemplate),
 		Entry("should set error condition when provider errors", providerErrCondition),
 		Entry("should set error condition when provider errors", providerErrCondition),
 		Entry("should set an error condition when store does not exist", storeMissingErrCondition),
 		Entry("should set an error condition when store does not exist", storeMissingErrCondition),

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

@@ -55,7 +55,7 @@ var _ = BeforeSuite(func() {
 
 
 	By("bootstrapping test environment")
 	By("bootstrapping test environment")
 	testEnv = &envtest.Environment{
 	testEnv = &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "deploy", "crds")},
+		CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds", "bases")},
 	}
 	}
 
 
 	var ctx context.Context
 	var ctx context.Context

+ 8 - 0
pkg/provider/testing/fake/fake.go

@@ -100,6 +100,14 @@ func (v *Client) WithGetSecretMap(secData map[string][]byte, err error) *Client
 	return v
 	return v
 }
 }
 
 
+// WithGetAllSecrets wraps the secret data map returned by this fake provider.
+func (v *Client) WithGetAllSecrets(secData map[string][]byte, err error) *Client {
+	v.GetAllSecretsFn = func(context.Context, esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+		return secData, err
+	}
+	return v
+}
+
 // WithNew wraps the fake provider factory function.
 // WithNew wraps the fake provider factory function.
 func (v *Client) WithNew(f func(context.Context, esv1beta1.GenericStore, client.Client,
 func (v *Client) WithNew(f func(context.Context, esv1beta1.GenericStore, client.Client,
 	string) (provider.SecretsClient, error)) *Client {
 	string) (provider.SecretsClient, error)) *Client {