diff --git a/cmd/kubeadm/app/constants/BUILD b/cmd/kubeadm/app/constants/BUILD index 4d1d89f4d09..c75c10130f7 100644 --- a/cmd/kubeadm/app/constants/BUILD +++ b/cmd/kubeadm/app/constants/BUILD @@ -45,6 +45,5 @@ go_test( deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", ], ) diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index ff57b8cd40f..fa2b8e0574f 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -27,6 +27,7 @@ import ( "time" "github.com/pkg/errors" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/wait" @@ -456,21 +457,45 @@ var ( ) // EtcdSupportedVersion returns officially supported version of etcd for a specific Kubernetes release -// if passed version is not listed, the function returns nil and an error -func EtcdSupportedVersion(versionString string) (*version.Version, error) { +// If passed version is not in the given list, the function returns the nearest version with a warning +func EtcdSupportedVersion(supportedEtcdVersion map[uint8]string, versionString string) (etcdVersion *version.Version, warning, err error) { kubernetesVersion, err := version.ParseSemantic(versionString) if err != nil { - return nil, err + return nil, nil, err + } + desiredVersion, etcdStringVersion := uint8(kubernetesVersion.Minor()), "" + + min, max := ^uint8(0), uint8(0) + for k, v := range supportedEtcdVersion { + if desiredVersion == k { + etcdStringVersion = v + break + } + if k < min { + min = k + } + if k > max { + max = k + } } - if etcdStringVersion, ok := SupportedEtcdVersion[uint8(kubernetesVersion.Minor())]; ok { - etcdVersion, err := version.ParseSemantic(etcdStringVersion) - if err != nil { - return nil, err + if len(etcdStringVersion) == 0 { + if desiredVersion < min { + etcdStringVersion = supportedEtcdVersion[min] } - return etcdVersion, nil + if desiredVersion > max { + etcdStringVersion = supportedEtcdVersion[max] + } + warning = fmt.Errorf("could not find officially supported version of etcd for Kubernetes %s, falling back to the nearest etcd version (%s)", + versionString, etcdStringVersion) } - return nil, errors.Errorf("unsupported or unknown Kubernetes version(%v)", kubernetesVersion) + + etcdVersion, err = version.ParseSemantic(etcdStringVersion) + if err != nil { + return nil, nil, err + } + + return etcdVersion, warning, nil } // GetStaticPodDirectory returns the location on the disk where the Static Pod should be present diff --git a/cmd/kubeadm/app/constants/constants_test.go b/cmd/kubeadm/app/constants/constants_test.go index 607a1a83974..83bc8814d63 100644 --- a/cmd/kubeadm/app/constants/constants_test.go +++ b/cmd/kubeadm/app/constants/constants_test.go @@ -18,11 +18,8 @@ package constants import ( "path/filepath" - "strings" "testing" - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/util/version" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" ) @@ -149,50 +146,62 @@ func TestAddSelfHostedPrefix(t *testing.T) { } func TestEtcdSupportedVersion(t *testing.T) { + var supportedEtcdVersion = map[uint8]string{ + 13: "3.2.24", + 14: "3.3.10", + 15: "3.3.10", + 16: "3.3.17-0", + 17: "3.4.3-0", + 18: "3.4.3-0", + } var tests = []struct { kubernetesVersion string expectedVersion *version.Version - expectedError error + expectedWarning bool + expectedError bool }{ { - kubernetesVersion: "1.99.0", + kubernetesVersion: "1.x.1", expectedVersion: nil, - expectedError: errors.New("unsupported or unknown Kubernetes version(1.99.0)"), + expectedWarning: false, + expectedError: true, }, { - kubernetesVersion: MinimumControlPlaneVersion.WithPatch(1).String(), - expectedVersion: version.MustParseSemantic(SupportedEtcdVersion[uint8(MinimumControlPlaneVersion.Minor())]), - expectedError: nil, + kubernetesVersion: "1.10.1", + expectedVersion: version.MustParseSemantic("3.2.24"), + expectedWarning: true, + expectedError: false, }, { - kubernetesVersion: CurrentKubernetesVersion.String(), - expectedVersion: version.MustParseSemantic(SupportedEtcdVersion[uint8(CurrentKubernetesVersion.Minor())]), - expectedError: nil, + kubernetesVersion: "1.99.0", + expectedVersion: version.MustParseSemantic("3.4.3-0"), + expectedWarning: true, + expectedError: false, + }, + { + kubernetesVersion: "v1.16.0", + expectedVersion: version.MustParseSemantic("3.3.17-0"), + expectedWarning: false, + expectedError: false, + }, + { + kubernetesVersion: "1.17.2", + expectedVersion: version.MustParseSemantic("3.4.3-0"), + expectedWarning: false, + expectedError: false, }, } for _, rt := range tests { t.Run(rt.kubernetesVersion, func(t *testing.T) { - actualVersion, actualError := EtcdSupportedVersion(rt.kubernetesVersion) - if actualError != nil { - if rt.expectedError == nil { - t.Errorf("failed EtcdSupportedVersion:\n\texpected no error, but got: %v", actualError) - } else if actualError.Error() != rt.expectedError.Error() { - t.Errorf( - "failed EtcdSupportedVersion:\n\texpected error: %v\n\t actual error: %v", - rt.expectedError, - actualError, - ) - } - } else { - if rt.expectedError != nil { - t.Errorf("failed EtcdSupportedVersion:\n\texpected error: %v, but got no error", rt.expectedError) - } else if strings.Compare(actualVersion.String(), rt.expectedVersion.String()) != 0 { - t.Errorf( - "failed EtcdSupportedVersion:\n\texpected version: %s\n\t actual version: %s", - rt.expectedVersion.String(), - actualVersion.String(), - ) - } + actualVersion, actualWarning, actualError := EtcdSupportedVersion(supportedEtcdVersion, rt.kubernetesVersion) + if (actualError != nil) != rt.expectedError { + t.Fatalf("expected error %v, got %v", rt.expectedError, actualError != nil) + } + if (actualWarning != nil) != rt.expectedWarning { + t.Fatalf("expected warning %v, got %v", rt.expectedWarning, actualWarning != nil) + } + if actualError == nil && actualVersion.String() != rt.expectedVersion.String() { + t.Errorf("expected version %s, got %s", rt.expectedVersion.String(), actualVersion.String()) } }) } diff --git a/cmd/kubeadm/app/images/images.go b/cmd/kubeadm/app/images/images.go index 61cca5f34ad..c5b68d67f40 100644 --- a/cmd/kubeadm/app/images/images.go +++ b/cmd/kubeadm/app/images/images.go @@ -73,10 +73,13 @@ func GetEtcdImage(cfg *kubeadmapi.ClusterConfiguration) string { } // Etcd uses an imageTag that corresponds to the etcd version matching the Kubernetes version etcdImageTag := constants.DefaultEtcdVersion - etcdVersion, err := constants.EtcdSupportedVersion(cfg.KubernetesVersion) + etcdVersion, warning, err := constants.EtcdSupportedVersion(constants.SupportedEtcdVersion, cfg.KubernetesVersion) if err == nil { etcdImageTag = etcdVersion.String() } + if warning != nil { + klog.Warningln(warning) + } // unless an override is specified if cfg.Etcd.Local != nil && cfg.Etcd.Local.ImageTag != "" { etcdImageTag = cfg.Etcd.Local.ImageTag diff --git a/cmd/kubeadm/app/phases/upgrade/compute.go b/cmd/kubeadm/app/phases/upgrade/compute.go index 7be74bc1684..440f330a1ea 100644 --- a/cmd/kubeadm/app/phases/upgrade/compute.go +++ b/cmd/kubeadm/app/phases/upgrade/compute.go @@ -22,6 +22,7 @@ import ( versionutil "k8s.io/apimachinery/pkg/util/version" clientset "k8s.io/client-go/kubernetes" + "k8s.io/klog" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" @@ -300,10 +301,13 @@ func minorUpgradePossibleWithPatchRelease(stableVersion, patchVersion *versionut } func getSuggestedEtcdVersion(kubernetesVersion string) string { - etcdVersion, err := kubeadmconstants.EtcdSupportedVersion(kubernetesVersion) + etcdVersion, warning, err := kubeadmconstants.EtcdSupportedVersion(kubeadmconstants.SupportedEtcdVersion, kubernetesVersion) if err != nil { - fmt.Printf("[upgrade/versions] WARNING: No recommended etcd for requested Kubernetes version (%s)\n", kubernetesVersion) + klog.Warningf("[upgrade/versions] could not retrieve an etcd version for the target Kubernetes version: %v", err) return "N/A" } + if warning != nil { + klog.Warningf("[upgrade/versions] %v", warning) + } return etcdVersion.String() } diff --git a/cmd/kubeadm/app/phases/upgrade/staticpods.go b/cmd/kubeadm/app/phases/upgrade/staticpods.go index ef6a07e9732..b90aaf105f9 100644 --- a/cmd/kubeadm/app/phases/upgrade/staticpods.go +++ b/cmd/kubeadm/app/phases/upgrade/staticpods.go @@ -24,9 +24,11 @@ import ( "time" "github.com/pkg/errors" + utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/version" clientset "k8s.io/client-go/kubernetes" + "k8s.io/klog" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/constants" certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" @@ -281,10 +283,13 @@ func performEtcdStaticPodUpgrade(certsRenewMgr *renewal.Manager, client clientse } // Need to check currently used version and version from constants, if differs then upgrade - desiredEtcdVersion, err := constants.EtcdSupportedVersion(cfg.KubernetesVersion) + desiredEtcdVersion, warning, err := constants.EtcdSupportedVersion(constants.SupportedEtcdVersion, cfg.KubernetesVersion) if err != nil { return true, errors.Wrap(err, "failed to retrieve an etcd version for the target Kubernetes version") } + if warning != nil { + klog.Warningf("[upgrade/etcd] %v", warning) + } // gets the etcd version of the local/stacked etcd member running on the current machine currentEtcdVersions, err := oldEtcdClient.GetClusterVersions()