diff --git a/Makefile b/Makefile index b5f4952359..b1e0065a3a 100644 --- a/Makefile +++ b/Makefile @@ -400,6 +400,7 @@ generate-e2e-templates-main: $(KUSTOMIZE) ## Generate test templates for the mai "$(KUSTOMIZE)" --load-restrictor LoadRestrictionsNone build "$(E2E_GOVMOMI_TEMPLATE_DIR)/main/ownerrefs-finalizers" > "$(E2E_GOVMOMI_TEMPLATE_DIR)/main/cluster-template-ownerrefs-finalizers.yaml" # for IPAM tests "$(KUSTOMIZE)" --load-restrictor LoadRestrictionsNone build "$(E2E_GOVMOMI_TEMPLATE_DIR)/main/ipam" > "$(E2E_GOVMOMI_TEMPLATE_DIR)/main/cluster-template-ipam.yaml" + "$(KUSTOMIZE)" --load-restrictor LoadRestrictionsNone build "$(E2E_GOVMOMI_TEMPLATE_DIR)/main/memory-reservation-locked" > "$(E2E_GOVMOMI_TEMPLATE_DIR)/main/cluster-template-memory-reservation-locked.yaml" # generate supervisor templates cp "$(RELEASE_DIR)/main/cluster-template-supervisor.yaml" "$(E2E_SUPERVISOR_TEMPLATE_DIR)/main/base/cluster-template-supervisor.yaml" "$(KUSTOMIZE)" --load-restrictor LoadRestrictionsNone build "$(E2E_SUPERVISOR_TEMPLATE_DIR)/main/base" > "$(E2E_SUPERVISOR_TEMPLATE_DIR)/main/cluster-template-supervisor.yaml" diff --git a/apis/v1beta1/types.go b/apis/v1beta1/types.go index 38bf6b5f81..e032aac003 100644 --- a/apis/v1beta1/types.go +++ b/apis/v1beta1/types.go @@ -172,6 +172,13 @@ type VirtualMachineCloneSpec struct { // virtual machine is cloned. // +optional MemoryMiB int64 `json:"memoryMiB,omitempty"` + // MemoryReservationLockedToMax is a flag that indicates whether or not the + // memory resource reservation for this virtual machine will always be + // equal to the virtual machine's memory size. + // Defaults to the eponymous property value in the template from which the + // virtual machine is cloned. + // +optional + MemoryReservationLockedToMax *bool `json:"memoryReservationLockedToMax,omitempty"` // DiskGiB is the size of a virtual machine's disk, in GiB. // Defaults to the eponymous property value in the template from which the // virtual machine is cloned. diff --git a/apis/v1beta1/zz_generated.deepcopy.go b/apis/v1beta1/zz_generated.deepcopy.go index 44d12a65fe..de88e3784b 100644 --- a/apis/v1beta1/zz_generated.deepcopy.go +++ b/apis/v1beta1/zz_generated.deepcopy.go @@ -1297,6 +1297,11 @@ func (in *VirtualMachine) DeepCopy() *VirtualMachine { func (in *VirtualMachineCloneSpec) DeepCopyInto(out *VirtualMachineCloneSpec) { *out = *in in.Network.DeepCopyInto(&out.Network) + if in.MemoryReservationLockedToMax != nil { + in, out := &in.MemoryReservationLockedToMax, &out.MemoryReservationLockedToMax + *out = new(bool) + **out = **in + } if in.AdditionalDisksGiB != nil { in, out := &in.AdditionalDisksGiB, &out.AdditionalDisksGiB *out = make([]int32, len(*in)) diff --git a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachines.yaml b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachines.yaml index 3f9b8fb56c..4bb03e7240 100644 --- a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachines.yaml +++ b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachines.yaml @@ -119,7 +119,7 @@ spec: Defaults to the eponymous property value in the template from which the virtual machine is cloned. format: int64 - type: integer + type: integer network: description: Network is the network configuration for this machine's VM. @@ -1042,6 +1042,14 @@ spec: virtual machine is cloned. format: int64 type: integer + memoryReservationLockedToMax: + description: |- + MemoryReservationLockedToMax is a flag that indicates + whether or not the memory resource reservation for this virtual + machine will always be equal to the virtual machine's memory size. + Defaults to the eponymous property value in the template from which + the virtual machine is cloned. + type: boolean network: description: Network is the network configuration for this machine's VM. diff --git a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachinetemplates.yaml b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachinetemplates.yaml index 9d72178886..04067dfab2 100644 --- a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachinetemplates.yaml +++ b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachinetemplates.yaml @@ -911,6 +911,14 @@ spec: virtual machine is cloned. format: int64 type: integer + memoryReservationLockedToMax: + description: |- + MemoryReservationLockedToMax is a flag that indicates + whether or not the memory resource reservation for this + virtual machine will always be equal to the virtual machine's + memory size. Defaults to the eponymous property value in + the template from which the virtual machine is cloned. + type: boolean network: description: Network is the network configuration for this machine's VM. diff --git a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspherevms.yaml b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspherevms.yaml index f7c8474262..073d0351d2 100644 --- a/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspherevms.yaml +++ b/config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspherevms.yaml @@ -1130,6 +1130,14 @@ spec: virtual machine is cloned. format: int64 type: integer + memoryReservationLockedToMax: + description: |- + MemoryReservationLockedToMax is a flag that indicates + whether or not the memory resource reservation for this virtual + machine will always be equal to the virtual machine's memory size. + Defaults to the eponymous property value in the template from which + the virtual machine is cloned. + type: boolean network: description: Network is the network configuration for this machine's VM. diff --git a/pkg/services/govmomi/vcenter/clone.go b/pkg/services/govmomi/vcenter/clone.go index 91f64eb28d..4de4bb73d8 100644 --- a/pkg/services/govmomi/vcenter/clone.go +++ b/pkg/services/govmomi/vcenter/clone.go @@ -174,14 +174,15 @@ func Clone(ctx context.Context, vmCtx *capvcontext.VMContext, bootstrapData []by // Assign the clone's InstanceUUID the value of the Kubernetes Machine // object's UID. This allows lookup of the cloned VM prior to knowing // the VM's UUID. - InstanceUuid: string(vmCtx.VSphereVM.UID), - Flags: newVMFlagInfo(), - DeviceChange: deviceSpecs, - ExtraConfig: extraConfig, - NumCPUs: numCPUs, - NumCoresPerSocket: numCoresPerSocket, - MemoryMB: memMiB, - VAppConfigRemoved: &vappConfigRemoved, + InstanceUuid: string(vmCtx.VSphereVM.UID), + Flags: newVMFlagInfo(), + DeviceChange: deviceSpecs, + ExtraConfig: extraConfig, + NumCPUs: numCPUs, + NumCoresPerSocket: numCoresPerSocket, + MemoryMB: memMiB, + MemoryReservationLockedToMax: vmCtx.VSphereVM.Spec.MemoryReservationLockedToMax, + VAppConfigRemoved: &vappConfigRemoved, }, Location: types.VirtualMachineRelocateSpec{ DiskMoveType: string(diskMoveType), diff --git a/test/e2e/config/vsphere.yaml b/test/e2e/config/vsphere.yaml index b818e492b1..9469501da4 100644 --- a/test/e2e/config/vsphere.yaml +++ b/test/e2e/config/vsphere.yaml @@ -182,6 +182,7 @@ providers: - sourcePath: "../../../test/e2e/data/infrastructure-vsphere-govmomi/main/cluster-template.yaml" - sourcePath: "../../../test/e2e/data/infrastructure-vsphere-govmomi/main/clusterclass-quick-start.yaml" - sourcePath: "../../../test/e2e/data/infrastructure-vsphere-govmomi/main/clusterclass-quick-start-runtimesdk.yaml" + - sourcePath: "../../../test/e2e/data/infrastructure-vsphere-govmomi/main/cluster-template-memory-reservation-locked.yaml" - sourcePath: "../../../test/e2e/data/infrastructure-vsphere-supervisor/main/cluster-template-topology-supervisor.yaml" - sourcePath: "../../../test/e2e/data/infrastructure-vsphere-supervisor/main/cluster-template-topology-runtimesdk-supervisor.yaml" - sourcePath: "../../../test/e2e/data/infrastructure-vsphere-supervisor/main/cluster-template-supervisor.yaml" diff --git a/test/e2e/data/infrastructure-vsphere-govmomi/main/memory-reservation-locked/kustomization.yaml b/test/e2e/data/infrastructure-vsphere-govmomi/main/memory-reservation-locked/kustomization.yaml new file mode 100644 index 0000000000..29eab52dea --- /dev/null +++ b/test/e2e/data/infrastructure-vsphere-govmomi/main/memory-reservation-locked/kustomization.yaml @@ -0,0 +1,12 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ../base +patches: + - target: + kind: VSphereMachineTemplate + patch: |- + - op: add + path: /spec/template/spec/memoryReservationLockedToMax + value: true + diff --git a/test/e2e/memory_reservation_locked_test.go b/test/e2e/memory_reservation_locked_test.go new file mode 100644 index 0000000000..3cbd219349 --- /dev/null +++ b/test/e2e/memory_reservation_locked_test.go @@ -0,0 +1,115 @@ +/* +Copyright 2022 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 e2e + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25/mo" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/cluster-api/test/framework" + "sigs.k8s.io/cluster-api/test/framework/clusterctl" + . "sigs.k8s.io/cluster-api/test/framework/ginkgoextensions" + "sigs.k8s.io/cluster-api/util" +) + +type MemoryReservationLockedSpecInput struct { + InfraClients + Global GlobalInput + SpecName string + Namespace *corev1.Namespace + // Allows to inject a function to be run after test namespace is created. + // If not specified, this is a no-op. + PostNamespaceCreated func(managementClusterProxy framework.ClusterProxy, workloadClusterNamespace string) +} + +var _ = Describe("Set memory reservation locked to max on VMs", func() { + const specName = "memory-reservation-locked" + Setup(specName, func(testSpecificSettingsGetter func() testSettings) { + var ( + namespace *corev1.Namespace + ) + + BeforeEach(func() { + Expect(bootstrapClusterProxy).NotTo(BeNil(), "BootstrapClusterProxy can't be nil") + namespace = setupSpecNamespace(specName, testSpecificSettingsGetter().PostNamespaceCreatedFunc) + }) + + AfterEach(func() { + cleanupSpecNamespace(namespace) + }) + + It("Creates a workload cluster whose VMs have memory reservation locked set to true", func() { + VerfiyMemoryReservationLockToMax(ctx, MemoryReservationLockedSpecInput{ + SpecName: specName, + Namespace: namespace, + InfraClients: InfraClients{ + Client: vsphereClient, + RestClient: restClient, + Finder: vsphereFinder, + }, + Global: GlobalInput{ + BootstrapClusterProxy: bootstrapClusterProxy, + ClusterctlConfigPath: testSpecificSettingsGetter().ClusterctlConfigPath, + E2EConfig: e2eConfig, + ArtifactFolder: artifactFolder, + }, + }) + }) + }) +}) + +func VerfiyMemoryReservationLockToMax(ctx context.Context, input MemoryReservationLockedSpecInput) { + var ( + specName = input.SpecName + namespace = input.Namespace + clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult) + ) + + clusterName := fmt.Sprintf("%s-%s", specName, util.RandomString(6)) + By("Creating a workload cluster") + configCluster := defaultConfigCluster(clusterName, namespace.Name, specName, 1, 1, input.Global) + + clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{ + ClusterProxy: input.Global.BootstrapClusterProxy, + ConfigCluster: configCluster, + WaitForClusterIntervals: input.Global.E2EConfig.GetIntervals(specName, "wait-cluster"), + WaitForControlPlaneIntervals: input.Global.E2EConfig.GetIntervals(specName, "wait-control-plane"), + WaitForMachineDeployments: input.Global.E2EConfig.GetIntervals(specName, "wait-worker-nodes"), + }, clusterResources) + + Byf("Fetching the VSphereVM objects for the cluster %s", clusterName) + vms := getVSphereVMsForCluster(clusterName, namespace.Name) + + By("Verifying memory reservation locked to max is set to true") + for _, vm := range vms.Items { + vmObj, err := input.Finder.VirtualMachine(ctx, vm.Name) + Expect(err).ToNot(HaveOccurred(), "expected to find VM %s", vm.Name) + Expect(getMemoryReservationLockedToMaxFromObj(vmObj)).To(Equal(true), "expected memory reservation locked to max to be set to true") + } +} + +func getMemoryReservationLockedToMaxFromObj(vmObj *object.VirtualMachine) *bool { + var virtualMachine mo.VirtualMachine + Expect(vmObj.Properties(ctx, vmObj.Reference(), []string{"config.memoryReservationLockedToMax"}, &virtualMachine)).To(Succeed()) + Expect(virtualMachine.Config.MemoryReservationLockedToMax).ToNot(BeEmpty()) + return virtualMachine.Config.MemoryReservationLockedToMax +}