Skip to content

Commit 7d40667

Browse files
fix(patches): handle patches that can add optional keys
cluster and subnet are optional, when failure domains are present we shouldn't generate nutanixmachinetemplate with placeholder values. when failure domains are not present, we should be able to inject actual values for cluster and subnet.
1 parent 5aea19f commit 7d40667

File tree

1 file changed

+68
-6
lines changed
  • pkg/handlers/nutanix/mutation/machinedetails

1 file changed

+68
-6
lines changed

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

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package machinedetails
66
import (
77
"context"
88
"errors"
9+
"fmt"
910
"slices"
1011

1112
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -88,15 +89,29 @@ func (h *nutanixMachineDetailsPatchHandler) Mutate(
8889
&holderRef,
8990
h.patchSelector,
9091
log,
91-
func(obj *capxv1.NutanixMachineTemplate) error {
92+
func(machineTemplate *capxv1.NutanixMachineTemplate) error {
93+
// First, ensure the required fields exist in the original unstructured object
94+
// if they will be set by the mutation
95+
if nutanixMachineDetailsVar.Cluster != nil {
96+
if err := ensureClusterFieldExists(obj); err != nil {
97+
return fmt.Errorf("failed to ensure cluster field exists: %w", err)
98+
}
99+
}
100+
if len(nutanixMachineDetailsVar.Subnets) > 0 {
101+
if err := ensureSubnetFieldExists(obj); err != nil {
102+
return fmt.Errorf("failed to ensure subnet field exists: %w", err)
103+
}
104+
}
105+
92106
log.WithValues(
93-
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
94-
"patchedObjectName", client.ObjectKeyFromObject(obj),
95-
).Info("setting Nutanix machine details in worker NutanixMachineTemplate spec")
107+
"patchedObjectKind", machineTemplate.GetObjectKind().GroupVersionKind().String(),
108+
"patchedObjectName", client.ObjectKeyFromObject(machineTemplate),
109+
).Info("setting Nutanix machine details in NutanixMachineTemplate spec")
96110

97-
spec := obj.Spec.Template.Spec
111+
spec := machineTemplate.Spec.Template.Spec
98112

99113
spec.BootType = nutanixMachineDetailsVar.BootType
114+
100115
if nutanixMachineDetailsVar.Cluster != nil {
101116
spec.Cluster = *nutanixMachineDetailsVar.Cluster
102117
}
@@ -125,8 +140,55 @@ func (h *nutanixMachineDetailsPatchHandler) Mutate(
125140
spec.GPUs = slices.Clone(nutanixMachineDetailsVar.GPUs)
126141
spec.Project = nutanixMachineDetailsVar.Project.DeepCopy()
127142

128-
obj.Spec.Template.Spec = spec
143+
machineTemplate.Spec.Template.Spec = spec
144+
log.WithValues(
145+
"patchedObjectKind", machineTemplate.GetObjectKind().GroupVersionKind().String(),
146+
"patchedObjectName", client.ObjectKeyFromObject(machineTemplate),
147+
"patchedObject", machineTemplate,
148+
).V(5).Info("Patched Nutanix machine details in NutanixMachineTemplate spec")
129149
return nil
130150
},
131151
)
132152
}
153+
154+
// ensureClusterFieldExists ensures the cluster field exists in the unstructured object
155+
func ensureClusterFieldExists(obj *unstructured.Unstructured) error {
156+
clusterPath := []string{"spec", "template", "spec", "cluster"}
157+
if !hasNestedField(obj.Object, clusterPath) {
158+
// Add empty cluster field structure
159+
return unstructured.SetNestedMap(obj.Object, map[string]any{
160+
"type": "",
161+
}, clusterPath...)
162+
}
163+
return nil
164+
}
165+
166+
// ensureSubnetFieldExists ensures the subnet field exists in the unstructured object
167+
func ensureSubnetFieldExists(obj *unstructured.Unstructured) error {
168+
subnetPath := []string{"spec", "template", "spec", "subnet"}
169+
if !hasNestedField(obj.Object, subnetPath) {
170+
// Add empty subnet field
171+
return unstructured.SetNestedSlice(obj.Object, []any{}, subnetPath...)
172+
}
173+
return nil
174+
}
175+
176+
// hasNestedField checks if a nested field exists in an unstructured object
177+
func hasNestedField(obj map[string]interface{}, path []string) bool {
178+
current := obj
179+
for _, key := range path {
180+
if current == nil {
181+
return false
182+
}
183+
val, exists := current[key]
184+
if !exists {
185+
return false
186+
}
187+
if next, ok := val.(map[string]interface{}); ok {
188+
current = next
189+
} else {
190+
return len(path) == 1
191+
}
192+
}
193+
return true
194+
}

0 commit comments

Comments
 (0)