Skip to content

Commit 250a2bc

Browse files
committed
NCN-108428: When failureDomain is used, machineDetails "cluster" and "subnets" should be optional
instead of required
1 parent cc7de7e commit 250a2bc

File tree

13 files changed

+176
-43
lines changed

13 files changed

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

0 commit comments

Comments
 (0)