Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions controlplane/kubeadm/controllers/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controllers

import (
"context"
"fmt"

"github.com/blang/semver"
"github.com/pkg/errors"
Expand Down Expand Up @@ -50,6 +51,15 @@ func (r *KubeadmControlPlaneReconciler) upgradeControlPlane(
return ctrl.Result{}, errors.Wrapf(err, "failed to parse kubernetes version %q", kcp.Spec.Version)
}

supported, err := workloadCluster.IsKubernetesVersionSupported(ctx, parsedVersion)
if err != nil {
return ctrl.Result{}, errors.Wrapf(err, "failed to check if Kubernetes version is supported")
}
if !supported {
logger.Info(fmt.Sprintf("Kubernetes version %q is not supported for management clusters", kcp.Spec.Version))
return ctrl.Result{}, nil
}

if err := workloadCluster.ReconcileKubeletRBACRole(ctx, parsedVersion); err != nil {
return ctrl.Result{}, errors.Wrap(err, "failed to reconcile the remote kubelet RBAC role")
}
Expand Down
29 changes: 29 additions & 0 deletions controlplane/kubeadm/internal/workload_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -70,6 +71,7 @@ type WorkloadCluster interface {
EtcdMembers(ctx context.Context) ([]string, error)

// Upgrade related tasks.
IsKubernetesVersionSupported(ctx context.Context, version semver.Version) (bool, error)
ReconcileKubeletRBACBinding(ctx context.Context, version semver.Version) error
ReconcileKubeletRBACRole(ctx context.Context, version semver.Version) error
UpdateKubernetesVersionInKubeadmConfigMap(ctx context.Context, version semver.Version) error
Expand Down Expand Up @@ -119,6 +121,33 @@ func (w *Workload) getConfigMap(ctx context.Context, configMap ctrlclient.Object
return original.DeepCopy(), nil
}

var managementClusterVersionCeiling = semver.MustParse("1.22.0")

// IsKubernetesVersionSupported checks if the Kubernetes version is supported.
// Kubernetes versions >= v1.22.0 for management clusters are not supported.
// Management clusters are identified by the existence of the KubeadmControlPlane CRD.
func (w *Workload) IsKubernetesVersionSupported(ctx context.Context, version semver.Version) (bool, error) {
// Kubernetes version is < v1.22.0.
if version.LT(managementClusterVersionCeiling) {
return true, nil
}

// Try to get *metav1.PartialObjectMetadata of KubeadmControlPlane CRD.
out := &metav1.PartialObjectMetadata{}
out.SetGroupVersionKind(apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"))
key := ctrlclient.ObjectKey{Name: "kubeadmcontrolplanes.controlplane.cluster.x-k8s.io"}
if err := w.Client.Get(ctx, key, out); err != nil {
if apierrors.IsNotFound(err) {
// KubeadmControlPlane CRD could not be found on the workload cluster (and thus it's not a management cluster).
return true, nil
}
// Get *metav1.PartialObjectMetadata request for KubeadmControlPlane CRD failed.
return false, err
}
// Request did not fail, that means we found the KubeadmControlPlane CRD (and thus a management cluster).
return false, nil
}

// UpdateKubernetesVersionInKubeadmConfigMap updates the kubernetes version in the kubeadm config map.
func (w *Workload) UpdateImageRepositoryInKubeadmConfigMap(ctx context.Context, imageRepository string) error {
configMapKey := ctrlclient.ObjectKey{Name: "kubeadm-config", Namespace: metav1.NamespaceSystem}
Expand Down
71 changes: 71 additions & 0 deletions controlplane/kubeadm/internal/workload_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,77 @@ func TestClusterStatus(t *testing.T) {
}
}

func TestIsKubernetesVersionSupported(t *testing.T) {
tests := []struct {
name string
kcpVersion string
kcpCRDExists bool
want bool
}{
{
name: "workload cluster and version < v1.22.0",
kcpVersion: "v1.21.0",
kcpCRDExists: false,
want: true,
},
{
name: "workload cluster and version >= v1.22.0",
kcpVersion: "v1.22.0",
kcpCRDExists: false,
want: true,
},
{
name: "management cluster and version < v1.22.0",
kcpVersion: "v1.21.0",
kcpCRDExists: true,
want: true,
},
{
name: "management cluster and version >= v1.22.0",
kcpVersion: "v1.22.0",
kcpCRDExists: true,
want: false,
}, {
name: "management cluster and version >= v1.23.0",
kcpVersion: "v1.23.5",
kcpCRDExists: true,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

objs := []runtime.Object{}
if tt.kcpCRDExists {
objs = append(objs, &metav1.PartialObjectMetadata{
TypeMeta: metav1.TypeMeta{
APIVersion: "apiextensions.k8s.io/v1",
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "kubeadmcontrolplanes.controlplane.cluster.x-k8s.io",
},
})
}
fakeScheme := runtime.NewScheme()
_ = metav1.AddMetaToScheme(fakeScheme)
client := fake.NewFakeClientWithScheme(fakeScheme, objs...)

w := &Workload{
Client: client,
}

parsedVersion, err := semver.ParseTolerant(tt.kcpVersion)
g.Expect(err).ToNot(HaveOccurred())

got, err := w.IsKubernetesVersionSupported(context.Background(), parsedVersion)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(got).To(Equal(tt.want))
})
}
}

func getProxyImageInfo(ctx context.Context, client ctrlclient.Client) (string, error) {
ds := &appsv1.DaemonSet{}

Expand Down