From f529289b36244a536af9dda2991fba17b4c75dbb Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Wed, 17 Apr 2024 12:26:08 -0700 Subject: [PATCH 1/3] feat: support setting Nutanix project on machines --- api/v1alpha1/nutanix_node_types.go | 9 +++++++++ api/v1alpha1/zz_generated.deepcopy.go | 5 +++++ .../content/customization/nutanix/machine-details.md | 12 ++++++++++++ .../nutanix/mutation/machinedetails/inject.go | 5 +++++ .../machinedetails/inject_control_plane_test.go | 12 ++++++++++++ .../mutation/machinedetails/variables_test.go | 5 +++++ 6 files changed, 48 insertions(+) diff --git a/api/v1alpha1/nutanix_node_types.go b/api/v1alpha1/nutanix_node_types.go index 58a7ce3b5..4dfe4287a 100644 --- a/api/v1alpha1/nutanix_node_types.go +++ b/api/v1alpha1/nutanix_node_types.go @@ -62,6 +62,11 @@ type NutanixMachineDetails struct { // systemDiskSize is size (in Quantity format) of the system disk of the VM // The minimum systemDiskSize is 20Gi bytes SystemDiskSize resource.Quantity `json:"systemDiskSize"` + + // add the virtual machines to the project defined in Prism Central. + // The project must already be present in the Prism Central. + // +optional + Project *NutanixResourceIdentifier `json:"project,omitempty"` } func (NutanixMachineDetails) VariableSchema() clusterv1.VariableSchema { @@ -112,6 +117,10 @@ func (NutanixMachineDetails) VariableSchema() clusterv1.VariableSchema { Description: "systemDiskSize is size (in Quantity format) of the system disk of the VM eg. 20Gi", Type: "string", }, + "project": NutanixResourceIdentifier{}.VariableSchemaFromDescription( + //nolint:lll // Long description. + "add the virtual machines to the project defined in Prism Central. The project must already be present in the Prism Central.", + ).OpenAPIV3Schema, }, Required: []string{ "vcpusPerSocket", diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index eb051f5a0..01029e04d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -766,6 +766,11 @@ func (in *NutanixMachineDetails) DeepCopyInto(out *NutanixMachineDetails) { copy(*out, *in) } out.SystemDiskSize = in.SystemDiskSize.DeepCopy() + if in.Project != nil { + in, out := &in.Project, &out.Project + *out = new(NutanixResourceIdentifier) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NutanixMachineDetails. diff --git a/docs/content/customization/nutanix/machine-details.md b/docs/content/customization/nutanix/machine-details.md index e3ff70c14..4480acc06 100644 --- a/docs/content/customization/nutanix/machine-details.md +++ b/docs/content/customization/nutanix/machine-details.md @@ -29,6 +29,9 @@ spec: name: os-image-name type: name memorySize: 4Gi + project: + type: name + name: project-name subnets: - name: subnet-name type: name @@ -47,6 +50,9 @@ spec: name: os-image-name type: name memorySize: 4Gi + project: + type: name + name: project-name subnets: - name: subnet-name type: name @@ -78,6 +84,9 @@ spec: name: os-image-name type: name memorySize: 4Gi + project: + type: name + name: project-name providerID: nutanix://vm-uuid subnet: - name: subnet-name @@ -108,6 +117,9 @@ spec: name: os-image-name type: name memorySize: 4Gi + project: + type: name + name: project-name providerID: nutanix://vm-uuid subnet: - name: subnet-name diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject.go b/pkg/handlers/nutanix/mutation/machinedetails/inject.go index 139842ed4..243bd7200 100644 --- a/pkg/handlers/nutanix/mutation/machinedetails/inject.go +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject.go @@ -8,6 +8,7 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/utils/ptr" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" @@ -116,6 +117,10 @@ func (h *nutanixMachineDetailsPatchHandler) Mutate( spec.AdditionalCategories[i] = capxv1.NutanixCategoryIdentifier(category) } + if nutanixMachineDetailsVar.Project != nil { + spec.Project = ptr.To(capxv1.NutanixResourceIdentifier(*nutanixMachineDetailsVar.Project)) + } + obj.Spec.Template.Spec = spec return nil }, diff --git a/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go b/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go index 9a87d6eec..060b1a73d 100644 --- a/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go +++ b/pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go @@ -51,6 +51,10 @@ var ( Value: "fake-value2", }, }, + Project: ptr.To(v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-project"), + }), } matchersForAllFieldsSet = []capitest.JSONPatchMatcher{ @@ -118,6 +122,14 @@ var ( ), ), }, + { + Operation: "add", + Path: "/spec/template/spec/project", + ValueMatcher: gomega.SatisfyAll( + gomega.HaveKeyWithValue("type", "name"), + gomega.HaveKeyWithValue("name", "fake-project"), + ), + }, } ) diff --git a/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go b/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go index b9dda5d33..11cb0f756 100644 --- a/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go +++ b/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go @@ -19,6 +19,10 @@ import ( func TestVariableValidation(t *testing.T) { testImageName := "fake-image" testPEClusterName := "fake-pe-cluster" + testProject := ptr.To(v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-project"), + }) capitest.ValidateDiscoverVariables( t, clusterconfig.MetaVariableName, @@ -43,6 +47,7 @@ func TestVariableValidation(t *testing.T) { Name: &testPEClusterName, }, MemorySize: resource.MustParse("8Gi"), + Project: testProject, SystemDiskSize: resource.MustParse("40Gi"), Subnets: []v1alpha1.NutanixResourceIdentifier{}, AdditionalCategories: []v1alpha1.NutanixCategoryIdentifier{ From e076a1d09bc5de6a9a2130d331fae56408eb7395 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Thu, 18 Apr 2024 10:35:27 -0700 Subject: [PATCH 2/3] refactor: nutanix variables test --- .../mutation/machinedetails/variables_test.go | 193 ++++++++---------- 1 file changed, 85 insertions(+), 108 deletions(-) diff --git a/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go b/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go index 11cb0f756..e11a9eb22 100644 --- a/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go +++ b/pkg/handlers/nutanix/mutation/machinedetails/variables_test.go @@ -17,12 +17,46 @@ import ( ) func TestVariableValidation(t *testing.T) { - testImageName := "fake-image" - testPEClusterName := "fake-pe-cluster" - testProject := ptr.To(v1alpha1.NutanixResourceIdentifier{ - Type: capxv1.NutanixIdentifierName, - Name: ptr.To("fake-project"), - }) + requiredFields := minimumClusterConfigSpec() + + withAdditionalCategories := minimumClusterConfigSpec() + //nolint:lll // gofumpt formats is this way + withAdditionalCategories.ControlPlane.Nutanix.MachineDetails.AdditionalCategories = []v1alpha1.NutanixCategoryIdentifier{ + { + Key: "fake-key", + Value: "fake-value1", + }, + { + Key: "fake-key", + Value: "fake-value2", + }, + } + + withProject := minimumClusterConfigSpec() + withProject.ControlPlane.Nutanix.MachineDetails.Project = ptr.To( + v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-project"), + }, + ) + + invalidBootType := minimumClusterConfigSpec() + invalidBootType.ControlPlane.Nutanix.MachineDetails.BootType = "invalid-boot-type" + + invalidImageType := minimumClusterConfigSpec() + invalidImageType.ControlPlane.Nutanix.MachineDetails.Image.Type = "invalid-image-type" + + invalidClusterType := minimumClusterConfigSpec() + invalidClusterType.ControlPlane.Nutanix.MachineDetails.Cluster.Type = "invalid-cluster-type" + + invalidProjectType := minimumClusterConfigSpec() + invalidProjectType.ControlPlane.Nutanix.MachineDetails.Project = ptr.To( + v1alpha1.NutanixResourceIdentifier{ + Type: "invalid-project-type", + Name: ptr.To("fake-project"), + }, + ) + capitest.ValidateDiscoverVariables( t, clusterconfig.MetaVariableName, @@ -30,118 +64,61 @@ func TestVariableValidation(t *testing.T) { true, nutanixclusterconfig.NewVariable, capitest.VariableTestDef{ - Name: "all fields set", - Vals: v1alpha1.ClusterConfigSpec{ - ControlPlane: &v1alpha1.NodeConfigSpec{ - Nutanix: &v1alpha1.NutanixNodeSpec{ - MachineDetails: v1alpha1.NutanixMachineDetails{ - BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), - VCPUSockets: 2, - VCPUsPerSocket: 1, - Image: v1alpha1.NutanixResourceIdentifier{ - Type: capxv1.NutanixIdentifierName, - Name: &testImageName, - }, - Cluster: v1alpha1.NutanixResourceIdentifier{ - Type: capxv1.NutanixIdentifierName, - Name: &testPEClusterName, - }, - MemorySize: resource.MustParse("8Gi"), - Project: testProject, - SystemDiskSize: resource.MustParse("40Gi"), - Subnets: []v1alpha1.NutanixResourceIdentifier{}, - AdditionalCategories: []v1alpha1.NutanixCategoryIdentifier{ - { - Key: "fake-key", - Value: "fake-value1", - }, - { - Key: "fake-key", - Value: "fake-value2", - }, - }, - }, - }, - }, - }, + Name: "required fields set", + Vals: requiredFields, }, capitest.VariableTestDef{ - Name: "invalid boot type", - Vals: v1alpha1.ClusterConfigSpec{ - ControlPlane: &v1alpha1.NodeConfigSpec{ - Nutanix: &v1alpha1.NutanixNodeSpec{ - MachineDetails: v1alpha1.NutanixMachineDetails{ - BootType: "invalid", - VCPUSockets: 2, - VCPUsPerSocket: 1, - Image: v1alpha1.NutanixResourceIdentifier{ - Type: capxv1.NutanixIdentifierName, - Name: &testImageName, - }, - Cluster: v1alpha1.NutanixResourceIdentifier{ - Type: capxv1.NutanixIdentifierName, - Name: &testPEClusterName, - }, - MemorySize: resource.MustParse("8Gi"), - SystemDiskSize: resource.MustParse("40Gi"), - Subnets: []v1alpha1.NutanixResourceIdentifier{}, - }, - }, - }, - }, + Name: "additional categories set", + Vals: withAdditionalCategories, + }, + capitest.VariableTestDef{ + Name: "project set", + Vals: withProject, + }, + capitest.VariableTestDef{ + Name: "invalid boot type", + Vals: invalidBootType, ExpectError: true, }, capitest.VariableTestDef{ - Name: "invalid image type", - Vals: v1alpha1.ClusterConfigSpec{ - ControlPlane: &v1alpha1.NodeConfigSpec{ - Nutanix: &v1alpha1.NutanixNodeSpec{ - MachineDetails: v1alpha1.NutanixMachineDetails{ - BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), - VCPUSockets: 2, - VCPUsPerSocket: 1, - Image: v1alpha1.NutanixResourceIdentifier{ - Type: "invalid", - Name: &testImageName, - }, - Cluster: v1alpha1.NutanixResourceIdentifier{ - Type: capxv1.NutanixIdentifierName, - Name: &testPEClusterName, - }, - MemorySize: resource.MustParse("8Gi"), - SystemDiskSize: resource.MustParse("40Gi"), - Subnets: []v1alpha1.NutanixResourceIdentifier{}, - }, - }, - }, - }, + Name: "invalid image type", + Vals: invalidImageType, + ExpectError: true, + }, + capitest.VariableTestDef{ + Name: "invalid cluster type", + Vals: invalidClusterType, ExpectError: true, }, capitest.VariableTestDef{ - Name: "invalid cluster type", - Vals: v1alpha1.ClusterConfigSpec{ - ControlPlane: &v1alpha1.NodeConfigSpec{ - Nutanix: &v1alpha1.NutanixNodeSpec{ - MachineDetails: v1alpha1.NutanixMachineDetails{ - BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), - VCPUSockets: 2, - VCPUsPerSocket: 1, - Image: v1alpha1.NutanixResourceIdentifier{ - Type: capxv1.NutanixIdentifierName, - Name: &testImageName, - }, - Cluster: v1alpha1.NutanixResourceIdentifier{ - Type: "invalid", - Name: &testPEClusterName, - }, - MemorySize: resource.MustParse("8Gi"), - SystemDiskSize: resource.MustParse("40Gi"), - Subnets: []v1alpha1.NutanixResourceIdentifier{}, - }, + Name: "invalid project type", + Vals: invalidProjectType, + ExpectError: true, + }, + ) +} + +func minimumClusterConfigSpec() v1alpha1.ClusterConfigSpec { + return v1alpha1.ClusterConfigSpec{ + ControlPlane: &v1alpha1.NodeConfigSpec{ + Nutanix: &v1alpha1.NutanixNodeSpec{ + MachineDetails: v1alpha1.NutanixMachineDetails{ + BootType: v1alpha1.NutanixBootType(capxv1.NutanixBootTypeLegacy), + VCPUSockets: 2, + VCPUsPerSocket: 1, + Image: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-image"), + }, + Cluster: v1alpha1.NutanixResourceIdentifier{ + Type: capxv1.NutanixIdentifierName, + Name: ptr.To("fake-pe-cluster"), }, + MemorySize: resource.MustParse("8Gi"), + SystemDiskSize: resource.MustParse("40Gi"), + Subnets: []v1alpha1.NutanixResourceIdentifier{}, }, }, - ExpectError: true, }, - ) + } } From a0f818d1146d819249362989778ef90388ceb959 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Fri, 19 Apr 2024 12:50:22 -0700 Subject: [PATCH 3/3] docs: separate optional Nutanix fields in public docs --- .../customization/nutanix/machine-details.md | 132 ++++++++++++++++-- 1 file changed, 119 insertions(+), 13 deletions(-) diff --git a/docs/content/customization/nutanix/machine-details.md b/docs/content/customization/nutanix/machine-details.md index 4480acc06..632651e84 100644 --- a/docs/content/customization/nutanix/machine-details.md +++ b/docs/content/customization/nutanix/machine-details.md @@ -6,7 +6,7 @@ Configure Machine Details of Control plane and Worker nodes ## Examples -### Set Machine details of Control Plane and Worker nodes +### (Required) Set Machine details for Control Plane and Worker nodes ```yaml apiVersion: cluster.x-k8s.io/v1beta1 @@ -29,9 +29,6 @@ spec: name: os-image-name type: name memorySize: 4Gi - project: - type: name - name: project-name subnets: - name: subnet-name type: name @@ -50,9 +47,6 @@ spec: name: os-image-name type: name memorySize: 4Gi - project: - type: name - name: project-name subnets: - name: subnet-name type: name @@ -84,9 +78,6 @@ spec: name: os-image-name type: name memorySize: 4Gi - project: - type: name - name: project-name providerID: nutanix://vm-uuid subnet: - name: subnet-name @@ -117,9 +108,6 @@ spec: name: os-image-name type: name memorySize: 4Gi - project: - type: name - name: project-name providerID: nutanix://vm-uuid subnet: - name: subnet-name @@ -131,3 +119,121 @@ spec: vcpuSockets: 2 vcpusPerSocket: 1 ``` + +### (Optional) Set Additional Categories for Control Plane and Worker nodes + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + controlPlane: + nutanix: + machineDetails: + additionalCategories: + - key: example-key + value: example-value + - name: workerConfig + value: + nutanix: + machineDetails: + additionalCategories: + - key: example-key + value: example-value +``` + +Applying this configuration will result in the following value being set: + +- control-plane `NutanixMachineTemplate`: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + name: nutanix-quick-start-cp-nmt +spec: + template: + spec: + additionalCategories: + - key: example-key + value: example-value +``` + +- worker `NutanixMachineTemplate`: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + name: nutanix-quick-start-md-nmt +spec: + template: + spec: + additionalCategories: + - key: example-key + value: example-value +``` + +### (Optional) Set Project for Control Plane and Worker nodes + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + controlPlane: + nutanix: + machineDetails: + project: + type: name + name: project-name + - name: workerConfig + value: + nutanix: + machineDetails: + project: + type: name + name: project-name +``` + +Applying this configuration will result in the following value being set: + +- control-plane `NutanixMachineTemplate`: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + name: nutanix-quick-start-cp-nmt +spec: + template: + spec: + project: + type: name + name: project-name +``` + +- worker `NutanixMachineTemplate`: + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: NutanixMachineTemplate +metadata: + name: nutanix-quick-start-md-nmt +spec: + template: + spec: + project: + type: name + name: project-name +```