From d1752a2b9adf5d7e25ccb25cd2b9c66c782808e3 Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Tue, 20 Aug 2024 17:16:18 +0200 Subject: [PATCH] add unit tests --- .../vspheremachinetemplate_controller.go | 8 +- .../vspheremachinetemplate_controller_test.go | 185 ++++++++++++++++++ 2 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 controllers/vmware/vspheremachinetemplate_controller_test.go diff --git a/controllers/vmware/vspheremachinetemplate_controller.go b/controllers/vmware/vspheremachinetemplate_controller.go index 87830b884a..250948f9b4 100644 --- a/controllers/vmware/vspheremachinetemplate_controller.go +++ b/controllers/vmware/vspheremachinetemplate_controller.go @@ -87,8 +87,12 @@ func (r *vSphereMachineTemplateReconciler) Reconcile(ctx context.Context, req ct if vSphereMachineTemplate.Status.Capacity == nil { vSphereMachineTemplate.Status.Capacity = corev1.ResourceList{} } - vSphereMachineTemplate.Status.Capacity[vmwarev1.VSphereResourceCPU] = *resource.NewQuantity(vmClass.Spec.Hardware.Cpus, resource.DecimalSI) - vSphereMachineTemplate.Status.Capacity[vmwarev1.VSphereResourceMemory] = vmClass.Spec.Hardware.Memory + if vmClass.Spec.Hardware.Cpus > 0 { + vSphereMachineTemplate.Status.Capacity[vmwarev1.VSphereResourceCPU] = *resource.NewQuantity(vmClass.Spec.Hardware.Cpus, resource.DecimalSI) + } + if !vmClass.Spec.Hardware.Memory.IsZero() { + vSphereMachineTemplate.Status.Capacity[vmwarev1.VSphereResourceMemory] = vmClass.Spec.Hardware.Memory + } return reconcile.Result{}, patchHelper.Patch(ctx, vSphereMachineTemplate) } diff --git a/controllers/vmware/vspheremachinetemplate_controller_test.go b/controllers/vmware/vspheremachinetemplate_controller_test.go new file mode 100644 index 0000000000..78fa8f399f --- /dev/null +++ b/controllers/vmware/vspheremachinetemplate_controller_test.go @@ -0,0 +1,185 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 vmware + +import ( + "context" + "testing" + + . "github.com/onsi/gomega" + vmoprv1 "github.com/vmware-tanzu/vm-operator/api/v1alpha2" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + vmwarev1 "sigs.k8s.io/cluster-api-provider-vsphere/apis/vmware/v1beta1" +) + +func Test_vSphereMachineTemplateReconciler_Reconcile(t *testing.T) { + g := NewWithT(t) + ctx := context.Background() + + scheme := runtime.NewScheme() + g.Expect(corev1.AddToScheme(scheme)).To(Succeed()) + g.Expect(vmwarev1.AddToScheme(scheme)).To(Succeed()) + g.Expect(vmoprv1.AddToScheme(scheme)).To(Succeed()) + + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + + tests := []struct { + name string + vSphereMachineTemplate *vmwarev1.VSphereMachineTemplate + objects []client.Object + wantErr bool + wantStatus *vmwarev1.VSphereMachineTemplateStatus + }{ + { + name: "object does not exist", + vSphereMachineTemplate: nil, + objects: []client.Object{}, + wantErr: false, + wantStatus: nil, + }, + { + name: "VirtualMachineClass does not exist", + vSphereMachineTemplate: vSphereMachineTemplate(namespace.Name, "no-class", "not-existing-class", nil), + objects: []client.Object{}, + wantErr: true, + wantStatus: nil, + }, + { + name: "VirtualMachineClass does exist but has no data", + vSphereMachineTemplate: vSphereMachineTemplate(namespace.Name, "with-class", "vm-class", nil), + objects: []client.Object{ + virtualMachineClass(namespace.Name, "vm-class", nil), + }, + wantErr: false, + wantStatus: &vmwarev1.VSphereMachineTemplateStatus{}, + }, + { + name: "VirtualMachineClass does exist and has cpu and memory set", + vSphereMachineTemplate: vSphereMachineTemplate(namespace.Name, "with-class", "vm-class", nil), + objects: []client.Object{ + virtualMachineClass(namespace.Name, "vm-class", &vmoprv1.VirtualMachineClassHardware{Cpus: 1, Memory: *resource.NewQuantity(1024, resource.DecimalSI)}), + }, + wantErr: false, + wantStatus: &vmwarev1.VSphereMachineTemplateStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceCPU: quantity(1), + corev1.ResourceMemory: quantity(1024), + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + fakeClientBuilder := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(append([]client.Object{namespace}, tt.objects...)...) + + vSphereMachineTemplateName := "not-exists" + if tt.vSphereMachineTemplate != nil { + vSphereMachineTemplateName = tt.vSphereMachineTemplate.GetName() + fakeClientBuilder = fakeClientBuilder. + WithObjects(tt.vSphereMachineTemplate). + WithStatusSubresource(tt.vSphereMachineTemplate) + } + + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: vSphereMachineTemplateName, + Namespace: namespace.Name, + }, + } + + r := &vSphereMachineTemplateReconciler{ + Client: fakeClientBuilder.Build(), + } + + vSphereMachineTemplateBefore := &vmwarev1.VSphereMachineTemplate{} + if tt.wantStatus != nil { + g.Expect(r.Client.Get(ctx, req.NamespacedName, vSphereMachineTemplateBefore)).To(Succeed()) + _ = vSphereMachineTemplateBefore + } + + _, err := r.Reconcile(ctx, req) + if (err != nil) != tt.wantErr { + g.Expect(err).ToNot(HaveOccurred()) + } + + if tt.wantStatus != nil { + vSphereMachineTemplate := &vmwarev1.VSphereMachineTemplate{} + g.Expect(r.Client.Get(ctx, req.NamespacedName, vSphereMachineTemplate)).To(Succeed()) + g.Expect(vSphereMachineTemplate.Status).To(BeEquivalentTo(*tt.wantStatus)) + } + }) + } +} + +func vSphereMachineTemplate(namespace, name, className string, status *vmwarev1.VSphereMachineTemplateStatus) *vmwarev1.VSphereMachineTemplate { + tpl := &vmwarev1.VSphereMachineTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: vmwarev1.VSphereMachineTemplateSpec{ + Template: vmwarev1.VSphereMachineTemplateResource{ + Spec: vmwarev1.VSphereMachineSpec{ + ClassName: className, + }, + }, + }, + } + + if status != nil { + tpl.Status = *status + } + + return tpl +} + +func virtualMachineClass(namespace, name string, hardware *vmoprv1.VirtualMachineClassHardware) *vmoprv1.VirtualMachineClass { + class := &vmoprv1.VirtualMachineClass{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + } + + if hardware != nil { + class.Spec.Hardware = *hardware + } + + return class +} + +func quantity(i int64) resource.Quantity { + q := resource.NewQuantity(i, resource.DecimalSI) + // Execute q.String to populate the internal s field + _ = q.String() + return *q +}