Просмотр исходного кода

chore: add regression test for feedback loop

Signed-off-by: Moritz Johner <beller.moritz@googlemail.com>
Moritz Johner 3 месяцев назад
Родитель
Сommit
4fa4585306

+ 117 - 0
e2e/suites/provider/cases/common/regressions.go

@@ -0,0 +1,117 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+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
+
+	https://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 common
+
+import (
+	"time"
+
+	. "github.com/onsi/ginkgo/v2"
+	"github.com/onsi/gomega"
+	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
+
+	"github.com/external-secrets/external-secrets-e2e/framework"
+	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
+)
+
+// StatusNotUpdatedAfterSuccessfulSync validates that a successful sync does not trigger
+// continuous status updates when the refresh interval is long.
+func StatusNotUpdatedAfterSuccessfulSync(f *framework.Framework) (string, func(*framework.TestCase)) {
+	return "[regression] should not continuously update status after a successful sync", func(tc *framework.TestCase) {
+		remoteKey := f.MakeRemoteRefKey(f.Namespace.Name + "-feedback-loop-key")
+		remoteValue := "feedback-loop-value"
+
+		tc.Secrets = map[string]framework.SecretEntry{
+			remoteKey: {Value: remoteValue},
+		}
+		tc.ExpectedSecret = &v1.Secret{
+			Type: v1.SecretTypeOpaque,
+			Data: map[string][]byte{
+				"value": []byte(remoteValue),
+			},
+		}
+		tc.ExternalSecret.ObjectMeta.Name = "feedback-loop-es"
+		tc.ExternalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Hour}
+		tc.ExternalSecret.Spec.Target.Name = framework.TargetSecretName
+		tc.ExternalSecret.Spec.Data = []esv1.ExternalSecretData{
+			{
+				SecretKey: "value",
+				RemoteRef: esv1.ExternalSecretDataRemoteRef{
+					Key: remoteKey,
+				},
+			},
+		}
+		tc.AfterSync = func(_ framework.SecretStoreProvider, _ *v1.Secret) {
+			key := types.NamespacedName{Name: tc.ExternalSecret.Name, Namespace: tc.ExternalSecret.Namespace}
+			var baseline *esv1.ExternalSecret
+
+			Eventually(func() bool {
+				current := &esv1.ExternalSecret{}
+				if err := tc.Framework.CRClient.Get(GinkgoT().Context(), key, current); err != nil {
+					return false
+				}
+
+				ready := getExternalSecretCondition(current.Status, esv1.ExternalSecretReady)
+				if ready == nil || ready.Status != v1.ConditionTrue {
+					return false
+				}
+				if current.Status.RefreshTime.IsZero() || current.Status.SyncedResourceVersion == "" {
+					return false
+				}
+
+				if baseline == nil {
+					baseline = current.DeepCopy()
+					return false
+				}
+
+				if current.ResourceVersion != baseline.ResourceVersion {
+					baseline = current.DeepCopy()
+					return false
+				}
+				if !current.Status.RefreshTime.Equal(&baseline.Status.RefreshTime) {
+					baseline = current.DeepCopy()
+					return false
+				}
+				if current.Status.SyncedResourceVersion != baseline.Status.SyncedResourceVersion {
+					baseline = current.DeepCopy()
+					return false
+				}
+
+				return true
+			}).WithTimeout(30 * time.Second).WithPolling(500 * time.Millisecond).Should(BeTrue())
+
+			Consistently(func(g gomega.Gomega) {
+				current := &esv1.ExternalSecret{}
+				g.Expect(tc.Framework.CRClient.Get(GinkgoT().Context(), key, current)).To(Succeed())
+				g.Expect(current.ResourceVersion).To(Equal(baseline.ResourceVersion))
+				g.Expect(current.Status.RefreshTime.Equal(&baseline.Status.RefreshTime)).To(BeTrue())
+				g.Expect(current.Status.SyncedResourceVersion).To(Equal(baseline.Status.SyncedResourceVersion))
+			}).WithTimeout(10 * time.Second).WithPolling(500 * time.Millisecond).Should(Succeed())
+		}
+	}
+}
+
+func getExternalSecretCondition(status esv1.ExternalSecretStatus, condType esv1.ExternalSecretConditionType) *esv1.ExternalSecretStatusCondition {
+	for i := range status.Conditions {
+		c := status.Conditions[i]
+		if c.Type == condType {
+			return &c
+		}
+	}
+	return nil
+}

+ 34 - 0
e2e/suites/provider/cases/fake/regressions.go

@@ -0,0 +1,34 @@
+/*
+Copyright © 2025 ESO Maintainer Team
+
+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
+
+    https://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 fake
+
+import (
+	"github.com/external-secrets/external-secrets-e2e/framework"
+	"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
+	. "github.com/onsi/ginkgo/v2"
+)
+
+var _ = Describe("[fake] ", Label("fake"), func() {
+	f := framework.New("eso-fake")
+	prov := NewProvider(f)
+
+	// we use the fake provider to test regressions
+	DescribeTable("controller regressions",
+		framework.TableFuncWithExternalSecret(f, prov),
+		Entry(common.StatusNotUpdatedAfterSuccessfulSync(f)),
+	)
+})

+ 1 - 0
e2e/suites/provider/cases/import.go

@@ -24,6 +24,7 @@ import (
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/azure"
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/conjur"
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/delinea"
+	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/fake"
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/gcp"
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/kubernetes"
 	_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/scaleway"