Преглед изворни кода

test: extend kubernetes v2 e2e coverage

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>
Moritz Johner пре 2 месеци
родитељ
комит
34a8ceefd2
2 измењених фајлова са 183 додато и 2 уклоњено
  1. 10 2
      e2e/framework/v2/helpers.go
  2. 173 0
      e2e/suites/provider/cases/kubernetes/provider_v2.go

+ 10 - 2
e2e/framework/v2/helpers.go

@@ -185,6 +185,14 @@ func CreateClusterProviderConnection(f *framework.Framework, name, address, prov
 }
 }
 
 
 func WaitForProviderConnectionReady(f *framework.Framework, namespace, name string, timeout time.Duration) *esv1.Provider {
 func WaitForProviderConnectionReady(f *framework.Framework, namespace, name string, timeout time.Duration) *esv1.Provider {
+	return WaitForProviderConnectionCondition(f, namespace, name, metav1.ConditionTrue, timeout)
+}
+
+func WaitForProviderConnectionNotReady(f *framework.Framework, namespace, name string, timeout time.Duration) *esv1.Provider {
+	return WaitForProviderConnectionCondition(f, namespace, name, metav1.ConditionFalse, timeout)
+}
+
+func WaitForProviderConnectionCondition(f *framework.Framework, namespace, name string, status metav1.ConditionStatus, timeout time.Duration) *esv1.Provider {
 	var providerConnection esv1.Provider
 	var providerConnection esv1.Provider
 	Eventually(func() bool {
 	Eventually(func() bool {
 		err := f.CRClient.Get(context.Background(),
 		err := f.CRClient.Get(context.Background(),
@@ -196,12 +204,12 @@ func WaitForProviderConnectionReady(f *framework.Framework, namespace, name stri
 		}
 		}
 
 
 		for _, condition := range providerConnection.Status.Conditions {
 		for _, condition := range providerConnection.Status.Conditions {
-			if condition.Type == "Ready" && condition.Status == metav1.ConditionTrue {
+			if condition.Type == "Ready" && condition.Status == status {
 				return true
 				return true
 			}
 			}
 		}
 		}
 		return false
 		return false
-	}, timeout, time.Second).Should(BeTrue(), "Provider should become ready")
+	}, timeout, time.Second).Should(BeTrue(), fmt.Sprintf("Provider should become %s", status))
 
 
 	return &providerConnection
 	return &providerConnection
 }
 }

+ 173 - 0
e2e/suites/provider/cases/kubernetes/provider_v2.go

@@ -22,11 +22,15 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 	. "github.com/onsi/gomega"
 	corev1 "k8s.io/api/core/v1"
 	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
 
 
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	"github.com/external-secrets/external-secrets-e2e/framework"
 	frameworkv2 "github.com/external-secrets/external-secrets-e2e/framework/v2"
 	frameworkv2 "github.com/external-secrets/external-secrets-e2e/framework/v2"
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
 	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	k8sv2alpha1 "github.com/external-secrets/external-secrets/apis/provider/kubernetes/v2alpha1"
 )
 )
 
 
 var _ = Describe("[kubernetes] v2 namespaced provider", Label("kubernetes", "v2", "namespaced-provider"), func() {
 var _ = Describe("[kubernetes] v2 namespaced provider", Label("kubernetes", "v2", "namespaced-provider"), func() {
@@ -81,6 +85,55 @@ var _ = Describe("[kubernetes] v2 namespaced provider", Label("kubernetes", "v2"
 		Expect(err).NotTo(HaveOccurred())
 		Expect(err).NotTo(HaveOccurred())
 	})
 	})
 
 
+	It("refreshes synced secrets after the remote Kubernetes secret changes", func() {
+		prov.CreateSecret("provider-v2-refresh-remote", framework.SecretEntry{
+			Value: `{"value":"provider-v2-initial"}`,
+		})
+
+		externalSecret := &esv1.ExternalSecret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "provider-v2-refresh-es",
+				Namespace: f.Namespace.Name,
+			},
+			Spec: esv1.ExternalSecretSpec{
+				RefreshInterval: &metav1.Duration{Duration: defaultV2RefreshInterval},
+				SecretStoreRef: esv1.SecretStoreRef{
+					Name: f.Namespace.Name,
+					Kind: esv1.ProviderKindStr,
+				},
+				Target: esv1.ExternalSecretTarget{
+					Name: "provider-v2-refresh-target",
+				},
+				Data: []esv1.ExternalSecretData{{
+					SecretKey: "value",
+					RemoteRef: esv1.ExternalSecretDataRemoteRef{
+						Key:      "provider-v2-refresh-remote",
+						Property: "value",
+					},
+				}},
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), externalSecret)).To(Succeed())
+
+		_, err := f.WaitForSecretValue(f.Namespace.Name, "provider-v2-refresh-target", &corev1.Secret{
+			Type: corev1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				"value": []byte("provider-v2-initial"),
+			},
+		})
+		Expect(err).NotTo(HaveOccurred())
+
+		updateRemoteSecretValue(f, f.Namespace.Name, "provider-v2-refresh-remote", "provider-v2-updated")
+
+		_, err = f.WaitForSecretValue(f.Namespace.Name, "provider-v2-refresh-target", &corev1.Secret{
+			Type: corev1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				"value": []byte("provider-v2-updated"),
+			},
+		})
+		Expect(err).NotTo(HaveOccurred())
+	})
+
 	It("syncs ExternalSecret dataFrom.find through a namespaced Provider", func() {
 	It("syncs ExternalSecret dataFrom.find through a namespaced Provider", func() {
 		secretOne := "provider-v2-find-one"
 		secretOne := "provider-v2-find-one"
 		secretTwo := "provider-v2-find-two"
 		secretTwo := "provider-v2-find-two"
@@ -132,4 +185,124 @@ var _ = Describe("[kubernetes] v2 namespaced provider", Label("kubernetes", "v2"
 		_, err := f.WaitForSecretValue(f.Namespace.Name, "provider-v2-find-target", expected)
 		_, err := f.WaitForSecretValue(f.Namespace.Name, "provider-v2-find-target", expected)
 		Expect(err).NotTo(HaveOccurred())
 		Expect(err).NotTo(HaveOccurred())
 	})
 	})
+
+	It("recovers after repairing namespaced Provider auth", func() {
+		prov.CreateSecret("provider-v2-recovery-remote", framework.SecretEntry{
+			Value: `{"value":"provider-v2-recovered"}`,
+		})
+
+		updateKubernetesProviderServiceAccount(f, f.Namespace.Name, providerConfigName(f.Namespace.Name, ""), "missing-service-account")
+		triggerProviderConnectionReconcile(f, f.Namespace.Name, f.Namespace.Name, "broken-auth")
+
+		brokenProvider := frameworkv2.WaitForProviderConnectionNotReady(f, f.Namespace.Name, f.Namespace.Name, defaultV2WaitTimeout)
+		Expect(brokenProvider.Status.Capabilities).To(BeEmpty())
+
+		externalSecret := &esv1.ExternalSecret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "provider-v2-recovery-es",
+				Namespace: f.Namespace.Name,
+			},
+			Spec: esv1.ExternalSecretSpec{
+				RefreshInterval: &metav1.Duration{Duration: defaultV2RefreshInterval},
+				SecretStoreRef: esv1.SecretStoreRef{
+					Name: f.Namespace.Name,
+					Kind: esv1.ProviderKindStr,
+				},
+				Target: esv1.ExternalSecretTarget{
+					Name: "provider-v2-recovery-target",
+				},
+				Data: []esv1.ExternalSecretData{{
+					SecretKey: "value",
+					RemoteRef: esv1.ExternalSecretDataRemoteRef{
+						Key:      "provider-v2-recovery-remote",
+						Property: "value",
+					},
+				}},
+			},
+		}
+		Expect(f.CRClient.Create(context.Background(), externalSecret)).To(Succeed())
+
+		waitForExternalSecretReadyStatus(f, f.Namespace.Name, externalSecret.Name, corev1.ConditionFalse)
+		expectSecretToBeAbsent(f, f.Namespace.Name, "provider-v2-recovery-target")
+
+		updateKubernetesProviderServiceAccount(f, f.Namespace.Name, providerConfigName(f.Namespace.Name, ""), frameworkv2.DefaultSAName)
+		triggerProviderConnectionReconcile(f, f.Namespace.Name, f.Namespace.Name, "repaired-auth")
+
+		repairedProvider := frameworkv2.WaitForProviderConnectionReady(f, f.Namespace.Name, f.Namespace.Name, defaultV2WaitTimeout)
+		Expect(repairedProvider.Status.Capabilities).To(Equal(esv1.ProviderReadWrite))
+
+		_, err := f.WaitForSecretValue(f.Namespace.Name, "provider-v2-recovery-target", &corev1.Secret{
+			Type: corev1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				"value": []byte("provider-v2-recovered"),
+			},
+		})
+		Expect(err).NotTo(HaveOccurred())
+	})
 })
 })
+
+func updateRemoteSecretValue(f *framework.Framework, namespace, name, value string) {
+	var secret corev1.Secret
+	Expect(f.CRClient.Get(context.Background(), types.NamespacedName{
+		Name:      name,
+		Namespace: namespace,
+	}, &secret)).To(Succeed())
+
+	secret.Data["value"] = []byte(value)
+	Expect(f.CRClient.Update(context.Background(), &secret)).To(Succeed())
+}
+
+func updateKubernetesProviderServiceAccount(f *framework.Framework, namespace, name, serviceAccountName string) {
+	var providerConfig k8sv2alpha1.Kubernetes
+	Expect(f.CRClient.Get(context.Background(), types.NamespacedName{
+		Name:      name,
+		Namespace: namespace,
+	}, &providerConfig)).To(Succeed())
+
+	if providerConfig.Spec.Auth == nil || providerConfig.Spec.Auth.ServiceAccount == nil {
+		providerConfig.Spec.Auth = &esv1.KubernetesAuth{
+			ServiceAccount: &esmeta.ServiceAccountSelector{},
+		}
+	}
+	providerConfig.Spec.Auth.ServiceAccount.Name = serviceAccountName
+	Expect(f.CRClient.Update(context.Background(), &providerConfig)).To(Succeed())
+}
+
+func triggerProviderConnectionReconcile(f *framework.Framework, namespace, name, value string) {
+	var provider esv1.Provider
+	Expect(f.CRClient.Get(context.Background(), types.NamespacedName{
+		Name:      name,
+		Namespace: namespace,
+	}, &provider)).To(Succeed())
+
+	if provider.Annotations == nil {
+		provider.Annotations = make(map[string]string)
+	}
+	provider.Annotations["e2e.external-secrets.io/reconcile"] = value
+	Expect(f.CRClient.Update(context.Background(), &provider)).To(Succeed())
+}
+
+func waitForExternalSecretReadyStatus(f *framework.Framework, namespace, name string, expectedStatus corev1.ConditionStatus) {
+	Eventually(func(g Gomega) {
+		var externalSecret esv1.ExternalSecret
+		g.Expect(f.CRClient.Get(context.Background(), types.NamespacedName{
+			Name:      name,
+			Namespace: namespace,
+		}, &externalSecret)).To(Succeed())
+
+		condition := esv1.GetExternalSecretCondition(externalSecret.Status, esv1.ExternalSecretReady)
+		g.Expect(condition).NotTo(BeNil())
+		g.Expect(condition.Status).To(Equal(expectedStatus))
+	}, defaultV2WaitTimeout, defaultV2PollInterval).Should(Succeed())
+}
+
+func expectSecretToBeAbsent(f *framework.Framework, namespace, name string) {
+	Consistently(func() bool {
+		var secret corev1.Secret
+		err := f.CRClient.Get(context.Background(), types.NamespacedName{
+			Name:      name,
+			Namespace: namespace,
+		}, &secret)
+		return apierrors.IsNotFound(err)
+	}, defaultV2RefreshInterval, defaultV2PollInterval).Should(BeTrue())
+}