Skip to content

Commit 77c079a

Browse files
committed
fix: ignore if control plane if spec.controlPlane is set
1 parent 53c0cef commit 77c079a

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed

internal/contract/controlplane.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,33 @@ func (c *ControlPlaneContract) MachineTemplate() *ControlPlaneMachineTemplate {
5252
return &ControlPlaneMachineTemplate{}
5353
}
5454

55+
// IgnorePaths returns a list of paths to be ignored when reconciling an ControlPlane.
56+
// NOTE: The controlPlaneEndpoint struct currently contains two mandatory fields (host and port).
57+
// As the host and port fields are not using omitempty, they are automatically set to their zero values
58+
// if they are not set by the user. We don't want to reconcile the zero values as we would then overwrite
59+
// changes applied by the infrastructure provider controller.
60+
func (c *ControlPlaneContract) IgnorePaths(controlPlane *unstructured.Unstructured) ([]Path, error) {
61+
var ignorePaths []Path
62+
63+
host, ok, err := unstructured.NestedString(controlPlane.UnstructuredContent(), ControlPlane().ControlPlaneEndpoint().host().Path()...)
64+
if err != nil {
65+
return nil, errors.Wrapf(err, "failed to retrieve %s", ControlPlane().ControlPlaneEndpoint().host().Path().String())
66+
}
67+
if ok && host == "" {
68+
ignorePaths = append(ignorePaths, ControlPlane().ControlPlaneEndpoint().host().Path())
69+
}
70+
71+
port, ok, err := unstructured.NestedInt64(controlPlane.UnstructuredContent(), ControlPlane().ControlPlaneEndpoint().port().Path()...)
72+
if err != nil {
73+
return nil, errors.Wrapf(err, "failed to retrieve %s", ControlPlane().ControlPlaneEndpoint().port().Path().String())
74+
}
75+
if ok && port == 0 {
76+
ignorePaths = append(ignorePaths, ControlPlane().ControlPlaneEndpoint().port().Path())
77+
}
78+
79+
return ignorePaths, nil
80+
}
81+
5582
// Version provide access to version field in a ControlPlane object, if any.
5683
// NOTE: When working with unstructured there is no way to understand if the ControlPlane provider
5784
// do support a field in the type definition from the fact that a field is not set in a given instance.

internal/contract/controlplane_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,169 @@ func TestControlPlane(t *testing.T) {
402402
})
403403
}
404404

405+
func TestControlPlaneEndpoints(t *testing.T) {
406+
tests := []struct {
407+
name string
408+
controlPlane *unstructured.Unstructured
409+
want []Path
410+
expectErr bool
411+
}{
412+
{
413+
name: "No ignore paths when controlPlaneEndpoint is not set",
414+
controlPlane: &unstructured.Unstructured{
415+
Object: map[string]interface{}{
416+
"spec": map[string]interface{}{
417+
"server": "1.2.3.4",
418+
},
419+
},
420+
},
421+
want: nil,
422+
},
423+
{
424+
name: "No ignore paths when controlPlaneEndpoint is nil",
425+
controlPlane: &unstructured.Unstructured{
426+
Object: map[string]interface{}{
427+
"spec": map[string]interface{}{
428+
"controlPlaneEndpoint": nil,
429+
},
430+
},
431+
},
432+
433+
want: nil,
434+
},
435+
{
436+
name: "No ignore paths when controlPlaneEndpoint is an empty object",
437+
controlPlane: &unstructured.Unstructured{
438+
Object: map[string]interface{}{
439+
"spec": map[string]interface{}{
440+
"controlPlaneEndpoint": map[string]interface{}{},
441+
},
442+
},
443+
},
444+
445+
want: nil,
446+
},
447+
{
448+
name: "Don't ignore host when controlPlaneEndpoint.host is set",
449+
controlPlane: &unstructured.Unstructured{
450+
Object: map[string]interface{}{
451+
"spec": map[string]interface{}{
452+
"controlPlaneEndpoint": map[string]interface{}{
453+
"host": "example.com",
454+
},
455+
},
456+
},
457+
},
458+
want: nil,
459+
},
460+
{
461+
name: "Ignore host when controlPlaneEndpoint.host is set to its zero value",
462+
controlPlane: &unstructured.Unstructured{
463+
Object: map[string]interface{}{
464+
"spec": map[string]interface{}{
465+
"controlPlaneEndpoint": map[string]interface{}{
466+
"host": "",
467+
},
468+
},
469+
},
470+
},
471+
want: []Path{
472+
{"spec", "controlPlaneEndpoint", "host"},
473+
},
474+
},
475+
{
476+
name: "Don't ignore port when controlPlaneEndpoint.port is set",
477+
controlPlane: &unstructured.Unstructured{
478+
Object: map[string]interface{}{
479+
"spec": map[string]interface{}{
480+
"controlPlaneEndpoint": map[string]interface{}{
481+
"port": int64(6443),
482+
},
483+
},
484+
},
485+
},
486+
487+
want: nil,
488+
},
489+
{
490+
name: "Ignore port when controlPlaneEndpoint.port is set to its zero value",
491+
controlPlane: &unstructured.Unstructured{
492+
Object: map[string]interface{}{
493+
"spec": map[string]interface{}{
494+
"controlPlaneEndpoint": map[string]interface{}{
495+
"port": int64(0),
496+
},
497+
},
498+
},
499+
},
500+
want: []Path{
501+
{"spec", "controlPlaneEndpoint", "port"},
502+
},
503+
},
504+
{
505+
name: "Ignore host and port when controlPlaneEndpoint host and port are set to their zero values",
506+
controlPlane: &unstructured.Unstructured{
507+
Object: map[string]interface{}{
508+
"spec": map[string]interface{}{
509+
"controlPlaneEndpoint": map[string]interface{}{
510+
"host": "",
511+
"port": int64(0),
512+
},
513+
},
514+
},
515+
},
516+
want: []Path{
517+
{"spec", "controlPlaneEndpoint", "host"},
518+
{"spec", "controlPlaneEndpoint", "port"},
519+
},
520+
},
521+
{
522+
name: "Ignore host when controlPlaneEndpoint host is to its zero values, even if port is set",
523+
controlPlane: &unstructured.Unstructured{
524+
Object: map[string]interface{}{
525+
"spec": map[string]interface{}{
526+
"controlPlaneEndpoint": map[string]interface{}{
527+
"host": "",
528+
"port": int64(6443),
529+
},
530+
},
531+
},
532+
},
533+
want: []Path{
534+
{"spec", "controlPlaneEndpoint", "host"},
535+
},
536+
},
537+
{
538+
name: "Ignore port when controlPlaneEndpoint port is to its zero values, even if host is set",
539+
controlPlane: &unstructured.Unstructured{
540+
Object: map[string]interface{}{
541+
"spec": map[string]interface{}{
542+
"controlPlaneEndpoint": map[string]interface{}{
543+
"host": "example.com",
544+
"port": int64(0),
545+
},
546+
},
547+
},
548+
},
549+
want: []Path{
550+
{"spec", "controlPlaneEndpoint", "port"},
551+
},
552+
},
553+
}
554+
for _, tt := range tests {
555+
t.Run(tt.name, func(t *testing.T) {
556+
g := NewWithT(t)
557+
got, err := InfrastructureCluster().IgnorePaths(tt.controlPlane)
558+
if tt.expectErr {
559+
g.Expect(err).To(HaveOccurred())
560+
return
561+
}
562+
g.Expect(err).ToNot(HaveOccurred())
563+
g.Expect(got).To(Equal(tt.want))
564+
})
565+
}
566+
}
567+
405568
func TestControlPlaneIsUpgrading(t *testing.T) {
406569
tests := []struct {
407570
name string

internal/controllers/topology/cluster/reconcile_state.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,17 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, s *scope.Scope)
346346
// Create or update the ControlPlaneObject for the ControlPlaneState.
347347
log := ctrl.LoggerFrom(ctx).WithValues(s.Desired.ControlPlane.Object.GetKind(), klog.KObj(s.Desired.ControlPlane.Object))
348348
ctx = ctrl.LoggerInto(ctx, log)
349+
350+
ignorePaths, err := contract.ControlPlane().IgnorePaths(s.Desired.ControlPlane.Object)
351+
if err != nil {
352+
return false, errors.Wrap(err, "failed to calculate ignore paths")
353+
}
349354
created, err := r.reconcileReferencedObject(ctx, reconcileReferencedObjectInput{
350355
cluster: s.Current.Cluster,
351356
current: s.Current.ControlPlane.Object,
352357
desired: s.Desired.ControlPlane.Object,
353358
versionGetter: contract.ControlPlane().Version().Get,
359+
ignorePaths: ignorePaths,
354360
})
355361
if err != nil {
356362
// Best effort cleanup of the InfrastructureMachineTemplate (only on creation).

0 commit comments

Comments
 (0)