Skip to content

Commit f9b3306

Browse files
committed
KCP: block upgrade to versions with old registry, improve registry
handling Signed-off-by: Stefan Büringer [email protected]
1 parent df9c56c commit f9b3306

15 files changed

+467
-125
lines changed

bootstrap/kubeadm/api/v1beta1/kubeadm_types.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,16 @@ type ClusterConfiguration struct {
125125
CertificatesDir string `json:"certificatesDir,omitempty"`
126126

127127
// ImageRepository sets the container registry to pull images from.
128-
// If empty, `registry.k8s.io` will be used by default; in case of kubernetes version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`)
129-
// `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components and for kube-proxy, while `registry.k8s.io`
130-
// will be used for all the other images.
128+
// * If not set, the default registry of kubeadm will be used, i.e.
129+
// * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0
130+
// * k8s.gcr.io (old registry): all older versions
131+
// Please note that when imageRepository is not set we don't allow upgrades to
132+
// versions >= v1.22.0 which use the old registry (k8s.gcr.io). Please use
133+
// a newer patch version with the new registry instead (i.e. >= v1.22.17,
134+
// >= v1.23.15, >= v1.24.9, >= v1.25.0).
135+
// * If the version is a CI build (kubernetes version starts with `ci/` or `ci-cross/`)
136+
// `gcr.io/k8s-staging-ci-images` will be used as a default for control plane components
137+
// and for kube-proxy, while `registry.k8s.io` will be used for all the other images.
131138
// +optional
132139
ImageRepository string `json:"imageRepository,omitempty"`
133140

bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml

Lines changed: 13 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml

Lines changed: 15 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ type KubeadmControlPlaneSpec struct {
6060
Replicas *int32 `json:"replicas,omitempty"`
6161

6262
// Version defines the desired Kubernetes version.
63+
// Please note that if kubeadmConfigSpec.ClusterConfiguration.imageRepository is not set
64+
// we don't allow upgrades to versions >= v1.22.0 for which kubeadm uses the old registry (k8s.gcr.io).
65+
// Please use a newer patch version with the new registry instead. The default registries of kubeadm are:
66+
// * registry.k8s.io (new registry): >= v1.22.17, >= v1.23.15, >= v1.24.9, >= v1.25.0
67+
// * k8s.gcr.io (old registry): all older versions
6368
Version string `json:"version"`
6469

6570
// MachineTemplate contains information about how machines

controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"sigs.k8s.io/controller-runtime/pkg/webhook"
3434

3535
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
36+
"sigs.k8s.io/cluster-api/internal/util/kubeadm"
3637
"sigs.k8s.io/cluster-api/util/container"
3738
"sigs.k8s.io/cluster-api/util/version"
3839
)
@@ -601,7 +602,10 @@ func (in *KubeadmControlPlane) validateVersion(previousVersion string) (allErrs
601602
return allErrs
602603
}
603604

604-
// Since upgrades to the next minor version are allowed, irrespective of the patch version.
605+
// Validate that the update is upgrading at most one minor version.
606+
// Note: Skipping a minor version is not allowed.
607+
// Note: Checking against this ceilVersion allows upgrading to the next minor
608+
// version irrespective of the patch version.
605609
ceilVersion := semver.Version{
606610
Major: fromVersion.Major,
607611
Minor: fromVersion.Minor + 2,
@@ -616,6 +620,31 @@ func (in *KubeadmControlPlane) validateVersion(previousVersion string) (allErrs
616620
)
617621
}
618622

623+
// The Kubernetes ecosystem has been requested to move users to the new registry due to cost issues.
624+
// This validation enforces the move to the new registry by forcing users to upgrade to kubeadm versions
625+
// with the new registry.
626+
// NOTE: This only affects users relying on the community maintained registry.
627+
// NOTE: Pinning to the upstream registry is not recommended because it could lead to issues
628+
// given how the migration has been implemented in kubeadm.
629+
//
630+
// Block if imageRepository is not set (i.e. the default registry should be used),
631+
if (in.Spec.KubeadmConfigSpec.ClusterConfiguration == nil ||
632+
in.Spec.KubeadmConfigSpec.ClusterConfiguration.ImageRepository == "") &&
633+
// the version changed (i.e. we have an upgrade),
634+
toVersion.NE(fromVersion) &&
635+
// the version is >= v1.22.0 and < v1.26.0
636+
toVersion.GTE(kubeadm.MinKubernetesVersionImageRegistryMigration) &&
637+
toVersion.LT(kubeadm.NextKubernetesVersionImageRegistryMigration) &&
638+
// and the default registry of the new Kubernetes/kubeadm version is the old default registry.
639+
kubeadm.GetDefaultRegistry(toVersion) == kubeadm.OldDefaultImageRepository {
640+
allErrs = append(allErrs,
641+
field.Forbidden(
642+
field.NewPath("spec", "version"),
643+
"cannot upgrade to a Kubernetes/kubeadm version which is using the old default registry. Please use a newer Kubernetes patch release which is using the new default registry (>= v1.22.17, >= v1.23.15, >= v1.24.9)",
644+
),
645+
)
646+
}
647+
619648
return allErrs
620649
}
621650

controlplane/kubeadm/api/v1beta1/kubeadm_control_plane_webhook_test.go

Lines changed: 156 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -451,14 +451,6 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
451451
kubernetesVersion := before.DeepCopy()
452452
kubernetesVersion.Spec.KubeadmConfigSpec.ClusterConfiguration.KubernetesVersion = "some kubernetes version"
453453

454-
prevKCPWithVersion := func(version string) *KubeadmControlPlane {
455-
prev := before.DeepCopy()
456-
prev.Spec.Version = version
457-
return prev
458-
}
459-
skipMinorControlPlaneVersion := prevKCPWithVersion("v1.18.1")
460-
emptyControlPlaneVersion := prevKCPWithVersion("")
461-
462454
controlPlaneEndpoint := before.DeepCopy()
463455
controlPlaneEndpoint.Spec.KubeadmConfigSpec.ClusterConfiguration.ControlPlaneEndpoint = "some control plane endpoint"
464456

@@ -611,13 +603,6 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
611603
DataDir: "/data",
612604
}
613605

614-
disallowedUpgrade118Prev := prevKCPWithVersion("v1.18.8")
615-
disallowedUpgrade119Version := before.DeepCopy()
616-
disallowedUpgrade119Version.Spec.Version = "v1.19.0"
617-
618-
disallowedUpgrade120AlphaVersion := before.DeepCopy()
619-
disallowedUpgrade120AlphaVersion.Spec.Version = "v1.20.0-alpha.0.734_ba502ee555924a"
620-
621606
updateNTPServers := before.DeepCopy()
622607
updateNTPServers.Spec.KubeadmConfigSpec.NTP.Servers = []string{"new-server"}
623608

@@ -925,36 +910,6 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
925910
before: withoutClusterConfiguration,
926911
kcp: afterEtcdLocalDirAddition,
927912
},
928-
{
929-
name: "should fail when skipping control plane minor versions",
930-
expectErr: true,
931-
before: before,
932-
kcp: skipMinorControlPlaneVersion,
933-
},
934-
{
935-
name: "should fail when no control plane version is passed",
936-
expectErr: true,
937-
before: before,
938-
kcp: emptyControlPlaneVersion,
939-
},
940-
{
941-
name: "should pass if control plane version is the same",
942-
expectErr: false,
943-
before: before,
944-
kcp: before.DeepCopy(),
945-
},
946-
{
947-
name: "should return error when trying to upgrade to v1.19.0",
948-
expectErr: true,
949-
before: disallowedUpgrade118Prev,
950-
kcp: disallowedUpgrade119Version,
951-
},
952-
{
953-
name: "should return error when trying to upgrade two minor versions",
954-
expectErr: true,
955-
before: disallowedUpgrade118Prev,
956-
kcp: disallowedUpgrade120AlphaVersion,
957-
},
958913
{
959914
name: "should not return an error when maxSurge value is updated to 0",
960915
expectErr: false,
@@ -1051,6 +1006,162 @@ func TestKubeadmControlPlaneValidateUpdate(t *testing.T) {
10511006
}
10521007
}
10531008

1009+
func TestValidateVersion(t *testing.T) {
1010+
tests := []struct {
1011+
name string
1012+
clusterConfiguration *bootstrapv1.ClusterConfiguration
1013+
oldVersion string
1014+
newVersion string
1015+
expectErr bool
1016+
}{
1017+
// Basic validation of old and new version.
1018+
{
1019+
name: "error when old version is empty",
1020+
oldVersion: "",
1021+
newVersion: "v1.16.6",
1022+
expectErr: true,
1023+
},
1024+
{
1025+
name: "error when old version is invalid",
1026+
oldVersion: "invalid-version",
1027+
newVersion: "v1.18.1",
1028+
expectErr: true,
1029+
},
1030+
{
1031+
name: "error when new version is empty",
1032+
oldVersion: "v1.16.6",
1033+
newVersion: "",
1034+
expectErr: true,
1035+
},
1036+
{
1037+
name: "error when new version is invalid",
1038+
oldVersion: "v1.18.1",
1039+
newVersion: "invalid-version",
1040+
expectErr: true,
1041+
},
1042+
// Validation that we block upgrade to v1.19.0.
1043+
// Note: Upgrading to v1.19.0 is not supported, because of issues in v1.19.0,
1044+
// see: https://github.com/kubernetes-sigs/cluster-api/issues/3564
1045+
{
1046+
name: "error when upgrading to v1.19.0",
1047+
oldVersion: "v1.18.8",
1048+
newVersion: "v1.19.0",
1049+
expectErr: true,
1050+
},
1051+
{
1052+
name: "pass when both versions are v1.19.0",
1053+
oldVersion: "v1.19.0",
1054+
newVersion: "v1.19.0",
1055+
expectErr: false,
1056+
},
1057+
// Validation for skip-level upgrades.
1058+
{
1059+
name: "error when upgrading two minor versions",
1060+
oldVersion: "v1.18.8",
1061+
newVersion: "v1.20.0-alpha.0.734_ba502ee555924a",
1062+
expectErr: true,
1063+
},
1064+
{
1065+
name: "pass when upgrading one minor version",
1066+
oldVersion: "v1.20.1",
1067+
newVersion: "v1.21.18",
1068+
expectErr: false,
1069+
},
1070+
// Validation for usage of the old registry.
1071+
// Notes:
1072+
// * kubeadm versions < v1.22 are always using the old registry.
1073+
// * kubeadm versions >= v1.25.0 are always using the new registry.
1074+
// * kubeadm versions in between are using the new registry
1075+
// starting with certain patch versions.
1076+
// This test validates that we don't block upgrades for < v1.22.0 and >= v1.25.0
1077+
// and block upgrades to kubeadm versions in between with the old registry.
1078+
{
1079+
name: "pass when imageRepository is set",
1080+
clusterConfiguration: &bootstrapv1.ClusterConfiguration{
1081+
ImageRepository: "k8s.gcr.io",
1082+
},
1083+
oldVersion: "v1.21.1",
1084+
newVersion: "v1.22.16",
1085+
expectErr: false,
1086+
},
1087+
{
1088+
name: "pass when version didn't change",
1089+
oldVersion: "v1.22.16",
1090+
newVersion: "v1.22.16",
1091+
expectErr: false,
1092+
},
1093+
{
1094+
name: "pass when new version is < v1.22.0",
1095+
oldVersion: "v1.20.10",
1096+
newVersion: "v1.21.5",
1097+
expectErr: false,
1098+
},
1099+
{
1100+
name: "error when new version is using old registry (v1.22.0 <= version <= v1.22.16)",
1101+
oldVersion: "v1.21.1",
1102+
newVersion: "v1.22.16", // last patch release using old registry
1103+
expectErr: true,
1104+
},
1105+
{
1106+
name: "pass when new version is using new registry (>= v1.22.17)",
1107+
oldVersion: "v1.21.1",
1108+
newVersion: "v1.22.17", // first patch release using new registry
1109+
expectErr: false,
1110+
},
1111+
{
1112+
name: "error when new version is using old registry (v1.23.0 <= version <= v1.23.14)",
1113+
oldVersion: "v1.22.17",
1114+
newVersion: "v1.23.14", // last patch release using old registry
1115+
expectErr: true,
1116+
},
1117+
{
1118+
name: "pass when new version is using new registry (>= v1.23.15)",
1119+
oldVersion: "v1.22.17",
1120+
newVersion: "v1.23.15", // first patch release using new registry
1121+
expectErr: false,
1122+
},
1123+
{
1124+
name: "error when new version is using old registry (v1.24.0 <= version <= v1.24.8)",
1125+
oldVersion: "v1.23.1",
1126+
newVersion: "v1.24.8", // last patch release using old registry
1127+
expectErr: true,
1128+
},
1129+
{
1130+
name: "pass when new version is using new registry (>= v1.24.9)",
1131+
oldVersion: "v1.23.1",
1132+
newVersion: "v1.24.9", // first patch release using new registry
1133+
expectErr: false,
1134+
},
1135+
{
1136+
name: "pass when new version is using new registry (>= v1.25.0)",
1137+
oldVersion: "v1.24.8",
1138+
newVersion: "v1.25.0", // uses new registry
1139+
expectErr: false,
1140+
},
1141+
}
1142+
1143+
for _, tt := range tests {
1144+
t.Run(tt.name, func(t *testing.T) {
1145+
g := NewWithT(t)
1146+
1147+
kcp := KubeadmControlPlane{
1148+
Spec: KubeadmControlPlaneSpec{
1149+
KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
1150+
ClusterConfiguration: tt.clusterConfiguration,
1151+
},
1152+
Version: tt.newVersion,
1153+
},
1154+
}
1155+
1156+
allErrs := kcp.validateVersion(tt.oldVersion)
1157+
if tt.expectErr {
1158+
g.Expect(allErrs).ToNot(HaveLen(0))
1159+
} else {
1160+
g.Expect(allErrs).To(HaveLen(0))
1161+
}
1162+
})
1163+
}
1164+
}
10541165
func TestKubeadmControlPlaneValidateUpdateAfterDefaulting(t *testing.T) {
10551166
before := &KubeadmControlPlane{
10561167
ObjectMeta: metav1.ObjectMeta{

0 commit comments

Comments
 (0)