Skip to content

Commit da5cc32

Browse files
authored
Merge pull request #12341 from sbueringer/pr-fix-infinite-reconcile
🐛 Fix continuous reconciles because of apiVersion differences in Cluster topology controller
2 parents db39f5f + 64a1b15 commit da5cc32

File tree

2 files changed

+55
-18
lines changed

2 files changed

+55
-18
lines changed

internal/controllers/topology/cluster/current_state.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package cluster
1919
import (
2020
"context"
2121
"fmt"
22+
"strings"
2223

2324
"github.com/pkg/errors"
2425
corev1 "k8s.io/api/core/v1"
@@ -83,7 +84,7 @@ func (r *Reconciler) getCurrentState(ctx context.Context, s *scope.Scope) (*scop
8384
// getCurrentInfrastructureClusterState looks for the state of the InfrastructureCluster. If a reference is set but not
8485
// found, either from an error or the object not being found, an error is thrown.
8586
func (r *Reconciler) getCurrentInfrastructureClusterState(ctx context.Context, blueprintInfrastructureClusterTemplate *unstructured.Unstructured, cluster *clusterv1.Cluster) (*unstructured.Unstructured, error) {
86-
ref, err := alignRefAPIVersion(blueprintInfrastructureClusterTemplate, cluster.Spec.InfrastructureRef)
87+
ref, err := alignRefAPIVersion(blueprintInfrastructureClusterTemplate, cluster.Spec.InfrastructureRef, false)
8788
if err != nil {
8889
return nil, errors.Wrapf(err, "failed to read %s %s", cluster.Spec.InfrastructureRef.Kind, klog.KRef(cluster.Spec.InfrastructureRef.Namespace, cluster.Spec.InfrastructureRef.Name))
8990
}
@@ -108,7 +109,7 @@ func (r *Reconciler) getCurrentControlPlaneState(ctx context.Context, blueprintC
108109
res := &scope.ControlPlaneState{}
109110

110111
// Get the control plane object.
111-
ref, err := alignRefAPIVersion(blueprintControlPlane.Template, cluster.Spec.ControlPlaneRef)
112+
ref, err := alignRefAPIVersion(blueprintControlPlane.Template, cluster.Spec.ControlPlaneRef, false)
112113
if err != nil {
113114
return nil, errors.Wrapf(err, "failed to read %s %s", cluster.Spec.ControlPlaneRef.Kind, klog.KRef(cluster.Spec.ControlPlaneRef.Namespace, cluster.Spec.ControlPlaneRef.Name))
114115
}
@@ -133,7 +134,7 @@ func (r *Reconciler) getCurrentControlPlaneState(ctx context.Context, blueprintC
133134
if err != nil {
134135
return res, errors.Wrapf(err, "failed to get InfrastructureMachineTemplate reference for %s %s", res.Object.GetKind(), klog.KObj(res.Object))
135136
}
136-
ref, err = alignRefAPIVersion(blueprintControlPlane.InfrastructureMachineTemplate, machineInfrastructureRef)
137+
ref, err = alignRefAPIVersion(blueprintControlPlane.InfrastructureMachineTemplate, machineInfrastructureRef, true)
137138
if err != nil {
138139
return nil, errors.Wrapf(err, "failed to get InfrastructureMachineTemplate for %s %s", res.Object.GetKind(), klog.KObj(res.Object))
139140
}
@@ -228,11 +229,11 @@ func (r *Reconciler) getCurrentMachineDeploymentState(ctx context.Context, bluep
228229
if !ok {
229230
return nil, fmt.Errorf("failed to find MachineDeployment class %s in ClusterClass", mdClassName)
230231
}
231-
bootstrapRef, err = alignRefAPIVersion(mdBluePrint.BootstrapTemplate, bootstrapRef)
232+
bootstrapRef, err = alignRefAPIVersion(mdBluePrint.BootstrapTemplate, bootstrapRef, true)
232233
if err != nil {
233234
return nil, errors.Wrap(err, fmt.Sprintf("MachineDeployment %s Bootstrap reference could not be retrieved", klog.KObj(m)))
234235
}
235-
infraRef, err = alignRefAPIVersion(mdBluePrint.InfrastructureMachineTemplate, infraRef)
236+
infraRef, err = alignRefAPIVersion(mdBluePrint.InfrastructureMachineTemplate, infraRef, true)
236237
if err != nil {
237238
return nil, errors.Wrap(err, fmt.Sprintf("MachineDeployment %s Infrastructure reference could not be retrieved", klog.KObj(m)))
238239
}
@@ -348,11 +349,11 @@ func (r *Reconciler) getCurrentMachinePoolState(ctx context.Context, blueprintMa
348349
if !ok {
349350
return nil, fmt.Errorf("failed to find MachinePool class %s in ClusterClass", mpClassName)
350351
}
351-
bootstrapRef, err = alignRefAPIVersion(mpBluePrint.BootstrapTemplate, bootstrapRef)
352+
bootstrapRef, err = alignRefAPIVersion(mpBluePrint.BootstrapTemplate, bootstrapRef, false)
352353
if err != nil {
353354
return nil, errors.Wrap(err, fmt.Sprintf("MachinePool %s Bootstrap reference could not be retrieved", klog.KObj(m)))
354355
}
355-
infraRef, err = alignRefAPIVersion(mpBluePrint.InfrastructureMachinePoolTemplate, infraRef)
356+
infraRef, err = alignRefAPIVersion(mpBluePrint.InfrastructureMachinePoolTemplate, infraRef, false)
356357
if err != nil {
357358
return nil, errors.Wrap(err, fmt.Sprintf("MachinePool %s Infrastructure reference could not be retrieved", klog.KObj(m)))
358359
}
@@ -397,17 +398,27 @@ func (r *Reconciler) getCurrentMachinePoolState(ctx context.Context, blueprintMa
397398
// If group or kind was changed in the ClusterClass, an exact copy of the currentRef is returned because
398399
// it will end up in a diff and a rollout anyway.
399400
// Only bootstrap template refs in a ClusterClass can change their group and kind.
400-
func alignRefAPIVersion(templateFromClusterClass *unstructured.Unstructured, currentRef *corev1.ObjectReference) (*corev1.ObjectReference, error) {
401+
func alignRefAPIVersion(templateFromClusterClass *unstructured.Unstructured, currentRef *corev1.ObjectReference, isCurrentTemplate bool) (*corev1.ObjectReference, error) {
401402
currentGV, err := schema.ParseGroupVersion(currentRef.APIVersion)
402403
if err != nil {
403404
return nil, errors.Wrapf(err, "failed to parse apiVersion: %q", currentRef.APIVersion)
404405
}
405406

406407
apiVersion := currentRef.APIVersion
407408
// Use apiVersion from ClusterClass if group and kind is the same.
408-
if templateFromClusterClass.GroupVersionKind().Group == currentGV.Group &&
409-
templateFromClusterClass.GetKind() == currentRef.Kind {
410-
apiVersion = templateFromClusterClass.GetAPIVersion()
409+
if templateFromClusterClass.GroupVersionKind().Group == currentGV.Group {
410+
if isCurrentTemplate {
411+
// If the current object is a template, kind has to be identical.
412+
if templateFromClusterClass.GetKind() == currentRef.Kind {
413+
apiVersion = templateFromClusterClass.GetAPIVersion()
414+
}
415+
} else {
416+
// If the current object is not a template, currentRef.Kind should be the kind from CC without the Template suffix,
417+
// e.g. KubeadmControlPlaneTemplate == KubeadmControlPlane
418+
if strings.TrimSuffix(templateFromClusterClass.GetKind(), "Template") == currentRef.Kind {
419+
apiVersion = templateFromClusterClass.GetAPIVersion()
420+
}
421+
}
411422
}
412423

413424
return &corev1.ObjectReference{

internal/controllers/topology/cluster/current_state_test.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,43 +1186,67 @@ func TestAlignRefAPIVersion(t *testing.T) {
11861186
name string
11871187
templateFromClusterClass *unstructured.Unstructured
11881188
currentRef *corev1.ObjectReference
1189+
isCurrentTemplate bool
11891190
want *corev1.ObjectReference
11901191
wantErr bool
11911192
}{
11921193
{
11931194
name: "Error for invalid apiVersion",
11941195
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
11951196
"apiVersion": clusterv1.GroupVersionInfrastructure.String(),
1196-
"kind": "DockerCluster",
1197+
"kind": "DockerClusterTemplate",
11971198
}},
11981199
currentRef: &corev1.ObjectReference{
11991200
APIVersion: "invalid/api/version",
12001201
Kind: "DockerCluster",
12011202
Name: "my-cluster-abc",
12021203
Namespace: metav1.NamespaceDefault,
12031204
},
1204-
wantErr: true,
1205+
isCurrentTemplate: false,
1206+
wantErr: true,
12051207
},
12061208
{
1207-
name: "Use apiVersion from ClusterClass: group and kind is the same",
1209+
name: "Use apiVersion from ClusterClass: group and kind is the same (+/- Template suffix)",
12081210
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
12091211
"apiVersion": clusterv1.GroupVersionInfrastructure.String(),
1210-
"kind": "DockerCluster",
1212+
"kind": "DockerClusterTemplate",
12111213
}},
12121214
currentRef: &corev1.ObjectReference{
1213-
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
1215+
APIVersion: "infrastructure.cluster.x-k8s.io/different", // should be overwritten with version from CC
12141216
Kind: "DockerCluster",
12151217
Name: "my-cluster-abc",
12161218
Namespace: metav1.NamespaceDefault,
12171219
},
1220+
isCurrentTemplate: false,
12181221
want: &corev1.ObjectReference{
1219-
// Group & kind is the same => apiVersion is taken from ClusterClass.
1222+
// Group & kind is the same (+/- Template suffix) => apiVersion is taken from ClusterClass.
12201223
APIVersion: clusterv1.GroupVersionInfrastructure.String(),
12211224
Kind: "DockerCluster",
12221225
Name: "my-cluster-abc",
12231226
Namespace: metav1.NamespaceDefault,
12241227
},
12251228
},
1229+
{
1230+
name: "Use apiVersion from ClusterClass: group and kind is the same",
1231+
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
1232+
"apiVersion": clusterv1.GroupVersionBootstrap.String(),
1233+
"kind": "KubeadmConfigTemplate",
1234+
}},
1235+
currentRef: &corev1.ObjectReference{
1236+
APIVersion: "bootstrap.cluster.x-k8s.io/different", // should be overwritten with version from CC
1237+
Kind: "KubeadmConfigTemplate",
1238+
Name: "my-cluster-abc",
1239+
Namespace: metav1.NamespaceDefault,
1240+
},
1241+
isCurrentTemplate: true,
1242+
want: &corev1.ObjectReference{
1243+
// Group & kind is the same => apiVersion is taken from ClusterClass.
1244+
APIVersion: clusterv1.GroupVersionBootstrap.String(),
1245+
Kind: "KubeadmConfigTemplate",
1246+
Name: "my-cluster-abc",
1247+
Namespace: metav1.NamespaceDefault,
1248+
},
1249+
},
12261250
{
12271251
name: "Use apiVersion from currentRef: kind is different",
12281252
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
@@ -1235,6 +1259,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12351259
Name: "my-cluster-abc",
12361260
Namespace: metav1.NamespaceDefault,
12371261
},
1262+
isCurrentTemplate: true,
12381263
want: &corev1.ObjectReference{
12391264
// kind is different => apiVersion is taken from currentRef.
12401265
APIVersion: clusterv1.GroupVersionBootstrap.String(),
@@ -1258,6 +1283,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12581283
Name: "my-cluster-abc",
12591284
Namespace: metav1.NamespaceDefault,
12601285
},
1286+
isCurrentTemplate: true,
12611287
want: &corev1.ObjectReference{
12621288
// group is different => apiVersion is taken from currentRef.
12631289
APIVersion: clusterv1.GroupVersionBootstrap.String(),
@@ -1271,7 +1297,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12711297
t.Run(tt.name, func(t *testing.T) {
12721298
g := NewWithT(t)
12731299

1274-
got, err := alignRefAPIVersion(tt.templateFromClusterClass, tt.currentRef)
1300+
got, err := alignRefAPIVersion(tt.templateFromClusterClass, tt.currentRef, tt.isCurrentTemplate)
12751301
if tt.wantErr {
12761302
g.Expect(err).To(HaveOccurred())
12771303
return

0 commit comments

Comments
 (0)