Browse Source

Update the ExternalSecret status even when data is empty (#2927)

https://github.com/external-secrets/external-secrets/issues/2874

Signed-off-by: shuheiktgw <s-kitagawa@mercari.com>
Co-authored-by: Moritz Johner <moolen@users.noreply.github.com>
Shuhei Kitagawa 2 years ago
parent
commit
373a9c23e8

+ 6 - 7
pkg/controllers/externalsecret/externalsecret_controller.go

@@ -230,21 +230,20 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
 				return ctrl.Result{}, err
 				return ctrl.Result{}, err
 			}
 			}
 
 
-			err = r.Delete(ctx, secret)
-			if err != nil && !apierrors.IsNotFound(err) {
+			if err := r.Delete(ctx, secret); err != nil && !apierrors.IsNotFound(err) {
 				r.markAsFailed(log, errDeleteSecret, err, &externalSecret, syncCallsError.With(resourceLabels))
 				r.markAsFailed(log, errDeleteSecret, err, &externalSecret, syncCallsError.With(resourceLabels))
+				return ctrl.Result{}, err
 			}
 			}
 
 
 			conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionTrue, esv1beta1.ConditionReasonSecretDeleted, "secret deleted due to DeletionPolicy")
 			conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionTrue, esv1beta1.ConditionReasonSecretDeleted, "secret deleted due to DeletionPolicy")
 			SetExternalSecretCondition(&externalSecret, *conditionSynced)
 			SetExternalSecretCondition(&externalSecret, *conditionSynced)
-			return ctrl.Result{RequeueAfter: refreshInt}, err
-
-		case esv1beta1.DeletionPolicyMerge:
-			// noop, handled below
-
+			return ctrl.Result{RequeueAfter: refreshInt}, nil
 		// In case provider secrets don't exist the kubernetes secret will be kept as-is.
 		// In case provider secrets don't exist the kubernetes secret will be kept as-is.
 		case esv1beta1.DeletionPolicyRetain:
 		case esv1beta1.DeletionPolicyRetain:
+			r.markAsDone(&externalSecret, start, log)
 			return ctrl.Result{RequeueAfter: refreshInt}, nil
 			return ctrl.Result{RequeueAfter: refreshInt}, nil
+		// noop, handled below
+		case esv1beta1.DeletionPolicyMerge:
 		}
 		}
 	}
 	}
 
 

+ 56 - 9
pkg/controllers/externalsecret/externalsecret_controller_test.go

@@ -22,6 +22,8 @@ import (
 	"strconv"
 	"strconv"
 	"time"
 	"time"
 
 
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 	. "github.com/onsi/gomega"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus"
@@ -599,7 +601,6 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 	}
 	}
 
 
 	deleteOrphanedSecrets := func(tc *testCase) {
 	deleteOrphanedSecrets := func(tc *testCase) {
-
 		tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
 		tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
 			cleanEs := es.DeepCopy()
 			cleanEs := es.DeepCopy()
 			oldSecret := v1.Secret{}
 			oldSecret := v1.Secret{}
@@ -628,6 +629,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 			}, time.Second*10, time.Millisecond*200).Should(BeTrue())
 			}, time.Second*10, time.Millisecond*200).Should(BeTrue())
 		}
 		}
 	}
 	}
+
 	ignoreMismatchControllerForGeneratorRef := func(tc *testCase) {
 	ignoreMismatchControllerForGeneratorRef := func(tc *testCase) {
 		const secretKey = "somekey"
 		const secretKey = "somekey"
 		const secretVal = "someValue"
 		const secretVal = "someValue"
@@ -957,6 +959,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 			Expect(string(secret.Data["map-foo-value-sec"])).To(Equal(BarValue))
 			Expect(string(secret.Data["map-foo-value-sec"])).To(Equal(BarValue))
 		}
 		}
 	}
 	}
+
 	syncTemplateFromLiteral := func(tc *testCase) {
 	syncTemplateFromLiteral := func(tc *testCase) {
 		tplDataVal := "{{ .targetKey }}-literal: {{ .targetValue }}"
 		tplDataVal := "{{ .targetKey }}-literal: {{ .targetValue }}"
 		tplAnnotationsVal := "{{ .targetKey }}-annotations: {{ .targetValue }}"
 		tplAnnotationsVal := "{{ .targetKey }}-annotations: {{ .targetValue }}"
@@ -1243,7 +1246,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 		}
 		}
 	}
 	}
 
 
-	deleteSecretPolicy := func(tc *testCase) {
+	deletionPolicyDelete := func(tc *testCase) {
 		expVal := []byte("1234")
 		expVal := []byte("1234")
 		// set initial value
 		// set initial value
 		fakeProvider.WithGetAllSecrets(map[string][]byte{
 		fakeProvider.WithGetAllSecrets(map[string][]byte{
@@ -1291,7 +1294,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 		}
 		}
 	}
 	}
 
 
-	deleteSecretPolicyRetain := func(tc *testCase) {
+	deletionPolicyRetain := func(tc *testCase) {
 		expVal := []byte("1234")
 		expVal := []byte("1234")
 		// set initial value
 		// set initial value
 		fakeProvider.WithGetAllSecrets(map[string][]byte{
 		fakeProvider.WithGetAllSecrets(map[string][]byte{
@@ -1321,14 +1324,56 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 			Consistently(func() bool {
 			Consistently(func() bool {
 				By("checking that secret has not been deleted")
 				By("checking that secret has not been deleted")
 				err := k8sClient.Get(context.Background(), secretLookupKey, sec)
 				err := k8sClient.Get(context.Background(), secretLookupKey, sec)
-				return apierrors.IsNotFound(err) && bytes.Equal(sec.Data["foo"], expVal)
-			}, time.Second*10, time.Second).Should(BeFalse())
+				if err != nil {
+					GinkgoLogr.Error(err, "failed getting a secret")
+					return false
+				}
+				if got := sec.Data["foo"]; !bytes.Equal(got, expVal) {
+					GinkgoLogr.Info("received an unexpected secret value", "got", got, "expected", expVal)
+					return false
+				}
+				return true
+			}, time.Second*10, time.Second).Should(BeTrue())
+		}
+	}
+
+	deletionPolicyRetainEmptyData := func(tc *testCase) {
+		// set initial value
+		fakeProvider.WithGetAllSecrets(make(map[string][]byte), nil)
+		tc.externalSecret.Spec.Data = make([]esv1beta1.ExternalSecretData, 0)
+		tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+			{
+				Find: &esv1beta1.ExternalSecretFind{
+					Tags: map[string]string{
+						"non-existing-key": "non-existing-value",
+					},
+				},
+			},
+		}
+		tc.externalSecret.Spec.Target.DeletionPolicy = esv1beta1.DeletionPolicyRetain
+		tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
+		tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
+			expected := []esv1beta1.ExternalSecretStatusCondition{
+				{
+					Type:    esv1beta1.ExternalSecretReady,
+					Status:  v1.ConditionTrue,
+					Reason:  esv1beta1.ConditionReasonSecretSynced,
+					Message: "Secret was synced",
+				},
+			}
+
+			opts := cmpopts.IgnoreFields(esv1beta1.ExternalSecretStatusCondition{}, "LastTransitionTime")
+			if diff := cmp.Diff(expected, es.Status.Conditions, opts); diff != "" {
+				GinkgoLogr.Info("(-got, +want)\n%s", "diff", diff)
+				return false
+			}
+			return true
 		}
 		}
 	}
 	}
 
 
 	// merge with existing secret using creationPolicy=Merge
 	// merge with existing secret using creationPolicy=Merge
 	// if provider secret gets deleted only the managed field should get deleted
 	// if provider secret gets deleted only the managed field should get deleted
-	deleteSecretPolicyMerge := func(tc *testCase) {
+	deletionPolicyMerge := func(tc *testCase) {
 		const secretVal = "someValue"
 		const secretVal = "someValue"
 		tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
 		tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Second}
 		tc.externalSecret.Spec.Target.CreationPolicy = esv1beta1.CreatePolicyMerge
 		tc.externalSecret.Spec.Target.CreationPolicy = esv1beta1.CreatePolicyMerge
@@ -1483,6 +1528,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 		}
 		}
 
 
 	}
 	}
+
 	// with rewrite keys from dataFrom
 	// with rewrite keys from dataFrom
 	// should error if keys are not compliant
 	// should error if keys are not compliant
 	invalidFindKeysErrCondition := func(tc *testCase) {
 	invalidFindKeysErrCondition := func(tc *testCase) {
@@ -2210,9 +2256,10 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
 		Entry("should set an error condition when store provider constructor fails", storeConstructErrCondition),
 		Entry("should set an error condition when store provider constructor fails", storeConstructErrCondition),
 		Entry("should not process store with mismatching controller field", ignoreMismatchController),
 		Entry("should not process store with mismatching controller field", ignoreMismatchController),
 		Entry("should not process cluster secret store when it is disabled", ignoreClusterSecretStoreWhenDisabled),
 		Entry("should not process cluster secret store when it is disabled", ignoreClusterSecretStoreWhenDisabled),
-		Entry("should eventually delete target secret with deletionPolicy=Delete", deleteSecretPolicy),
-		Entry("should not delete target secret with deletionPolicy=Retain", deleteSecretPolicyRetain),
-		Entry("should not delete pre-existing secret with deletionPolicy=Merge", deleteSecretPolicyMerge),
+		Entry("should eventually delete target secret with deletionPolicy=Delete", deletionPolicyDelete),
+		Entry("should not delete target secret with deletionPolicy=Retain", deletionPolicyRetain),
+		Entry("should update the status properly even if the deletionPolicy is Retain and the data is empty", deletionPolicyRetainEmptyData),
+		Entry("should not delete pre-existing secret with deletionPolicy=Merge", deletionPolicyMerge),
 		Entry("secret is created when there are no conditions for the cluster secret store", useClusterSecretStore, noConditionsSecretCreated),
 		Entry("secret is created when there are no conditions for the cluster secret store", useClusterSecretStore, noConditionsSecretCreated),
 		Entry("secret is not created when the condition for the cluster secret store states a different namespace single string condition", useClusterSecretStore, noSecretCreatedWhenNamespaceDoesntMatchStringCondition),
 		Entry("secret is not created when the condition for the cluster secret store states a different namespace single string condition", useClusterSecretStore, noSecretCreatedWhenNamespaceDoesntMatchStringCondition),
 		Entry("secret is not created when the condition for the cluster secret store states a different namespace single string condition with multiple names", useClusterSecretStore, noSecretCreatedWhenNamespaceDoesntMatchStringConditionWithMultipleNames),
 		Entry("secret is not created when the condition for the cluster secret store states a different namespace single string condition with multiple names", useClusterSecretStore, noSecretCreatedWhenNamespaceDoesntMatchStringConditionWithMultipleNames),