Skip to content

Commit 1ac08cf

Browse files
committed
feat: CAREN support for NutanixFailureDomain
1 parent eebf3cf commit 1ac08cf

File tree

17 files changed

+399
-135
lines changed

17 files changed

+399
-135
lines changed

api/v1alpha1/controlplane_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type DockerControlPlaneSpec struct {
3131
// NutanixControlPlaneSpec defines the desired state of the control plane for a Nutanix cluster.
3232
type NutanixControlPlaneSpec struct {
3333
// +kubebuilder:validation:Optional
34-
Nutanix *NutanixNodeSpec `json:"nutanix,omitempty"`
34+
Nutanix *NutanixControlPlaneNodeSpec `json:"nutanix,omitempty"`
3535

3636
GenericControlPlaneSpec `json:",inline"`
3737

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ spec:
329329
type: object
330330
nutanix:
331331
properties:
332+
failureDomains:
333+
description: |-
334+
failureDomains specifies a list of NutanixFailureDomains (by names)
335+
that the cluster uses to deploy its control-plane machines.
336+
items:
337+
type: string
338+
type: array
339+
x-kubernetes-list-type: set
332340
machineDetails:
333341
properties:
334342
additionalCategories:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ spec:
3939
metadata:
4040
type: object
4141
spec:
42-
description: NutanixWorkerNodeConfigSpec defines the desired state of NutanixNodeSpec.
42+
description: NutanixWorkerNodeConfigSpec defines the desired state of NutanixWorkerNodeSpec.
4343
properties:
4444
nodeRegistration:
4545
default: {}

api/v1alpha1/nodeconfig_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ func (s NutanixWorkerNodeConfig) VariableSchema() clusterv1.VariableSchema { //n
9494
return nutanixNodeConfigVariableSchema
9595
}
9696

97-
// NutanixWorkerNodeConfigSpec defines the desired state of NutanixNodeSpec.
97+
// NutanixWorkerNodeConfigSpec defines the desired state of NutanixWorkerNodeSpec.
9898
type NutanixWorkerNodeConfigSpec struct {
9999
// +kubebuilder:validation:Optional
100-
Nutanix *NutanixNodeSpec `json:"nutanix,omitempty"`
100+
Nutanix *NutanixWorkerNodeSpec `json:"nutanix,omitempty"`
101101

102102
GenericNodeSpec `json:",inline"`
103103
}

api/v1alpha1/nutanix_node_types.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,17 @@ import (
99
capxv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1"
1010
)
1111

12-
type NutanixNodeSpec struct {
12+
type NutanixControlPlaneNodeSpec struct {
13+
MachineDetails NutanixMachineDetails `json:"machineDetails"`
14+
15+
// failureDomains specifies a list of NutanixFailureDomains (by names)
16+
// that the cluster uses to deploy its control-plane machines.
17+
// +listType=set
18+
// +optional
19+
FailureDomains []string `json:"failureDomains,omitempty"`
20+
}
21+
22+
type NutanixWorkerNodeSpec struct {
1323
MachineDetails NutanixMachineDetails `json:"machineDetails"`
1424
}
1525

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 39 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/variables/aggregate_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type ControlPlaneSpec struct {
3434

3535
Docker *carenv1.DockerNodeSpec `json:"docker,omitempty"`
3636

37-
Nutanix *carenv1.NutanixNodeSpec `json:"nutanix,omitempty"`
37+
Nutanix *carenv1.NutanixControlPlaneNodeSpec `json:"nutanix,omitempty"`
3838

3939
carenv1.GenericControlPlaneSpec `json:",inline"`
4040

@@ -46,7 +46,7 @@ type WorkerNodeConfigSpec struct {
4646

4747
Docker *carenv1.DockerNodeSpec `json:"docker,omitempty"`
4848

49-
Nutanix *carenv1.NutanixNodeSpec `json:"nutanix,omitempty"`
49+
Nutanix *carenv1.NutanixWorkerNodeSpec `json:"nutanix,omitempty"`
5050
}
5151

5252
type Addons struct {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func TestVariableValidation_Nutanix(t *testing.T) {
153153
func minimalNutanixClusterConfigSpec() v1alpha1.NutanixClusterConfigSpec {
154154
return v1alpha1.NutanixClusterConfigSpec{
155155
ControlPlane: &v1alpha1.NutanixControlPlaneSpec{
156-
Nutanix: &v1alpha1.NutanixNodeSpec{
156+
Nutanix: &v1alpha1.NutanixControlPlaneNodeSpec{
157157
MachineDetails: v1alpha1.NutanixMachineDetails{
158158
BootType: capxv1.NutanixBootTypeLegacy,
159159
VCPUSockets: 2,
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright 2025 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package controlplanefailuredomains
5+
6+
import (
7+
"context"
8+
9+
corev1 "k8s.io/api/core/v1"
10+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
13+
ctrl "sigs.k8s.io/controller-runtime"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
16+
capxv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/external/github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
19+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
20+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
21+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
22+
)
23+
24+
const (
25+
// VariableName is the external patch variable name.
26+
VariableName = "failureDomains"
27+
)
28+
29+
type nutanixControlPlaneFailureDomains struct {
30+
variableName string
31+
variableFieldPath []string
32+
}
33+
34+
func NewPatch() *nutanixControlPlaneFailureDomains {
35+
return newNutanixControlPlaneFailureDomains(
36+
v1alpha1.ClusterConfigVariableName,
37+
v1alpha1.ControlPlaneConfigVariableName,
38+
v1alpha1.NutanixVariableName,
39+
VariableName,
40+
)
41+
}
42+
43+
func newNutanixControlPlaneFailureDomains(
44+
variableName string,
45+
variableFieldPath ...string,
46+
) *nutanixControlPlaneFailureDomains {
47+
return &nutanixControlPlaneFailureDomains{
48+
variableName: variableName,
49+
variableFieldPath: variableFieldPath,
50+
}
51+
}
52+
53+
func (h *nutanixControlPlaneFailureDomains) Mutate(
54+
ctx context.Context,
55+
obj *unstructured.Unstructured,
56+
vars map[string]apiextensionsv1.JSON,
57+
holderRef runtimehooksv1.HolderReference,
58+
_ client.ObjectKey,
59+
_ mutation.ClusterGetter,
60+
) error {
61+
log := ctrl.LoggerFrom(ctx).WithValues(
62+
"holderRef", holderRef,
63+
"variableName", h.variableName,
64+
"variableFieldPath", h.variableFieldPath,
65+
)
66+
67+
controlPlaneFDsVar, err := variables.Get[[]string](
68+
vars,
69+
h.variableName,
70+
h.variableFieldPath...,
71+
)
72+
if err != nil {
73+
if variables.IsNotFoundError(err) {
74+
log.Info("ControlPlane nutanix failureDomains variable not defined", "error", err.Error())
75+
return nil
76+
}
77+
log.Error(err, "failed to get controlPlane nutanix failureDomains variable.")
78+
return err
79+
}
80+
81+
log = log.WithValues("variableValue", controlPlaneFDsVar)
82+
83+
if len(controlPlaneFDsVar) == 0 {
84+
return nil
85+
}
86+
87+
return patches.MutateIfApplicable(
88+
obj,
89+
vars,
90+
&holderRef,
91+
selectors.InfrastructureCluster(capxv1.GroupVersion.Version, "NutanixClusterTemplate"),
92+
log,
93+
func(obj *capxv1.NutanixClusterTemplate) error {
94+
log.WithValues(
95+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
96+
"patchedObjectName", client.ObjectKeyFromObject(obj),
97+
).Info("setting controlPlaneFailureDomains in NutanixCluster spec")
98+
99+
fdRefs := []corev1.LocalObjectReference{}
100+
for _, fd := range controlPlaneFDsVar {
101+
fdRefs = append(fdRefs, corev1.LocalObjectReference{Name: fd})
102+
}
103+
104+
// set controlPlaneFailureDomains in NutanixCluster spec
105+
obj.Spec.Template.Spec.ControlPlaneFailureDomains = fdRefs
106+
107+
return nil
108+
},
109+
)
110+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2025 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package controlplanefailuredomains
5+
6+
import (
7+
"testing"
8+
9+
. "github.com/onsi/ginkgo/v2"
10+
"github.com/onsi/gomega"
11+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
12+
13+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
14+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
15+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/testutils/capitest"
16+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/internal/test/request"
17+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/test/helpers"
18+
)
19+
20+
func TestControlPlaneFailureDomainsPatch(t *testing.T) {
21+
gomega.RegisterFailHandler(Fail)
22+
RunSpecs(t, "ControlPlane nutanix failuredomains suite")
23+
}
24+
25+
var _ = Describe("Generate ControlPlane nutanix failuredomains patches", func() {
26+
patchGenerator := func() mutation.GeneratePatches {
27+
return mutation.NewMetaGeneratePatchesHandler("", helpers.TestEnv.Client, NewPatch()).(mutation.GeneratePatches)
28+
}
29+
30+
testDefs := []capitest.PatchTestDef{
31+
{
32+
Name: "unset variable",
33+
},
34+
{
35+
Name: "FailureDomains set to valid values",
36+
Vars: []runtimehooksv1.Variable{
37+
capitest.VariableWithValue(
38+
v1alpha1.ClusterConfigVariableName,
39+
[]string{"fd-1", "fd-2", "fd-3"},
40+
v1alpha1.ControlPlaneConfigVariableName,
41+
v1alpha1.NutanixVariableName,
42+
VariableName,
43+
),
44+
},
45+
RequestItem: request.NewNutanixClusterTemplateRequestItem(""),
46+
ExpectedPatchMatchers: []capitest.JSONPatchMatcher{
47+
{
48+
Operation: "add",
49+
Path: "/spec/template/spec/controlPlaneFailureDomains",
50+
ValueMatcher: gomega.ConsistOf(
51+
gomega.HaveKeyWithValue("name", "fd-1"),
52+
gomega.HaveKeyWithValue("name", "fd-2"),
53+
gomega.HaveKeyWithValue("name", "fd-3"),
54+
),
55+
},
56+
},
57+
},
58+
}
59+
60+
// create test node for each case
61+
for _, tt := range testDefs {
62+
It(tt.Name, func() {
63+
capitest.AssertGeneratePatches(
64+
GinkgoT(),
65+
patchGenerator,
66+
&tt,
67+
)
68+
})
69+
}
70+
})

0 commit comments

Comments
 (0)