diff --git a/controllers/cel/cel_drpolicy_test.go b/controllers/cel/cel_drpolicy_test.go new file mode 100644 index 000000000..3c50818a3 --- /dev/null +++ b/controllers/cel/cel_drpolicy_test.go @@ -0,0 +1,139 @@ +package cel_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + // gomegaTypes "github.com/onsi/gomega/types" + ramen "github.com/ramendr/ramen/api/v1alpha1" + // corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation/field" + // validationErrors "k8s.io/kube-openapi/pkg/validation/errors" + // "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("DRPolicy-CEL", func() { + drpolicyCreate := func(drpolicy *ramen.DRPolicy) { + Expect(k8sClient.Create(context.TODO(), drpolicy)).To(Succeed()) + } + + getDRPolicy := func(drpolicy *ramen.DRPolicy) *ramen.DRPolicy { + drp := &ramen.DRPolicy{} + err := k8sClient.Get(context.TODO(), + types.NamespacedName{Name: drpolicy.Name}, + drp) + + Expect(err).NotTo(HaveOccurred()) + + return drp + } + + dRClusters := []string{ + "dr-cluster-0", + "dr-cluster-1", + } + + drpolicies := [...]ramen.DRPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "drpolicy0"}, + Spec: ramen.DRPolicySpec{}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "drpolicy1"}, + Spec: ramen.DRPolicySpec{DRClusters: dRClusters[0:2], SchedulingInterval: `3m`}, + }, + } + + drpolicyDeleteAndConfirm := func(drpolicy *ramen.DRPolicy) { + Expect(k8sClient.Delete(context.TODO(), drpolicy)).To(Succeed()) + Eventually(func() bool { + return errors.IsNotFound(k8sClient.Get(context.TODO(), types.NamespacedName{Name: drpolicy.Name}, drpolicy)) + }, timeout, interval).Should(BeTrue()) + } + drpolicyDelete := func(drpolicy *ramen.DRPolicy) { + drpolicyDeleteAndConfirm(drpolicy) + } + + When("a valid DRPolicy is given", func() { + It("should return success", func() { + drp := drpolicies[1].DeepCopy() + drpolicyCreate(drp) + getDRPolicy(drp) + drpolicyDelete(drp) + }) + }) + + When("a drpolicy having no drclusters", func() { + It("should fail to create drpolicy", func() { + drp := drpolicies[0].DeepCopy() + drp.Spec.DRClusters = nil + err := func() *errors.StatusError { + path := field.NewPath("spec", "drClusters") + + return errors.NewInvalid( + schema.GroupKind{ + Group: ramen.GroupVersion.Group, + Kind: "DRPolicy", + }, + drp.Name, + field.ErrorList{ + field.Required( + path, + "", + ), + field.Invalid(nil, "null", + "some validation rules were not checked because the object was invalid; "+ + "correct the existing errors to complete validation"), + }, + ) + }() + Expect(k8sClient.Create(context.TODO(), drp)).To(MatchError(err)) + }) + }) + + When("a DRPolicy having empty list in DRClusters", func() { + It("should not create DRPolicy", func() { + drp := drpolicies[0].DeepCopy() + drp.Spec.DRClusters = []string{} + Expect(k8sClient.Create(context.TODO(), drp)).NotTo(Succeed()) + }) + }) + + When("a DRPolicy having only one cluster in DRClusters", func() { + It("should not create DRPolicy", func() { + drp := drpolicies[0].DeepCopy() + drp.Spec.DRClusters = dRClusters[0:1] + Expect(k8sClient.Create(context.TODO(), drp)).NotTo(Succeed()) + }) + }) + + When("a valid DRPolicy is created", func() { + It("should return with error on modifying DRCluster field", func() { + drp := drpolicies[1].DeepCopy() + drpolicyCreate(drp) + drp.Spec.DRClusters = []string{ + "dr-cluster1-not-exists", + "dr-cluster2-not-exists", + } + + Expect(k8sClient.Update(context.TODO(), drp)).NotTo(Succeed()) + drpolicyDelete(drp) + }) + }) + + When("a valid DRPolicy is created", func() { + It("should not update on modifying schedulingInterval field", func() { + drp := drpolicies[1].DeepCopy() + drpolicyCreate(drp) + drp.Spec.SchedulingInterval = "6m" + Expect(k8sClient.Update(context.TODO(), drp)).NotTo(Succeed()) + drpolicyDelete(drp) + }) + }) +}) diff --git a/controllers/cel/cel_suite_test.go b/controllers/cel/cel_suite_test.go new file mode 100644 index 000000000..ef2ea0338 --- /dev/null +++ b/controllers/cel/cel_suite_test.go @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: The RamenDR authors +// SPDX-License-Identifier: Apache-2.0 + +package cel_test + +import ( + // "context" + "os" + "path/filepath" + "testing" + "time" + + "github.com/go-logr/logr" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" + ramendrv1alpha1 "github.com/ramendr/ramen/api/v1alpha1" + // "github.com/ramendr/ramen/controllers/util" + "go.uber.org/zap/zapcore" + "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/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +const ( + timeout = time.Second * 10 + interval = time.Millisecond * 10 +) + +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment + testLogger logr.Logger +) + +func TestUtil(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "CEL Suite") +} + +var _ = BeforeSuite(func() { + // onsi.github.io/gomega/#adjusting-output + format.MaxLength = 0 + testLogger = zap.New(zap.UseFlagOptions(&zap.Options{ + Development: true, + DestWriter: GinkgoWriter, + TimeEncoder: zapcore.ISO8601TimeEncoder, + })) + logf.SetLogger(testLogger) + testLog := ctrl.Log.WithName("tester") + testLog.Info("Starting the CEL test suite", "time", time.Now()) + + By("Setting up KUBEBUILDER_ASSETS for envtest") + if _, set := os.LookupEnv("KUBEBUILDER_ASSETS"); !set { + testLog.Info("Setting up KUBEBUILDER_ASSETS for envtest") + + // read content of the file ../../testbin/testassets.txt + // and set the content as the value of KUBEBUILDER_ASSETS + // this is to avoid the need to set KUBEBUILDER_ASSETS + // when running the test suite + content, err := os.ReadFile("../../testbin/testassets.txt") + Expect(err).NotTo(HaveOccurred()) + Expect(os.Setenv("KUBEBUILDER_ASSETS", string(content))).To(Succeed()) + } + + By("Bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{ + filepath.Join("..", "..", "config", "crd", "bases"), + filepath.Join("..", "..", "hack", "test"), + }, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + By("Setting up required schemes in envtest") + err = ramendrv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + By("Creating a k8s client") + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +})