Przeglądaj źródła

Delete old ClusterExternalSecrets when name changed (#2601)

Signed-off-by: shuheiktgw <s-kitagawa@mercari.com>
Shuhei Kitagawa 2 lat temu
rodzic
commit
d5271d0dab

+ 3 - 0
apis/externalsecrets/v1beta1/clusterexternalsecret_types.go

@@ -77,6 +77,9 @@ type ClusterExternalSecretNamespaceFailure struct {
 
 // ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret.
 type ClusterExternalSecretStatus struct {
+	// ExternalSecretName is the name of the ExternalSecrets created by the ClusterExternalSecret
+	ExternalSecretName string `json:"externalSecretName,omitempty"`
+
 	// Failed namespaces are the namespaces that failed to apply an ExternalSecret
 	// +optional
 	FailedNamespaces []ClusterExternalSecretNamespaceFailure `json:"failedNamespaces,omitempty"`

+ 4 - 0
config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml

@@ -503,6 +503,10 @@ spec:
                   - type
                   type: object
                 type: array
+              externalSecretName:
+                description: ExternalSecretName is the name of the ExternalSecrets
+                  created by the ClusterExternalSecret
+                type: string
               failedNamespaces:
                 description: Failed namespaces are the namespaces that failed to apply
                   an ExternalSecret

+ 3 - 0
deploy/crds/bundle.yaml

@@ -426,6 +426,9 @@ spec:
                       - type
                     type: object
                   type: array
+                externalSecretName:
+                  description: ExternalSecretName is the name of the ExternalSecrets created by the ClusterExternalSecret
+                  type: string
                 failedNamespaces:
                   description: Failed namespaces are the namespaces that failed to apply an ExternalSecret
                   items:

+ 11 - 0
docs/api/spec.md

@@ -1383,6 +1383,17 @@ Kubernetes meta/v1.Duration
 <tbody>
 <tr>
 <td>
+<code>externalSecretName</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<p>ExternalSecretName is the name of the ExternalSecrets created by the ClusterExternalSecret</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>failedNamespaces</code></br>
 <em>
 <a href="#external-secrets.io/v1beta1.ClusterExternalSecretNamespaceFailure">

+ 12 - 0
pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go

@@ -107,6 +107,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
 		esName = clusterExternalSecret.ObjectMeta.Name
 	}
 
+	if prevName := clusterExternalSecret.Status.ExternalSecretName; prevName != esName {
+		// ExternalSecretName has changed, so remove the old ones
+		for _, ns := range clusterExternalSecret.Status.ProvisionedNamespaces {
+			if err := r.deleteExternalSecret(ctx, prevName, clusterExternalSecret.Name, ns); err != nil {
+				log.Error(err, "could not delete ExternalSecret")
+				return ctrl.Result{}, err
+			}
+		}
+	}
+
+	clusterExternalSecret.Status.ExternalSecretName = esName
+
 	failedNamespaces := r.deleteOutdatedExternalSecrets(ctx, namespaceList, esName, clusterExternalSecret.Name, clusterExternalSecret.Status.ProvisionedNamespaces)
 
 	provisionedNamespaces := []string{}

+ 62 - 0
pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller_test.go

@@ -166,6 +166,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 					},
 					Spec: created.Spec,
 					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName:    created.Name,
 						ProvisionedNamespaces: []string{namespaces[0].Name},
 						Conditions: []esv1beta1.ClusterExternalSecretStatusCondition{
 							{
@@ -209,6 +210,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 					},
 					Spec: created.Spec,
 					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName:    "test-es",
 						ProvisionedNamespaces: []string{namespaces[0].Name},
 						Conditions: []esv1beta1.ClusterExternalSecretStatusCondition{
 							{
@@ -233,6 +235,61 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 				}
 			},
 		}),
+		Entry("Should delete old external secrets if name has changed", testCase{
+			namespaces: []v1.Namespace{
+				{ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
+			},
+			clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
+				ces := defaultClusterExternalSecret()
+				ces.Spec.NamespaceSelector.MatchLabels = map[string]string{"kubernetes.io/metadata.name": namespaces[0].Name}
+				ces.Spec.ExternalSecretName = "old-es-name"
+				return *ces
+			},
+			beforeCheck: func(ctx context.Context, namespaces []v1.Namespace, created esv1beta1.ClusterExternalSecret) {
+				// Wait until the external secret is provisioned
+				var es esv1beta1.ExternalSecret
+				Eventually(func(g Gomega) {
+					key := types.NamespacedName{Namespace: namespaces[0].Name, Name: "old-es-name"}
+					g.Expect(k8sClient.Get(ctx, key, &es)).ShouldNot(HaveOccurred())
+				}).WithTimeout(timeout).WithPolling(interval).Should(Succeed())
+
+				copied := created.DeepCopy()
+				copied.Spec.ExternalSecretName = "new-es-name"
+				Expect(k8sClient.Patch(ctx, copied, crclient.MergeFrom(created.DeepCopy()))).ShouldNot(HaveOccurred())
+			},
+			expectedClusterExternalSecret: func(namespaces []v1.Namespace, created esv1beta1.ClusterExternalSecret) esv1beta1.ClusterExternalSecret {
+				updatedSpec := created.Spec.DeepCopy()
+				updatedSpec.ExternalSecretName = "new-es-name"
+
+				return esv1beta1.ClusterExternalSecret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: created.Name,
+					},
+					Spec: *updatedSpec,
+					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName:    "new-es-name",
+						ProvisionedNamespaces: []string{namespaces[0].Name},
+						Conditions: []esv1beta1.ClusterExternalSecretStatusCondition{
+							{
+								Type:   esv1beta1.ClusterExternalSecretReady,
+								Status: v1.ConditionTrue,
+							},
+						},
+					},
+				}
+			},
+			expectedExternalSecrets: func(namespaces []v1.Namespace, created esv1beta1.ClusterExternalSecret) []esv1beta1.ExternalSecret {
+				return []esv1beta1.ExternalSecret{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Namespace: namespaces[0].Name,
+							Name:      "new-es-name",
+						},
+						Spec: created.Spec.ExternalSecretSpec,
+					},
+				}
+			},
+		}),
 		Entry("Should update external secret if the fields change", testCase{
 			namespaces: []v1.Namespace{
 				{ObjectMeta: metav1.ObjectMeta{Name: randomNamespaceName()}},
@@ -275,6 +332,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 					},
 					Spec: *updatedSpec,
 					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName:    created.Name,
 						ProvisionedNamespaces: []string{namespaces[0].Name},
 						Conditions: []esv1beta1.ClusterExternalSecretStatusCondition{
 							{
@@ -327,6 +385,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 					},
 					Spec: created.Spec,
 					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName: created.Name,
 						FailedNamespaces: []esv1beta1.ClusterExternalSecretNamespaceFailure{
 							{
 								Namespace: namespaces[0].Name,
@@ -401,6 +460,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 					},
 					Spec: created.Spec,
 					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName:    created.Name,
 						ProvisionedNamespaces: []string{namespaces[0].Name},
 						Conditions: []esv1beta1.ClusterExternalSecretStatusCondition{
 							{
@@ -470,6 +530,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 					},
 					Spec: created.Spec,
 					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName: created.Name,
 						Conditions: []esv1beta1.ClusterExternalSecretStatusCondition{
 							{
 								Type:   esv1beta1.ClusterExternalSecretReady,
@@ -525,6 +586,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
 					},
 					Spec: created.Spec,
 					Status: esv1beta1.ClusterExternalSecretStatus{
+						ExternalSecretName:    created.Name,
 						ProvisionedNamespaces: provisionedNamespaces,
 						Conditions: []esv1beta1.ClusterExternalSecretStatusCondition{
 							{