From f14b50498bf8ffbb0462764be3ce3b5d26771f94 Mon Sep 17 00:00:00 2001 From: Yannick Struyf Date: Thu, 30 Nov 2023 09:37:46 +0100 Subject: [PATCH] Add unit tests for failure domains --- controllers/helpers_test.go | 123 +++++++++++++++ controllers/nutanixcluster_controller_test.go | 142 +++++++++++++++--- 2 files changed, 244 insertions(+), 21 deletions(-) create mode 100644 controllers/helpers_test.go diff --git a/controllers/helpers_test.go b/controllers/helpers_test.go new file mode 100644 index 0000000000..c4a12bf4d0 --- /dev/null +++ b/controllers/helpers_test.go @@ -0,0 +1,123 @@ +/* +Copyright 2023 Nutanix + +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 + + http://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 controllers + +import ( + "context" + "testing" + + credentialTypes "github.com/nutanix-cloud-native/prism-go-client/environment/credentials" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/cluster-api/util" + + infrav1 "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestControllerHelpers(t *testing.T) { + g := NewWithT(t) + + _ = Describe("ControllerHelpers", func() { + const ( + fd1Name = "fd-1" + fd2Name = "fd-2" + ) + + var ( + ntnxCluster *infrav1.NutanixCluster + ctx context.Context + ) + + BeforeEach(func() { + ctx = context.Background() + ntnxCluster = &infrav1.NutanixCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: infrav1.NutanixClusterSpec{ + PrismCentral: &credentialTypes.NutanixPrismEndpoint{ + // Adding port info to override default value (0) + Port: 9440, + }, + }, + } + }) + + AfterEach(func() { + err := k8sClient.Delete(ctx, ntnxCluster) + Expect(err).NotTo(HaveOccurred()) + }) + + Context("Get failure domains", func() { + It("should error when passing empty failure domain name", func() { + g.Expect(k8sClient.Create(ctx, ntnxCluster)).To(Succeed()) + _, err := GetFailureDomain("", ntnxCluster) + Expect(err).To(HaveOccurred()) + }) + It("should error when passing nil cluster", func() { + g.Expect(k8sClient.Create(ctx, ntnxCluster)).To(Succeed()) + _, err := GetFailureDomain(fd1Name, nil) + Expect(err).To(HaveOccurred()) + }) + It("should error when no failure domain has been found", func() { + g.Expect(k8sClient.Create(ctx, ntnxCluster)).To(Succeed()) + _, err := GetFailureDomain(fd1Name, ntnxCluster) + Expect(err).To(HaveOccurred()) + }) + It("should return the correct failuredomain", func() { + r := util.RandomString(10) + fd1 := infrav1.NutanixFailureDomain{ + Name: fd1Name, + Cluster: infrav1.NutanixResourceIdentifier{ + Type: infrav1.NutanixIdentifierName, + Name: &r, + }, + Subnets: []infrav1.NutanixResourceIdentifier{ + { + Type: infrav1.NutanixIdentifierName, + Name: &r, + }, + }, + } + fd2 := infrav1.NutanixFailureDomain{ + Name: fd2Name, + Cluster: infrav1.NutanixResourceIdentifier{ + Type: infrav1.NutanixIdentifierName, + Name: &r, + }, + Subnets: []infrav1.NutanixResourceIdentifier{ + { + Type: infrav1.NutanixIdentifierName, + Name: &r, + }, + }, + } + ntnxCluster.Spec.FailureDomains = []infrav1.NutanixFailureDomain{ + fd1, + fd2, + } + g.Expect(k8sClient.Create(ctx, ntnxCluster)).To(Succeed()) + fd, err := GetFailureDomain(fd2Name, ntnxCluster) + Expect(err).ToNot(HaveOccurred()) + Expect(*fd).To(Equal(fd2)) + }) + }) + }) +} diff --git a/controllers/nutanixcluster_controller_test.go b/controllers/nutanixcluster_controller_test.go index dbfecd397c..c416a66533 100644 --- a/controllers/nutanixcluster_controller_test.go +++ b/controllers/nutanixcluster_controller_test.go @@ -21,48 +21,80 @@ import ( "testing" credentialTypes "github.com/nutanix-cloud-native/prism-go-client/environment/credentials" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/cluster-api/util" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" infrav1 "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1" + nctx "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/pkg/context" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/onsi/gomega/gstruct" ) func TestNutanixClusterReconciler(t *testing.T) { g := NewWithT(t) _ = Describe("NutanixClusterReconciler", func() { - Context("Reconcile an NutanixCluster", func() { - It("should not error and not requeue the request", func() { - ctx := context.Background() - reconciler := &NutanixClusterReconciler{ - Client: k8sClient, - Scheme: runtime.NewScheme(), - } + const ( + fd1Name = "fd-1" + fd2Name = "fd-2" + ) - ntnxCluster := &infrav1.NutanixCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", + var ( + ntnxCluster *infrav1.NutanixCluster + ctx context.Context + fd1 infrav1.NutanixFailureDomain + reconciler *NutanixClusterReconciler + ) + + BeforeEach(func() { + ctx = context.Background() + ntnxCluster = &infrav1.NutanixCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: infrav1.NutanixClusterSpec{ + PrismCentral: &credentialTypes.NutanixPrismEndpoint{ + // Adding port info to override default value (0) + Port: 9440, }, - Spec: infrav1.NutanixClusterSpec{ - PrismCentral: &credentialTypes.NutanixPrismEndpoint{ - // Adding port info to override default value (0) - Port: 9440, - }, + }, + } + r := util.RandomString(10) + fd1 = infrav1.NutanixFailureDomain{ + Name: fd1Name, + Cluster: infrav1.NutanixResourceIdentifier{ + Type: infrav1.NutanixIdentifierName, + Name: &r, + }, + Subnets: []infrav1.NutanixResourceIdentifier{ + { + Type: infrav1.NutanixIdentifierName, + Name: &r, }, - } + }, + } + reconciler = &NutanixClusterReconciler{ + Client: k8sClient, + Scheme: runtime.NewScheme(), + } + }) + + AfterEach(func() { + err := k8sClient.Delete(ctx, ntnxCluster) + Expect(err).NotTo(HaveOccurred()) + }) + Context("Reconcile an NutanixCluster", func() { + It("should not error and not requeue the request", func() { // Create the NutanixCluster object and expect the Reconcile to be created g.Expect(k8sClient.Create(ctx, ntnxCluster)).To(Succeed()) - defer func() { - err := k8sClient.Delete(ctx, ntnxCluster) - Expect(err).NotTo(HaveOccurred()) - }() result, err := reconciler.Reconcile(ctx, ctrl.Request{ NamespacedName: client.ObjectKey{ @@ -75,5 +107,73 @@ func TestNutanixClusterReconciler(t *testing.T) { g.Expect(result.Requeue).To(BeFalse()) }) }) + + Context("Reconcile failure domains", func() { + It("sets the failure domains in the nutanixcluster status and failure domain reconciled condition", func() { + ntnxCluster.Spec.FailureDomains = []infrav1.NutanixFailureDomain{ + fd1, + } + + // Create the NutanixCluster object and expect the Reconcile to be created + g.Expect(k8sClient.Create(ctx, ntnxCluster)).To(Succeed()) + // Retrieve the applied nutanix cluster objects + appliedNtnxCluster := &infrav1.NutanixCluster{} + k8sClient.Get(ctx, client.ObjectKey{ + Namespace: ntnxCluster.Namespace, + Name: ntnxCluster.Name, + }, appliedNtnxCluster) + + err := reconciler.reconcileFailureDomains(&nctx.ClusterContext{ + Context: ctx, + NutanixCluster: appliedNtnxCluster, + }) + g.Expect(err).NotTo(HaveOccurred()) + + g.Expect(appliedNtnxCluster.Status.Conditions).To(ContainElement( + gstruct.MatchFields( + gstruct.IgnoreExtras, + gstruct.Fields{ + "Type": Equal(infrav1.FailureDomainsReconciled), + "Status": Equal(corev1.ConditionTrue), + }, + ), + )) + g.Expect(appliedNtnxCluster.Status.FailureDomains).To(HaveKey(fd1Name)) + g.Expect(appliedNtnxCluster.Status.FailureDomains[fd1Name]).To(gstruct.MatchFields( + gstruct.IgnoreExtras, + gstruct.Fields{ + "ControlPlane": Equal(fd1.ControlPlane), + }, + )) + }) + + It("sets the NoFailureDomainsReconciled condition when no failure domains are set", func() { + // Create the NutanixCluster object and expect the Reconcile to be created + g.Expect(k8sClient.Create(ctx, ntnxCluster)).To(Succeed()) + // Retrieve the applied nutanix cluster objects + appliedNtnxCluster := &infrav1.NutanixCluster{} + k8sClient.Get(ctx, client.ObjectKey{ + Namespace: ntnxCluster.Namespace, + Name: ntnxCluster.Name, + }, appliedNtnxCluster) + + err := reconciler.reconcileFailureDomains(&nctx.ClusterContext{ + Context: ctx, + NutanixCluster: appliedNtnxCluster, + }) + g.Expect(err).NotTo(HaveOccurred()) + + g.Expect(appliedNtnxCluster.Status.Conditions).To(ContainElement( + gstruct.MatchFields( + gstruct.IgnoreExtras, + gstruct.Fields{ + "Type": Equal(infrav1.NoFailureDomainsReconciled), + "Status": Equal(corev1.ConditionTrue), + }, + ), + )) + g.Expect(appliedNtnxCluster.Status.FailureDomains).To(BeEmpty()) + }) + }) }) }