Skip to content

Commit 91212d0

Browse files
committed
Fix infinite reconcile because of apiVersions in Cluster topology controller
Signed-off-by: Stefan Büringer [email protected]
1 parent ed56c4c commit 91212d0

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
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"
@@ -84,7 +85,7 @@ func (r *Reconciler) getCurrentState(ctx context.Context, s *scope.Scope) (*scop
8485
// getCurrentInfrastructureClusterState looks for the state of the InfrastructureCluster. If a reference is set but not
8586
// found, either from an error or the object not being found, an error is thrown.
8687
func (r *Reconciler) getCurrentInfrastructureClusterState(ctx context.Context, blueprintInfrastructureClusterTemplate *unstructured.Unstructured, cluster *clusterv1.Cluster) (*unstructured.Unstructured, error) {
87-
ref, err := alignRefAPIVersion(blueprintInfrastructureClusterTemplate, cluster.Spec.InfrastructureRef)
88+
ref, err := alignRefAPIVersion(blueprintInfrastructureClusterTemplate, cluster.Spec.InfrastructureRef, false)
8889
if err != nil {
8990
return nil, errors.Wrapf(err, "failed to read %s %s", cluster.Spec.InfrastructureRef.Kind, klog.KRef(cluster.Spec.InfrastructureRef.Namespace, cluster.Spec.InfrastructureRef.Name))
9091
}
@@ -109,7 +110,7 @@ func (r *Reconciler) getCurrentControlPlaneState(ctx context.Context, blueprintC
109110
res := &scope.ControlPlaneState{}
110111

111112
// Get the control plane object.
112-
ref, err := alignRefAPIVersion(blueprintControlPlane.Template, cluster.Spec.ControlPlaneRef)
113+
ref, err := alignRefAPIVersion(blueprintControlPlane.Template, cluster.Spec.ControlPlaneRef, false)
113114
if err != nil {
114115
return nil, errors.Wrapf(err, "failed to read %s %s", cluster.Spec.ControlPlaneRef.Kind, klog.KRef(cluster.Spec.ControlPlaneRef.Namespace, cluster.Spec.ControlPlaneRef.Name))
115116
}
@@ -134,7 +135,7 @@ func (r *Reconciler) getCurrentControlPlaneState(ctx context.Context, blueprintC
134135
if err != nil {
135136
return res, errors.Wrapf(err, "failed to get InfrastructureMachineTemplate reference for %s %s", res.Object.GetKind(), klog.KObj(res.Object))
136137
}
137-
ref, err = alignRefAPIVersion(blueprintControlPlane.InfrastructureMachineTemplate, machineInfrastructureRef)
138+
ref, err = alignRefAPIVersion(blueprintControlPlane.InfrastructureMachineTemplate, machineInfrastructureRef, true)
138139
if err != nil {
139140
return nil, errors.Wrapf(err, "failed to get InfrastructureMachineTemplate for %s %s", res.Object.GetKind(), klog.KObj(res.Object))
140141
}
@@ -229,11 +230,11 @@ func (r *Reconciler) getCurrentMachineDeploymentState(ctx context.Context, bluep
229230
if !ok {
230231
return nil, fmt.Errorf("failed to find MachineDeployment class %s in ClusterClass", mdClassName)
231232
}
232-
bootstrapRef, err = alignRefAPIVersion(mdBluePrint.BootstrapTemplate, bootstrapRef)
233+
bootstrapRef, err = alignRefAPIVersion(mdBluePrint.BootstrapTemplate, bootstrapRef, true)
233234
if err != nil {
234235
return nil, errors.Wrap(err, fmt.Sprintf("MachineDeployment %s Bootstrap reference could not be retrieved", klog.KObj(m)))
235236
}
236-
infraRef, err = alignRefAPIVersion(mdBluePrint.InfrastructureMachineTemplate, infraRef)
237+
infraRef, err = alignRefAPIVersion(mdBluePrint.InfrastructureMachineTemplate, infraRef, true)
237238
if err != nil {
238239
return nil, errors.Wrap(err, fmt.Sprintf("MachineDeployment %s Infrastructure reference could not be retrieved", klog.KObj(m)))
239240
}
@@ -349,11 +350,11 @@ func (r *Reconciler) getCurrentMachinePoolState(ctx context.Context, blueprintMa
349350
if !ok {
350351
return nil, fmt.Errorf("failed to find MachinePool class %s in ClusterClass", mpClassName)
351352
}
352-
bootstrapRef, err = alignRefAPIVersion(mpBluePrint.BootstrapTemplate, bootstrapRef)
353+
bootstrapRef, err = alignRefAPIVersion(mpBluePrint.BootstrapTemplate, bootstrapRef, false)
353354
if err != nil {
354355
return nil, errors.Wrap(err, fmt.Sprintf("MachinePool %s Bootstrap reference could not be retrieved", klog.KObj(m)))
355356
}
356-
infraRef, err = alignRefAPIVersion(mpBluePrint.InfrastructureMachinePoolTemplate, infraRef)
357+
infraRef, err = alignRefAPIVersion(mpBluePrint.InfrastructureMachinePoolTemplate, infraRef, false)
357358
if err != nil {
358359
return nil, errors.Wrap(err, fmt.Sprintf("MachinePool %s Infrastructure reference could not be retrieved", klog.KObj(m)))
359360
}
@@ -398,17 +399,27 @@ func (r *Reconciler) getCurrentMachinePoolState(ctx context.Context, blueprintMa
398399
// If group or kind was changed in the ClusterClass, an exact copy of the currentRef is returned because
399400
// it will end up in a diff and a rollout anyway.
400401
// Only bootstrap template refs in a ClusterClass can change their group and kind.
401-
func alignRefAPIVersion(templateFromClusterClass *unstructured.Unstructured, currentRef *corev1.ObjectReference) (*corev1.ObjectReference, error) {
402+
func alignRefAPIVersion(templateFromClusterClass *unstructured.Unstructured, currentRef *corev1.ObjectReference, isCurrentTemplate bool) (*corev1.ObjectReference, error) {
402403
currentGV, err := schema.ParseGroupVersion(currentRef.APIVersion)
403404
if err != nil {
404405
return nil, errors.Wrapf(err, "failed to parse apiVersion: %q", currentRef.APIVersion)
405406
}
406407

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

414425
return &corev1.ObjectReference{

internal/controllers/topology/cluster/current_state_test.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
corev1 "k8s.io/api/core/v1"
2828
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2929
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30+
"k8s.io/apimachinery/pkg/runtime/schema"
3031
"k8s.io/utils/ptr"
3132
"sigs.k8s.io/controller-runtime/pkg/client"
3233
"sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -1187,43 +1188,67 @@ func TestAlignRefAPIVersion(t *testing.T) {
11871188
name string
11881189
templateFromClusterClass *unstructured.Unstructured
11891190
currentRef *corev1.ObjectReference
1191+
isCurrentTemplate bool
11901192
want *corev1.ObjectReference
11911193
wantErr bool
11921194
}{
11931195
{
11941196
name: "Error for invalid apiVersion",
11951197
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
1196-
"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
1197-
"kind": "DockerCluster",
1198+
"apiVersion": schema.GroupVersion{Group: "infrastructure.cluster.x-k8s.io", Version: "v1beta1"}.String(),
1199+
"kind": "DockerClusterTemplate",
11981200
}},
11991201
currentRef: &corev1.ObjectReference{
12001202
APIVersion: "invalid/api/version",
12011203
Kind: "DockerCluster",
12021204
Name: "my-cluster-abc",
12031205
Namespace: metav1.NamespaceDefault,
12041206
},
1205-
wantErr: true,
1207+
isCurrentTemplate: false,
1208+
wantErr: true,
12061209
},
12071210
{
1208-
name: "Use apiVersion from ClusterClass: group and kind is the same",
1211+
name: "Use apiVersion from ClusterClass: group and kind is the same (+/- Template suffix)",
12091212
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
1210-
"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
1211-
"kind": "DockerCluster",
1213+
"apiVersion": schema.GroupVersion{Group: "infrastructure.cluster.x-k8s.io", Version: "v1beta1"}.String(),
1214+
"kind": "DockerClusterTemplate",
12121215
}},
12131216
currentRef: &corev1.ObjectReference{
1214-
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
1217+
APIVersion: "infrastructure.cluster.x-k8s.io/different", // should be overwritten with version from CC
12151218
Kind: "DockerCluster",
12161219
Name: "my-cluster-abc",
12171220
Namespace: metav1.NamespaceDefault,
12181221
},
1222+
isCurrentTemplate: false,
12191223
want: &corev1.ObjectReference{
1220-
// Group & kind is the same => apiVersion is taken from ClusterClass.
1221-
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
1224+
// Group & kind is the same (+/- Template suffix) => apiVersion is taken from ClusterClass.
1225+
APIVersion: schema.GroupVersion{Group: "infrastructure.cluster.x-k8s.io", Version: "v1beta1"}.String(),
12221226
Kind: "DockerCluster",
12231227
Name: "my-cluster-abc",
12241228
Namespace: metav1.NamespaceDefault,
12251229
},
12261230
},
1231+
{
1232+
name: "Use apiVersion from ClusterClass: group and kind is the same",
1233+
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
1234+
"apiVersion": schema.GroupVersion{Group: "bootstrap.cluster.x-k8s.io", Version: "v1beta1"}.String(),
1235+
"kind": "KubeadmConfigTemplate",
1236+
}},
1237+
currentRef: &corev1.ObjectReference{
1238+
APIVersion: "bootstrap.cluster.x-k8s.io/different", // should be overwritten with version from CC
1239+
Kind: "KubeadmConfigTemplate",
1240+
Name: "my-cluster-abc",
1241+
Namespace: metav1.NamespaceDefault,
1242+
},
1243+
isCurrentTemplate: true,
1244+
want: &corev1.ObjectReference{
1245+
// Group & kind is the same => apiVersion is taken from ClusterClass.
1246+
APIVersion: schema.GroupVersion{Group: "bootstrap.cluster.x-k8s.io", Version: "v1beta1"}.String(),
1247+
Kind: "KubeadmConfigTemplate",
1248+
Name: "my-cluster-abc",
1249+
Namespace: metav1.NamespaceDefault,
1250+
},
1251+
},
12271252
{
12281253
name: "Use apiVersion from currentRef: kind is different",
12291254
templateFromClusterClass: &unstructured.Unstructured{Object: map[string]interface{}{
@@ -1236,6 +1261,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12361261
Name: "my-cluster-abc",
12371262
Namespace: metav1.NamespaceDefault,
12381263
},
1264+
isCurrentTemplate: true,
12391265
want: &corev1.ObjectReference{
12401266
// kind is different => apiVersion is taken from currentRef.
12411267
APIVersion: "bootstrap.cluster.x-k8s.io/v1beta1",
@@ -1256,6 +1282,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12561282
Name: "my-cluster-abc",
12571283
Namespace: metav1.NamespaceDefault,
12581284
},
1285+
isCurrentTemplate: true,
12591286
want: &corev1.ObjectReference{
12601287
// group is different => apiVersion is taken from currentRef.
12611288
APIVersion: "bootstrap.cluster.x-k8s.io/v1beta1",
@@ -1269,7 +1296,7 @@ func TestAlignRefAPIVersion(t *testing.T) {
12691296
t.Run(tt.name, func(t *testing.T) {
12701297
g := NewWithT(t)
12711298

1272-
got, err := alignRefAPIVersion(tt.templateFromClusterClass, tt.currentRef)
1299+
got, err := alignRefAPIVersion(tt.templateFromClusterClass, tt.currentRef, tt.isCurrentTemplate)
12731300
if tt.wantErr {
12741301
g.Expect(err).To(HaveOccurred())
12751302
return

0 commit comments

Comments
 (0)