Skip to content

Commit b69d2fb

Browse files
authored
Merge pull request #5479 from spectrocloud/failure-domain-fix
🐛 Cluster FailureDomains should always be fully synced rather than additive
2 parents e33f04d + 51af4eb commit b69d2fb

File tree

2 files changed

+141
-1
lines changed

2 files changed

+141
-1
lines changed

controllers/cluster_controller_phases.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,12 @@ func (r *ClusterReconciler) reconcileInfrastructure(ctx context.Context, cluster
188188
}
189189

190190
// Get and parse Status.FailureDomains from the infrastructure provider.
191-
if err := util.UnstructuredUnmarshalField(infraConfig, &cluster.Status.FailureDomains, "status", "failureDomains"); err != nil && err != util.ErrUnstructuredFieldNotFound {
191+
failureDomains := clusterv1.FailureDomains{}
192+
if err := util.UnstructuredUnmarshalField(infraConfig, &failureDomains, "status", "failureDomains"); err != nil && err != util.ErrUnstructuredFieldNotFound {
192193
return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve Status.FailureDomains from infrastructure provider for Cluster %q in namespace %q",
193194
cluster.Name, cluster.Namespace)
194195
}
196+
cluster.Status.FailureDomains = failureDomains
195197

196198
return ctrl.Result{}, nil
197199
}

controllers/cluster_controller_phases_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,141 @@ func TestClusterReconciler_reconcilePhase(t *testing.T) {
370370
})
371371
}
372372
}
373+
374+
func TestClusterReconcilePhases_reconcileFailureDomains(t *testing.T) {
375+
cluster := &clusterv1.Cluster{
376+
ObjectMeta: metav1.ObjectMeta{
377+
Name: "test-cluster",
378+
Namespace: "test-namespace",
379+
},
380+
Status: clusterv1.ClusterStatus{
381+
InfrastructureReady: true,
382+
},
383+
Spec: clusterv1.ClusterSpec{
384+
ControlPlaneEndpoint: clusterv1.APIEndpoint{
385+
Host: "1.2.3.4",
386+
Port: 8443,
387+
},
388+
InfrastructureRef: &corev1.ObjectReference{
389+
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
390+
Kind: "GenericInfrastructureMachine",
391+
Name: "test",
392+
},
393+
},
394+
}
395+
396+
newFailureDomain := clusterv1.FailureDomains{
397+
"newdomain": clusterv1.FailureDomainSpec{
398+
ControlPlane: false,
399+
Attributes: map[string]string{
400+
"attribute1": "value1",
401+
},
402+
},
403+
}
404+
405+
newFailureDomainUpdated := clusterv1.FailureDomains{
406+
"newdomain": clusterv1.FailureDomainSpec{
407+
ControlPlane: false,
408+
Attributes: map[string]string{
409+
"attribute2": "value2",
410+
},
411+
},
412+
}
413+
414+
clusterWithNewFailureDomainUpdated := cluster.DeepCopy()
415+
clusterWithNewFailureDomainUpdated.Status.FailureDomains = newFailureDomainUpdated
416+
417+
oldFailureDomain := clusterv1.FailureDomains{
418+
"olddomain": clusterv1.FailureDomainSpec{
419+
ControlPlane: false,
420+
Attributes: map[string]string{
421+
"attribute1": "value1",
422+
},
423+
},
424+
}
425+
426+
clusterWithOldFailureDomain := cluster.DeepCopy()
427+
clusterWithOldFailureDomain.Status.FailureDomains = oldFailureDomain
428+
429+
tests := []struct {
430+
name string
431+
cluster *clusterv1.Cluster
432+
infraRef map[string]interface{}
433+
expectFailureDomains clusterv1.FailureDomains
434+
}{
435+
{
436+
name: "expect no failure domain if infrastructure ref is nil",
437+
cluster: &clusterv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "test-cluster", Namespace: "test-namespace"}},
438+
},
439+
{
440+
name: "expect no failure domain if infra config does not have failure domain",
441+
cluster: cluster,
442+
infraRef: generateInfraRef(false),
443+
},
444+
{
445+
name: "expect failure domain to remain same if infra config have same failure domain",
446+
cluster: cluster,
447+
infraRef: generateInfraRef(true),
448+
expectFailureDomains: newFailureDomain,
449+
},
450+
{
451+
name: "expect failure domain to be updated if infra config have update to failure domain",
452+
cluster: clusterWithNewFailureDomainUpdated,
453+
infraRef: generateInfraRef(true),
454+
expectFailureDomains: newFailureDomain,
455+
},
456+
{
457+
name: "expect failure domain to be reset if infra config have different failure domain",
458+
cluster: clusterWithOldFailureDomain,
459+
infraRef: generateInfraRef(true),
460+
expectFailureDomains: newFailureDomain,
461+
},
462+
}
463+
464+
for _, tt := range tests {
465+
t.Run(tt.name, func(t *testing.T) {
466+
g := NewWithT(t)
467+
468+
objs := []client.Object{builder.GenericInfrastructureMachineCRD.DeepCopy(), tt.cluster}
469+
if tt.infraRef != nil {
470+
objs = append(objs, &unstructured.Unstructured{Object: tt.infraRef})
471+
}
472+
473+
r := &ClusterReconciler{
474+
Client: fake.NewClientBuilder().WithObjects(objs...).Build(),
475+
}
476+
477+
_, err := r.reconcileInfrastructure(ctx, tt.cluster)
478+
g.Expect(err).ToNot(HaveOccurred())
479+
g.Expect(cluster.Status.FailureDomains).To(BeEquivalentTo(tt.expectFailureDomains))
480+
})
481+
}
482+
}
483+
484+
func generateInfraRef(withFailureDomain bool) map[string]interface{} {
485+
infraRef := map[string]interface{}{
486+
"kind": "GenericInfrastructureMachine",
487+
"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
488+
"metadata": map[string]interface{}{
489+
"name": "test",
490+
"namespace": "test-namespace",
491+
"deletionTimestamp": "sometime",
492+
},
493+
}
494+
495+
if withFailureDomain {
496+
infraRef["status"] = map[string]interface{}{
497+
"failureDomains": map[string]interface{}{
498+
"newdomain": map[string]interface{}{
499+
"controlPlane": false,
500+
"attributes": map[string]interface{}{
501+
"attribute1": "value1",
502+
},
503+
},
504+
},
505+
"ready": true,
506+
}
507+
}
508+
509+
return infraRef
510+
}

0 commit comments

Comments
 (0)