Skip to content

Commit 6dbfed8

Browse files
committed
fix: machineDetails fields "cluster" and "subnets" should be optional
1 parent cc7de7e commit 6dbfed8

File tree

14 files changed

+372
-43
lines changed

14 files changed

+372
-43
lines changed

api/v1alpha1/crds/caren.nutanix.com_nutanixclusterconfigs.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,9 +511,7 @@ spec:
511511
format: int32
512512
type: integer
513513
required:
514-
- cluster
515514
- memorySize
516-
- subnets
517515
- systemDiskSize
518516
- vcpuSockets
519517
- vcpusPerSocket

api/v1alpha1/crds/caren.nutanix.com_nutanixworkernodeconfigs.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,7 @@ spec:
234234
format: int32
235235
type: integer
236236
required:
237-
- cluster
238237
- memorySize
239-
- subnets
240238
- systemDiskSize
241239
- vcpuSockets
242240
- vcpusPerSocket

api/v1alpha1/nutanix_node_types.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,15 @@ type NutanixMachineDetails struct {
5050

5151
// cluster identifies the Prism Element in which the machine will be created.
5252
// The identifier (uuid or name) can be obtained from the console or API.
53-
// +kubebuilder:validation:Required
54-
Cluster capxv1.NutanixResourceIdentifier `json:"cluster"`
53+
// +kubebuilder:validation:Optional
54+
// +optional
55+
Cluster *capxv1.NutanixResourceIdentifier `json:"cluster,omitempty"`
5556

5657
// subnet identifies the network subnet to use for the machine.
5758
// The identifier (uuid or name) can be obtained from the console or API.
58-
// +kubebuilder:validation:Required
59-
Subnets []capxv1.NutanixResourceIdentifier `json:"subnets"`
59+
// +kubebuilder:validation:Optional
60+
// +optional
61+
Subnets []capxv1.NutanixResourceIdentifier `json:"subnets,omitempty"`
6062

6163
// List of categories that need to be added to the machines. Categories must already
6264
// exist in Prism Central. One category key can have more than one value.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/handlers/generic/mutation/kubeproxymode/variables_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,18 @@ func minimalNutanixClusterConfigSpec() v1alpha1.NutanixClusterConfigSpec {
162162
Type: capxv1.NutanixIdentifierName,
163163
Name: ptr.To("fake-image"),
164164
},
165-
Cluster: capxv1.NutanixResourceIdentifier{
165+
Cluster: &capxv1.NutanixResourceIdentifier{
166166
Type: capxv1.NutanixIdentifierName,
167167
Name: ptr.To("fake-pe-cluster"),
168168
},
169169
MemorySize: resource.MustParse("8Gi"),
170170
SystemDiskSize: resource.MustParse("40Gi"),
171-
Subnets: []capxv1.NutanixResourceIdentifier{},
171+
Subnets: []capxv1.NutanixResourceIdentifier{
172+
{
173+
Type: capxv1.NutanixIdentifierName,
174+
Name: ptr.To("fake-subnet"),
175+
},
176+
},
172177
},
173178
},
174179
},

pkg/handlers/nutanix/mutation/controlplanefailuredomains/variables_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,18 @@ func minimumClusterConfigSpec() v1alpha1.NutanixClusterConfigSpec {
5050
Type: capxv1.NutanixIdentifierName,
5151
Name: ptr.To("fake-image"),
5252
},
53-
Cluster: capxv1.NutanixResourceIdentifier{
53+
Cluster: &capxv1.NutanixResourceIdentifier{
5454
Type: capxv1.NutanixIdentifierName,
5555
Name: ptr.To("fake-pe-cluster"),
5656
},
5757
MemorySize: resource.MustParse("8Gi"),
5858
SystemDiskSize: resource.MustParse("40Gi"),
59-
Subnets: []capxv1.NutanixResourceIdentifier{},
59+
Subnets: []capxv1.NutanixResourceIdentifier{
60+
{
61+
Type: capxv1.NutanixIdentifierName,
62+
Name: ptr.To("fake-subnet"),
63+
},
64+
},
6065
},
6166
},
6267
},

pkg/handlers/nutanix/mutation/machinedetails/inject.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,14 @@ func (h *nutanixMachineDetailsPatchHandler) Mutate(
9797
spec := obj.Spec.Template.Spec
9898

9999
spec.BootType = nutanixMachineDetailsVar.BootType
100-
spec.Cluster = nutanixMachineDetailsVar.Cluster
100+
if nutanixMachineDetailsVar.Cluster != nil {
101+
spec.Cluster = *nutanixMachineDetailsVar.Cluster
102+
} else {
103+
// Have to set the required Type field.
104+
spec.Cluster = capxv1.NutanixResourceIdentifier{
105+
Type: capxv1.NutanixIdentifierName,
106+
}
107+
}
101108

102109
switch {
103110
case nutanixMachineDetailsVar.Image != nil:
@@ -118,7 +125,6 @@ func (h *nutanixMachineDetailsPatchHandler) Mutate(
118125
spec.Subnets = slices.Clone(nutanixMachineDetailsVar.Subnets)
119126
spec.AdditionalCategories = slices.Clone(nutanixMachineDetailsVar.AdditionalCategories)
120127
spec.GPUs = slices.Clone(nutanixMachineDetailsVar.GPUs)
121-
122128
spec.Project = nutanixMachineDetailsVar.Project.DeepCopy()
123129

124130
obj.Spec.Template.Spec = spec

pkg/handlers/nutanix/mutation/machinedetails/inject_control_plane_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var (
2727
Type: capxv1.NutanixIdentifierName,
2828
Name: ptr.To("fake-image"),
2929
},
30-
Cluster: capxv1.NutanixResourceIdentifier{
30+
Cluster: &capxv1.NutanixResourceIdentifier{
3131
Type: capxv1.NutanixIdentifierName,
3232
Name: ptr.To("fake-pe-cluster"),
3333
},
@@ -72,7 +72,7 @@ var (
7272
ImageLookup: &capxv1.NutanixImageLookup{
7373
BaseOS: "rockylinux-9",
7474
},
75-
Cluster: capxv1.NutanixResourceIdentifier{
75+
Cluster: &capxv1.NutanixResourceIdentifier{
7676
Type: capxv1.NutanixIdentifierName,
7777
Name: ptr.To("fake-pe-cluster"),
7878
},

pkg/handlers/nutanix/mutation/machinedetails/variables_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,18 @@ func minimumClusterConfigSpec() v1alpha1.NutanixClusterConfigSpec {
109109
Type: capxv1.NutanixIdentifierName,
110110
Name: ptr.To("fake-image"),
111111
},
112-
Cluster: capxv1.NutanixResourceIdentifier{
112+
Cluster: &capxv1.NutanixResourceIdentifier{
113113
Type: capxv1.NutanixIdentifierName,
114114
Name: ptr.To("fake-pe-cluster"),
115115
},
116116
MemorySize: resource.MustParse("8Gi"),
117117
SystemDiskSize: resource.MustParse("40Gi"),
118-
Subnets: []capxv1.NutanixResourceIdentifier{},
118+
Subnets: []capxv1.NutanixResourceIdentifier{
119+
{
120+
Type: capxv1.NutanixIdentifierName,
121+
Name: ptr.To("fake-subnet"),
122+
},
123+
},
119124
},
120125
},
121126
},

pkg/webhook/cluster/nutanix_validator.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"net/netip"
1212

1313
v1 "k8s.io/api/admission/v1"
14+
"k8s.io/apimachinery/pkg/util/validation/field"
1415
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1516
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
1617
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -89,9 +90,113 @@ func (a *nutanixValidator) validate(
8990
}
9091
}
9192

93+
if err := validateFailureDomainRelatedConfig(clusterConfig, cluster); err != nil {
94+
return admission.Denied(err.Error())
95+
}
96+
9297
return admission.Allowed("")
9398
}
9499

100+
// validateFailureDomainRelatedConfig validates the failure domain related configuration in cluster topology.
101+
func validateFailureDomainRelatedConfig(
102+
clusterConfig *variables.ClusterConfigSpec,
103+
cluster *clusterv1.Cluster,
104+
) error {
105+
fldErrs := field.ErrorList{}
106+
fldPath := field.NewPath("spec", "topology")
107+
// Validate that either failureDomains is set, or cluster and subnets are set with machineDetails, for control plane.
108+
if clusterConfig.ControlPlane != nil &&
109+
clusterConfig.ControlPlane.Nutanix != nil &&
110+
len(clusterConfig.ControlPlane.Nutanix.FailureDomains) == 0 {
111+
machineDetails := clusterConfig.ControlPlane.Nutanix.MachineDetails
112+
if machineDetails.Cluster == nil || (!machineDetails.Cluster.IsName() && !machineDetails.Cluster.IsUUID()) {
113+
fldErrs = append(fldErrs, field.Required(
114+
fldPath.Child(
115+
"variables",
116+
"clusterConfig",
117+
"value",
118+
"controlPlane",
119+
"nutanix",
120+
"machineDetails",
121+
"cluster",
122+
),
123+
"\"cluster\" must set when failureDomains is not configured.",
124+
))
125+
}
126+
127+
if len(machineDetails.Subnets) == 0 {
128+
fldErrs = append(fldErrs, field.Required(
129+
fldPath.Child(
130+
"variables",
131+
"clusterConfig",
132+
"value",
133+
"controlPlane",
134+
"nutanix",
135+
"machineDetails",
136+
"subnets",
137+
),
138+
"\"subnets\" must set when failureDomains is not configured.",
139+
))
140+
}
141+
}
142+
143+
// Validate either failureDomains is set, or cluster and sebnets are set with machineDetails, for workers.
144+
if cluster.Spec.Topology.Workers != nil {
145+
for i := range cluster.Spec.Topology.Workers.MachineDeployments {
146+
md := cluster.Spec.Topology.Workers.MachineDeployments[i]
147+
if md.FailureDomain != nil && *md.FailureDomain != "" {
148+
// failureDomain is configured
149+
continue
150+
}
151+
152+
if md.Variables != nil && len(md.Variables.Overrides) > 0 {
153+
workerConfig, err := variables.UnmarshalWorkerConfigVariable(md.Variables.Overrides)
154+
if err != nil {
155+
fldErrs = append(fldErrs, field.InternalError(
156+
fldPath.Child("workers", "machineDeployments", "variables", "workerConfig"),
157+
fmt.Errorf("failed to unmarshal worker topology variable: %w", err)))
158+
}
159+
if workerConfig.Nutanix == nil {
160+
continue
161+
}
162+
163+
machineDetails := workerConfig.Nutanix.MachineDetails
164+
if machineDetails.Cluster == nil ||
165+
(!machineDetails.Cluster.IsName() && !machineDetails.Cluster.IsUUID()) {
166+
fldErrs = append(fldErrs, field.Required(
167+
fldPath.Child(
168+
"workers",
169+
"machineDeployments",
170+
"variables",
171+
"workerConfig",
172+
"nutanix",
173+
"machineDetails",
174+
"cluster",
175+
),
176+
"\"cluster\" must set when failureDomain is not configured.",
177+
))
178+
}
179+
if len(machineDetails.Subnets) == 0 {
180+
fldErrs = append(fldErrs, field.Required(
181+
fldPath.Child(
182+
"workers",
183+
"machineDeployments",
184+
"variables",
185+
"workerConfig",
186+
"nutanix",
187+
"machineDetails",
188+
"subnets",
189+
),
190+
"\"subnets\" must set when failureDomain is not configured.",
191+
))
192+
}
193+
}
194+
}
195+
}
196+
197+
return fldErrs.ToAggregate()
198+
}
199+
95200
// validatePrismCentralIPNotInLoadBalancerIPRange checks if the Prism Central IP is not
96201
// in the MetalLB Load Balancer IP range and error out if it is.
97202
func validatePrismCentralIPNotInLoadBalancerIPRange(

0 commit comments

Comments
 (0)