Skip to content

Commit 64a1b15

Browse files
committed
Fix infinite reconcile because of apiVersions in Cluster topology controller
Signed-off-by: Stefan Büringer buringerst@vmware.com
1 parent db916ae commit 64a1b15

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
@@ -1187,43 +1187,67 @@ func TestAlignRefAPIVersion(t *testing.T) {
11871187
name string
11881188
templateFromClusterClass *unstructured.Unstructured
11891189
currentRef *corev1.ObjectReference
1190+
isCurrentTemplate bool
11901191
want *corev1.ObjectReference
11911192
wantErr bool
11921193
}{
11931194
{
11941195
name: "Error for invalid apiVersion",
11951196
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
11961197
"apiVersion": clusterv1.GroupVersionInfrastructure.String(),
1197-
"kind": "DockerCluster",
1198+
"kind": "DockerClusterTemplate",
11981199
}},
11991200
currentRef: &corev1.ObjectReference{
12001201
APIVersion: "invalid/api/version",
12011202
Kind: "DockerCluster",
12021203
Name: "my-cluster-abc",
12031204
Namespace: metav1.NamespaceDefault,
12041205
},
1205-
wantErr: true,
1206+
isCurrentTemplate: false,
1207+
wantErr: true,
12061208
},
12071209
{
1208-
name: "Use apiVersion from ClusterClass: group and kind is the same",
1210+
name: "Use apiVersion from ClusterClass: group and kind is the same (+/- Template suffix)",
12091211
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
12101212
"apiVersion": clusterv1.GroupVersionInfrastructure.String(),
1211-
"kind": "DockerCluster",
1213+
"kind": "DockerClusterTemplate",
12121214
}},
12131215
currentRef: &corev1.ObjectReference{
1214-
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
1216+
APIVersion: "infrastructure.cluster.x-k8s.io/different", // should be overwritten with version from CC
12151217
Kind: "DockerCluster",
12161218
Name: "my-cluster-abc",
12171219
Namespace: metav1.NamespaceDefault,
12181220
},
1221+
isCurrentTemplate: false,
12191222
want: &corev1.ObjectReference{
1220-
// Group & kind is the same => apiVersion is taken from ClusterClass.
1223+
// Group & kind is the same (+/- Template suffix) => apiVersion is taken from ClusterClass.
12211224
APIVersion: clusterv1.GroupVersionInfrastructure.String(),
12221225
Kind: "DockerCluster",
12231226
Name: "my-cluster-abc",
12241227
Namespace: metav1.NamespaceDefault,
12251228
},
12261229
},
1230+
{
1231+
name: "Use apiVersion from ClusterClass: group and kind is the same",
1232+
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
1233+
"apiVersion": clusterv1.GroupVersionBootstrap.String(),
1234+
"kind": "KubeadmConfigTemplate",
1235+
}},
1236+
currentRef: &corev1.ObjectReference{
1237+
APIVersion: "bootstrap.cluster.x-k8s.io/different", // should be overwritten with version from CC
1238+
Kind: "KubeadmConfigTemplate",
1239+
Name: "my-cluster-abc",
1240+
Namespace: metav1.NamespaceDefault,
1241+
},
1242+
isCurrentTemplate: true,
1243+
want: &corev1.ObjectReference{
1244+
// Group & kind is the same => apiVersion is taken from ClusterClass.
1245+
APIVersion: clusterv1.GroupVersionBootstrap.String(),
1246+
Kind: "KubeadmConfigTemplate",
1247+
Name: "my-cluster-abc",
1248+
Namespace: metav1.NamespaceDefault,
1249+
},
1250+
},
12271251
{
12281252
name: "Use apiVersion from currentRef: kind is different",
12291253
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
@@ -1236,6 +1260,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12361260
Name: "my-cluster-abc",
12371261
Namespace: metav1.NamespaceDefault,
12381262
},
1263+
isCurrentTemplate: true,
12391264
want: &corev1.ObjectReference{
12401265
// kind is different => apiVersion is taken from currentRef.
12411266
APIVersion: clusterv1.GroupVersionBootstrap.String(),
@@ -1259,6 +1284,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12591284
Name: "my-cluster-abc",
12601285
Namespace: metav1.NamespaceDefault,
12611286
},
1287+
isCurrentTemplate: true,
12621288
want: &corev1.ObjectReference{
12631289
// group is different => apiVersion is taken from currentRef.
12641290
APIVersion: clusterv1.GroupVersionBootstrap.String(),
@@ -1272,7 +1298,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12721298
t.Run(tt.name, func(t *testing.T) {
12731299
g := NewWithT(t)
12741300

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

0 commit comments

Comments
 (0)