Skip to content

Commit 97409aa

Browse files
committed
fixup! refactor: Handle overrides in failure domain validation
1 parent ead35e8 commit 97409aa

File tree

2 files changed

+92
-30
lines changed

2 files changed

+92
-30
lines changed

pkg/webhook/cluster/nutanix_validator.go

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import (
1111
"net/netip"
1212

1313
v1 "k8s.io/api/admission/v1"
14+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1415
"k8s.io/apimachinery/pkg/util/validation/field"
1516
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1617
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
1718
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
1819

1920
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
2021
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/variables"
22+
commonvariables "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
2123
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/utils"
2224
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/helpers"
2325
)
@@ -220,41 +222,69 @@ func validateWorkerFailureDomainConfig(
220222
"workerConfig",
221223
)
222224

223-
// Get the machineDetails from cluster variable "workerConfig" if it is configured
224-
defaultWorkerConfig, err := variables.UnmarshalWorkerConfigVariable(cluster.Spec.Topology.Variables)
225-
if err != nil {
226-
fldErrs = append(fldErrs, field.InternalError(workerConfigVarPath,
227-
fmt.Errorf("failed to unmarshal cluster topology variable %q: %w", v1alpha1.WorkerConfigVariableName, err)))
228-
}
225+
// Merge the global cluster variables with the worker config overrides.
226+
mdVariables := commonvariables.ClusterVariablesToVariablesMap(cluster.Spec.Topology.Variables)
229227

230228
if cluster.Spec.Topology.Workers != nil {
231229
for i := range cluster.Spec.Topology.Workers.MachineDeployments {
232230
md := cluster.Spec.Topology.Workers.MachineDeployments[i]
233231
hasFailureDomain := md.FailureDomain != nil && *md.FailureDomain != ""
232+
wcfgPath := workerConfigVarPath
234233

235-
// Get the machineDetails from the overrides variable "workerConfig" if it is configured,
236-
// otherwise use the defaultWorkerConfig if it is configured.
237-
var workerConfig *variables.WorkerNodeConfigSpec
234+
// Get the md variable overrides.
235+
var mdVariableOverrides map[string]apiextensionsv1.JSON
238236
if md.Variables != nil && len(md.Variables.Overrides) > 0 {
239-
workerConfig, err = variables.UnmarshalWorkerConfigVariable(md.Variables.Overrides)
240-
if err != nil {
241-
fldErrs = append(fldErrs, field.InternalError(
242-
workerConfigMDVarOverridePath,
243-
fmt.Errorf(
244-
"failed to unmarshal worker overrides variable %q: %w",
245-
v1alpha1.WorkerConfigVariableName,
246-
err,
247-
),
248-
))
237+
wcfgPath = workerConfigMDVarOverridePath
238+
mdVariableOverrides = commonvariables.ClusterVariablesToVariablesMap(md.Variables.Overrides)
239+
240+
// If mdVariables is nil, initialize it with mdVariableOverrides, otherwise merge global and
241+
// variable overrides.
242+
if mdVariables == nil {
243+
mdVariables = mdVariableOverrides
244+
} else {
245+
// Merge global and variable overrides if global variables are present.
246+
mergedVariables, err := commonvariables.MergeVariableOverridesWithGlobal(
247+
mdVariableOverrides,
248+
mdVariables,
249+
)
250+
if err != nil {
251+
fldErrs = append(fldErrs, field.InternalError(
252+
workerConfigMDVarOverridePath,
253+
fmt.Errorf(
254+
"failed to merge global and worker variable overrides for MachineDeployment %q: %w",
255+
md.Name,
256+
err,
257+
),
258+
))
259+
}
260+
261+
mdVariables = mergedVariables
249262
}
250263
}
251264

252-
wcfgPath := workerConfigMDVarOverridePath
253-
if workerConfig == nil {
254-
workerConfig = defaultWorkerConfig
255-
wcfgPath = workerConfigVarPath
265+
workerConfigVarJSON, workerConfigPresent := mdVariables[v1alpha1.WorkerConfigVariableName]
266+
if !workerConfigPresent {
267+
continue
268+
}
269+
270+
workerConfigClusterVar := clusterv1.ClusterVariable{
271+
Name: v1alpha1.WorkerConfigVariableName,
272+
Value: *workerConfigVarJSON.DeepCopy(),
273+
}
274+
275+
workerConfig := variables.WorkerNodeConfigSpec{}
276+
if err := variables.UnmarshalClusterVariable(&workerConfigClusterVar, &workerConfig); err != nil {
277+
fldErrs = append(fldErrs, field.InternalError(
278+
workerConfigMDVarOverridePath,
279+
fmt.Errorf(
280+
"failed to unmarshal worker overrides variable %q: %w",
281+
v1alpha1.WorkerConfigVariableName,
282+
err,
283+
),
284+
))
256285
}
257-
if workerConfig == nil || workerConfig.Nutanix == nil {
286+
287+
if workerConfig.Nutanix == nil {
258288
continue
259289
}
260290

pkg/webhook/cluster/nutanix_validator_test.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
1112
"k8s.io/apimachinery/pkg/api/resource"
1213
"k8s.io/utils/ptr"
1314
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -366,6 +367,16 @@ func TestValidateTopologyFailureDomainConfig(t *testing.T) {
366367
expectedErr: false,
367368
expectedErrMessages: nil,
368369
},
370+
{
371+
name: "worker cluster and subnet configured in default with failure domain in variables overrides violates XOR", //nolint:lll // name is long.
372+
clusterConfig: fakeClusterConfigSpec(false, true, true),
373+
cluster: fakeCluster(t, true, false, false, workerConfigDefaultOverridesConflicting),
374+
expectedErr: true,
375+
expectedErrMessages: []string{
376+
"spec.topology.workers.machineDeployments.variables.overrides.workerConfig.value.nutanix.machineDetails.cluster: Forbidden: \"cluster\" must not be set when failureDomain is configured.", //nolint:lll // Message is long.
377+
"spec.topology.workers.machineDeployments.variables.overrides.workerConfig.value.nutanix.machineDetails.subnets: Forbidden: \"subnets\" must not be set when failureDomain is configured.", //nolint:lll // Message is long.
378+
},
379+
},
369380
}
370381

371382
for _, tc := range testcases {
@@ -432,10 +443,11 @@ func fakeClusterConfigSpec(fd, pe, subnets bool) *variables.ClusterConfigSpec {
432443
type workerConfigExistence string
433444

434445
const (
435-
workerConfigDefaultOnly workerConfigExistence = "workerConfigDefaultOnly"
436-
workerConfigOverridesOnly workerConfigExistence = "workerConfigOverridesOnly"
437-
workerConfigDefaultOverridesBoth workerConfigExistence = "workerConfigDefaultOverridesBoth"
438-
workerConfigNone workerConfigExistence = "workerConfigNone"
446+
workerConfigDefaultOnly workerConfigExistence = "workerConfigDefaultOnly"
447+
workerConfigOverridesOnly workerConfigExistence = "workerConfigOverridesOnly"
448+
workerConfigDefaultOverridesBoth workerConfigExistence = "workerConfigDefaultOverridesBoth"
449+
workerConfigDefaultOverridesConflicting workerConfigExistence = "workerConfigDefaultOverridesConflicting"
450+
workerConfigNone workerConfigExistence = "workerConfigNone"
439451
)
440452

441453
func fakeCluster(t *testing.T, fd, pe, subnets bool, wcfg workerConfigExistence) *clusterv1.Cluster {
@@ -469,8 +481,8 @@ func fakeCluster(t *testing.T, fd, pe, subnets bool, wcfg workerConfigExistence)
469481
MachineDetails: *fakeMachineDetails(pe, subnets),
470482
},
471483
}
472-
workerConfigVar, err := variables.MarshalToClusterVariable("workerConfig", workerConfig)
473-
assert.NoError(t, err)
484+
workerConfigVar, err := variables.MarshalToClusterVariable(string(v1alpha1.WorkerConfigVariableName), workerConfig)
485+
require.NoError(t, err)
474486

475487
switch wcfg {
476488
case workerConfigDefaultOnly:
@@ -486,6 +498,26 @@ func fakeCluster(t *testing.T, fd, pe, subnets bool, wcfg workerConfigExistence)
486498
cluster.Spec.Topology.Workers.MachineDeployments[0].Variables.Overrides,
487499
*workerConfigVar,
488500
)
501+
case workerConfigDefaultOverridesConflicting:
502+
// If a failure domain is defined, create a default worker config with cluster and subnet to force
503+
// conflict and therefore error.
504+
defaultWorkerConfig := variables.WorkerNodeConfigSpec{
505+
Nutanix: &v1alpha1.NutanixWorkerNodeSpec{
506+
MachineDetails: *fakeMachineDetails(fd, fd),
507+
},
508+
}
509+
510+
defaultWorkerConfigVar, err := variables.MarshalToClusterVariable(
511+
string(v1alpha1.WorkerConfigVariableName),
512+
defaultWorkerConfig,
513+
)
514+
require.NoError(t, err)
515+
516+
cluster.Spec.Topology.Variables = append(cluster.Spec.Topology.Variables, *defaultWorkerConfigVar)
517+
cluster.Spec.Topology.Workers.MachineDeployments[0].Variables.Overrides = append(
518+
cluster.Spec.Topology.Workers.MachineDeployments[0].Variables.Overrides,
519+
*workerConfigVar,
520+
)
489521
case workerConfigNone:
490522
default:
491523
break

0 commit comments

Comments
 (0)