From e3fbe90e65681f2e45b1dc92dc39f0ad0c47c45b Mon Sep 17 00:00:00 2001 From: Shilpa-Gokul Date: Mon, 4 Nov 2024 14:57:49 +0530 Subject: [PATCH] Added UT for reconcilePowerVSResources function --- .../ibmpowervscluster_controller_test.go | 180 ++++++++++++++++++ go.mod | 2 +- 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/controllers/ibmpowervscluster_controller_test.go b/controllers/ibmpowervscluster_controller_test.go index efc13ae78..daeaa0e4d 100644 --- a/controllers/ibmpowervscluster_controller_test.go +++ b/controllers/ibmpowervscluster_controller_test.go @@ -17,11 +17,17 @@ limitations under the License. package controllers import ( + "errors" "fmt" + "sync" "testing" "time" + "github.com/IBM-Cloud/power-go-client/power/models" "github.com/IBM/go-sdk-core/v5/core" + "github.com/IBM/platform-services-go-sdk/resourcecontrollerv2" + "github.com/google/go-cmp/cmp" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -32,6 +38,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" capiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" "sigs.k8s.io/cluster-api/util" @@ -39,6 +46,8 @@ import ( infrav1beta2 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta2" "sigs.k8s.io/cluster-api-provider-ibmcloud/cloud/scope" "sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/powervs" + mockP "sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/powervs/mock" + mockRC "sigs.k8s.io/cluster-api-provider-ibmcloud/pkg/cloud/services/resourcecontroller/mock" . "github.com/onsi/gomega" ) @@ -456,3 +465,174 @@ func cleanupCluster(g *WithT, powervsCluster *infrav1beta2.IBMPowerVSCluster, na }(powervsCluster, namespace) } } + +func TestReconcilePowerVSResources(t *testing.T) { + testCases := []struct { + name string + powerVSClusterScopeFunc func() *scope.PowerVSClusterScope + reconcileResult reconcileResult + conditions capiv1beta1.Conditions + }{ + { + name: "when Reconciling PowerVS service instance returns error", + powerVSClusterScopeFunc: func() *scope.PowerVSClusterScope { + clusterScope := &scope.PowerVSClusterScope{ + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + ServiceInstance: &infrav1beta2.ResourceReference{ + ID: ptr.To("serviceInstanceID"), + }, + }, + }, + } + mockResourceController := mockRC.NewMockResourceController(gomock.NewController(t)) + mockResourceController.EXPECT().GetResourceInstance(gomock.Any()).Return(nil, nil, fmt.Errorf("error getting resource instance")) + clusterScope.ResourceClient = mockResourceController + return clusterScope + }, + reconcileResult: reconcileResult{ + error: errors.New("error getting resource instance"), + }, + + conditions: capiv1beta1.Conditions{ + capiv1beta1.Condition{ + Type: infrav1beta2.ServiceInstanceReadyCondition, + Status: "False", + Severity: capiv1beta1.ConditionSeverityError, + LastTransitionTime: metav1.Time{}, + Reason: infrav1beta2.ServiceInstanceReconciliationFailedReason, + Message: "error getting resource instance", + }, + }, + }, + { + name: "when Reconciling PowerVS service instance returns requeue", + powerVSClusterScopeFunc: func() *scope.PowerVSClusterScope { + clusterScope := &scope.PowerVSClusterScope{ + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Status: infrav1beta2.IBMPowerVSClusterStatus{ + ServiceInstance: &infrav1beta2.ResourceReference{ + ID: ptr.To("serviceInstanceID"), + }, + }, + }, + } + mockResourceController := mockRC.NewMockResourceController(gomock.NewController(t)) + mockResourceController.EXPECT().GetResourceInstance(gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{State: (*string)(&infrav1beta2.ServiceInstanceStateProvisioning), Name: ptr.To("serviceInstanceName")}, nil, nil) + clusterScope.ResourceClient = mockResourceController + return clusterScope + }, + reconcileResult: reconcileResult{ + Result: reconcile.Result{ + Requeue: true, + }, + }, + }, + { + name: "when Reconciling network returns requeue", + powerVSClusterScopeFunc: func() *scope.PowerVSClusterScope { + clusterScope := &scope.PowerVSClusterScope{ + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + ServiceInstanceID: "serviceInstanceID", + }, + Status: infrav1beta2.IBMPowerVSClusterStatus{ + DHCPServer: &infrav1beta2.ResourceReference{ID: ptr.To("DHCPServerID")}, + ServiceInstance: &infrav1beta2.ResourceReference{ID: ptr.To("serviceInstanceID")}, + }, + }, + } + mockPowerVS := mockP.NewMockPowerVS(gomock.NewController(t)) + dhcpServer := &models.DHCPServerDetail{ID: ptr.To("dhcpID"), Status: ptr.To(string(infrav1beta2.DHCPServerStateError))} + mockPowerVS.EXPECT().GetDHCPServer(gomock.Any()).Return(dhcpServer, nil) + mockPowerVS.EXPECT().WithClients(gomock.Any()) + mockResourceController := mockRC.NewMockResourceController(gomock.NewController(t)) + mockResourceController.EXPECT().GetResourceInstance(gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{State: (*string)(&infrav1beta2.ServiceInstanceStateActive), Name: ptr.To("serviceInstanceName")}, nil, nil) + clusterScope.ResourceClient = mockResourceController + clusterScope.IBMPowerVSClient = mockPowerVS + return clusterScope + }, + reconcileResult: reconcileResult{ + error: errors.New("DHCP server creation failed and is in error state"), + }, + conditions: capiv1beta1.Conditions{ + capiv1beta1.Condition{ + Type: infrav1beta2.NetworkReadyCondition, + Status: "False", + Severity: capiv1beta1.ConditionSeverityError, + LastTransitionTime: metav1.Time{}, + Reason: infrav1beta2.NetworkReconciliationFailedReason, + Message: "DHCP server creation failed and is in error state", + }, + getServiceInstanceReadyCondition(), + }, + }, + { + name: "when dhcpServer is active", + powerVSClusterScopeFunc: func() *scope.PowerVSClusterScope { + clusterScope := &scope.PowerVSClusterScope{ + IBMPowerVSCluster: &infrav1beta2.IBMPowerVSCluster{ + Spec: infrav1beta2.IBMPowerVSClusterSpec{ + ServiceInstanceID: "serviceInstanceID", + }, + Status: infrav1beta2.IBMPowerVSClusterStatus{ + DHCPServer: &infrav1beta2.ResourceReference{ID: ptr.To("DHCPServerID")}, + ServiceInstance: &infrav1beta2.ResourceReference{ID: ptr.To("serviceInstanceID")}, + }, + }, + } + mockPowerVS := mockP.NewMockPowerVS(gomock.NewController(t)) + dhcpServer := &models.DHCPServerDetail{ID: ptr.To("dhcpID"), Status: ptr.To(string(infrav1beta2.DHCPServerStateActive))} + mockPowerVS.EXPECT().GetDHCPServer(gomock.Any()).Return(dhcpServer, nil) + mockPowerVS.EXPECT().WithClients(gomock.Any()) + mockResourceController := mockRC.NewMockResourceController(gomock.NewController(t)) + mockResourceController.EXPECT().GetResourceInstance(gomock.Any()).Return(&resourcecontrollerv2.ResourceInstance{State: (*string)(&infrav1beta2.ServiceInstanceStateActive), Name: ptr.To("serviceInstanceName")}, nil, nil) + clusterScope.ResourceClient = mockResourceController + clusterScope.IBMPowerVSClient = mockPowerVS + return clusterScope + }, + conditions: capiv1beta1.Conditions{ + getNetworkReadyCondition(), + getServiceInstanceReadyCondition(), + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + reconciler := &IBMPowerVSClusterReconciler{ + Client: testEnv.Client, + } + clusterScope := tc.powerVSClusterScopeFunc() + ch := make(chan reconcileResult, 1) + pvsCluster := &powerVSCluster{ + cluster: clusterScope.IBMPowerVSCluster, + } + wg := &sync.WaitGroup{} + wg.Add(1) + reconciler.reconcilePowerVSResources(clusterScope, pvsCluster, ch, wg) + wg.Wait() + close(ch) + g.Expect(<-ch).To(Equal(tc.reconcileResult)) + if len(tc.conditions) > 0 { + ignoreLastTransitionTime := cmp.Transformer("", func(metav1.Time) metav1.Time { + return metav1.Time{} + }) + g.Expect(pvsCluster.cluster.GetConditions()).To(BeComparableTo(tc.conditions, ignoreLastTransitionTime)) + } + }) + } +} + +func getServiceInstanceReadyCondition() capiv1beta1.Condition { + return capiv1beta1.Condition{ + Type: infrav1beta2.ServiceInstanceReadyCondition, + Status: "True", + } +} +func getNetworkReadyCondition() capiv1beta1.Condition { + return capiv1beta1.Condition{ + Type: infrav1beta2.NetworkReadyCondition, + Status: "True", + } +} diff --git a/go.mod b/go.mod index d3875e275..9273e9086 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/go-logr/logr v1.4.2 github.com/go-openapi/strfmt v0.23.0 github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/google/go-cmp v0.6.0 github.com/onsi/ginkgo/v2 v2.19.1 github.com/onsi/gomega v1.34.0 github.com/pkg/errors v0.9.1 @@ -101,7 +102,6 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/cel-go v0.17.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v53 v53.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect