Merge pull request #89588 from rosti/kubeadm-etcd-upgrade

kubeadm: Use image tag as version of stacked etcd
This commit is contained in:
Kubernetes Prow Robot 2020-04-09 18:08:03 -07:00 committed by GitHub
commit 2da163bcf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 273 additions and 211 deletions

View File

@ -29,7 +29,6 @@ go_library(
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/dryrun:go_default_library",
"//cmd/kubeadm/app/util/etcd:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",

View File

@ -32,7 +32,6 @@ import (
outputapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
)
type planFlags struct {
@ -73,28 +72,13 @@ func runPlan(flags *planFlags, userVersion string) error {
return err
}
var etcdClient etcdutil.ClusterInterrogator
// Currently this is the only method we have for distinguishing
// external etcd vs static pod etcd
isExternalEtcd := cfg.Etcd.External != nil
if isExternalEtcd {
etcdClient, err = etcdutil.New(
cfg.Etcd.External.Endpoints,
cfg.Etcd.External.CAFile,
cfg.Etcd.External.CertFile,
cfg.Etcd.External.KeyFile)
} else {
// Connects to local/stacked etcd existing in the cluster
etcdClient, err = etcdutil.NewFromCluster(client, cfg.CertificatesDir)
}
if err != nil {
return err
}
// Compute which upgrade possibilities there are
klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities")
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, etcdClient, cfg.DNS.Type, client)
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, isExternalEtcd, cfg.DNS.Type, client, constants.GetStaticPodDirectory())
if err != nil {
return errors.Wrap(err, "[upgrade/versions] FATAL")
}
@ -161,10 +145,6 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha
components := []outputapiv1alpha1.ComponentUpgradePlan{}
if isExternalEtcd && up.CanUpgradeEtcd() {
components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion))
}
if up.CanUpgradeKubelets() {
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted
// in order to always get the right order. Then the map value is extracted separately
@ -204,11 +184,8 @@ func printUpgradePlan(up *upgrade.Upgrade, plan *outputapiv1alpha1.UpgradePlan,
printManualUpgradeHeader := true
for _, component := range plan.Components {
if isExternalEtcd && component.Name == constants.Etcd {
fmt.Fprintln(w, "External components that should be upgraded manually before you upgrade the control plane with 'kubeadm upgrade apply':")
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
// end of external components table
endOfTable()
// Don't print etcd if it's external
continue
} else if component.Name == constants.Kubelet {
if printManualUpgradeHeader {
fmt.Fprintln(w, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':")

View File

@ -429,11 +429,7 @@ _____________________________________________________________________
},
},
externalEtcd: true,
expectedBytes: []byte(`External components that should be upgraded manually before you upgrade the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
etcd 3.0.17 3.1.12
Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
kubelet 1 x v1.9.2 v1.9.3

View File

@ -34,6 +34,7 @@ go_library(
"//cmd/kubeadm/app/util/apiclient:go_default_library",
"//cmd/kubeadm/app/util/dryrun:go_default_library",
"//cmd/kubeadm/app/util/etcd:go_default_library",
"//cmd/kubeadm/app/util/image:go_default_library",
"//cmd/kubeadm/app/util/staticpod:go_default_library",
"//staging/src/k8s.io/api/apps/v1:go_default_library",
"//staging/src/k8s.io/api/batch/v1:go_default_library",

View File

@ -26,7 +26,6 @@ import (
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"
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
)
// Upgrade defines an upgrade possibility to upgrade from a current version to a new one
@ -75,7 +74,7 @@ type ClusterState struct {
// GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which
// kinds of upgrades can be performed
func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed bool, etcdClient etcdutil.ClusterInterrogator, dnsType kubeadmapi.DNSAddOnType, client clientset.Interface) ([]Upgrade, error) {
func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed, externalEtcd bool, dnsType kubeadmapi.DNSAddOnType, client clientset.Interface, manifestsDir string) ([]Upgrade, error) {
fmt.Println("[upgrade] Fetching available versions to upgrade to")
// Collect the upgrades kubeadm can do in this list
@ -111,10 +110,13 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
return upgrades, err
}
// Get current etcd version
etcdVersion, err := etcdClient.GetVersion()
if err != nil {
return upgrades, err
// Get current stacked etcd version on the local node
var etcdVersion string
if !externalEtcd {
etcdVersion, err = GetEtcdImageTagFromStaticPod(manifestsDir)
if err != nil {
return upgrades, err
}
}
currentDNSType, dnsVersion, err := dns.DeployedDNSAddon(client)
@ -174,7 +176,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
DNSType: dnsType,
DNSVersion: kubeadmconstants.GetDNSVersion(dnsType),
KubeadmVersion: newKubeadmVer,
EtcdVersion: getSuggestedEtcdVersion(patchVersionStr),
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, patchVersionStr),
// KubeletVersions is unset here as it is not used anywhere in .After
},
})
@ -191,7 +193,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
DNSType: dnsType,
DNSVersion: kubeadmconstants.GetDNSVersion(dnsType),
KubeadmVersion: stableVersionStr,
EtcdVersion: getSuggestedEtcdVersion(stableVersionStr),
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, stableVersionStr),
// KubeletVersions is unset here as it is not used anywhere in .After
},
})
@ -239,7 +241,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
DNSType: dnsType,
DNSVersion: kubeadmconstants.GetDNSVersion(dnsType),
KubeadmVersion: previousBranchLatestVersionStr,
EtcdVersion: getSuggestedEtcdVersion(previousBranchLatestVersionStr),
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, previousBranchLatestVersionStr),
// KubeletVersions is unset here as it is not used anywhere in .After
},
})
@ -266,7 +268,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
DNSType: dnsType,
DNSVersion: unstableKubeDNSVersion,
KubeadmVersion: unstableKubeVersion,
EtcdVersion: getSuggestedEtcdVersion(unstableKubeVersion),
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, unstableKubeVersion),
// KubeletVersions is unset here as it is not used anywhere in .After
},
})
@ -300,7 +302,10 @@ func minorUpgradePossibleWithPatchRelease(stableVersion, patchVersion *versionut
return patchVersion.LessThan(stableVersion)
}
func getSuggestedEtcdVersion(kubernetesVersion string) string {
func getSuggestedEtcdVersion(externalEtcd bool, kubernetesVersion string) string {
if externalEtcd {
return ""
}
etcdVersion, warning, err := kubeadmconstants.EtcdSupportedVersion(kubeadmconstants.SupportedEtcdVersion, kubernetesVersion)
if err != nil {
klog.Warningf("[upgrade/versions] could not retrieve an etcd version for the target Kubernetes version: %v", err)

View File

@ -18,12 +18,12 @@ package upgrade
import (
"fmt"
"io/ioutil"
"os"
"reflect"
"strings"
"testing"
"time"
"github.com/pkg/errors"
apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -31,7 +31,6 @@ import (
clientsetfake "k8s.io/client-go/kubernetes/fake"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
)
type fakeVersionGetter struct {
@ -72,54 +71,18 @@ func (f *fakeVersionGetter) KubeletVersions() (map[string]uint16, error) {
}
const fakeCurrentEtcdVersion = "3.1.12"
type fakeEtcdClient struct {
TLS bool
mismatchedVersions bool
}
func (f fakeEtcdClient) WaitForClusterAvailable(retries int, retryInterval time.Duration) (bool, error) {
return true, nil
}
func (f fakeEtcdClient) CheckClusterHealth() error {
return nil
}
func (f fakeEtcdClient) GetVersion() (string, error) {
versions, _ := f.GetClusterVersions()
if f.mismatchedVersions {
return "", errors.Errorf("etcd cluster contains endpoints with mismatched versions: %v", versions)
}
return fakeCurrentEtcdVersion, nil
}
func (f fakeEtcdClient) GetClusterVersions() (map[string]string, error) {
if f.mismatchedVersions {
return map[string]string{
"foo": fakeCurrentEtcdVersion,
"bar": "3.2.0",
}, nil
}
return map[string]string{
"foo": fakeCurrentEtcdVersion,
"bar": fakeCurrentEtcdVersion,
}, nil
}
func (f fakeEtcdClient) Sync() error { return nil }
func (f fakeEtcdClient) AddMember(name string, peerAddrs string) ([]etcdutil.Member, error) {
return []etcdutil.Member{}, nil
}
func (f fakeEtcdClient) GetMemberID(peerURL string) (uint64, error) {
return 0, nil
}
func (f fakeEtcdClient) RemoveMember(id uint64) ([]etcdutil.Member, error) {
return []etcdutil.Member{}, nil
}
const etcdStaticPod = `apiVersion: v1
kind: Pod
metadata:
labels:
component: etcd
tier: control-plane
name: etcd
namespace: kube-system
spec:
containers:
- name: etcd
image: k8s.gcr.io/etcd:` + fakeCurrentEtcdVersion
func getEtcdVersion(v *versionutil.Version) string {
return constants.SupportedEtcdVersion[uint8(v.Minor())]
@ -154,14 +117,13 @@ func TestGetAvailableUpgrades(t *testing.T) {
v1Z0rc1 := v1Z0.WithPreRelease("rc.1")
v1Z1 := v1Z0.WithPatch(1)
etcdClient := fakeEtcdClient{}
tests := []struct {
name string
vg VersionGetter
expectedUpgrades []Upgrade
allowExperimental, allowRCs bool
errExpected bool
etcdClient etcdutil.ClusterInterrogator
externalEtcd bool
beforeDNSType kubeadmapi.DNSAddOnType
beforeDNSVersion string
dnsType kubeadmapi.DNSAddOnType
@ -182,7 +144,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
expectedUpgrades: []Upgrade{},
allowExperimental: false,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "simple patch version upgrade",
@ -221,7 +182,45 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: false,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "simple patch version upgrade with external etcd",
vg: &fakeVersionGetter{
clusterVersion: v1Y1.String(),
kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane
kubeadmVersion: v1Y2.String(),
stablePatchVersion: v1Y3.String(),
stableVersion: v1Y3.String(),
},
beforeDNSType: kubeadmapi.CoreDNS,
beforeDNSVersion: fakeCurrentCoreDNSVersion,
dnsType: kubeadmapi.CoreDNS,
externalEtcd: true,
expectedUpgrades: []Upgrade{
{
Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
Before: ClusterState{
KubeVersion: v1Y1.String(),
KubeletVersions: map[string]uint16{
v1Y1.String(): 1,
},
KubeadmVersion: v1Y2.String(),
DNSType: kubeadmapi.CoreDNS,
DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: "",
},
After: ClusterState{
KubeVersion: v1Y3.String(),
KubeadmVersion: v1Y3.String(),
DNSType: kubeadmapi.CoreDNS,
DNSVersion: constants.CoreDNSVersion,
EtcdVersion: "",
},
},
},
allowExperimental: false,
errExpected: false,
},
{
name: "no version provided to offline version getter does not change behavior",
@ -260,7 +259,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: false,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "minor version upgrade only",
@ -299,7 +297,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: false,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "both minor version upgrade and patch version upgrade available",
@ -358,7 +355,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: false,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "allow experimental upgrades, but no upgrade available",
@ -377,7 +373,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
expectedUpgrades: []Upgrade{},
allowExperimental: true,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "upgrade to an unstable version should be supported",
@ -417,7 +412,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: true,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "upgrade from an unstable version to an unstable version should be supported",
@ -457,7 +451,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: true,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "v1.X.0-alpha.0 should be ignored",
@ -498,7 +491,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: true,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "upgrade to an RC version should be supported",
@ -539,7 +531,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowRCs: true,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "it is possible (but very uncommon) that the latest version from the previous branch is an rc and the current latest version is alpha.0. In that case, show the RC",
@ -580,7 +571,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
allowExperimental: true,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "upgrade to an RC version should be supported. There may also be an even newer unstable version.",
@ -642,22 +632,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
allowRCs: true,
allowExperimental: true,
errExpected: false,
etcdClient: etcdClient,
},
{
name: "Upgrades with external etcd with mismatched versions should not be allowed.",
vg: &fakeVersionGetter{
clusterVersion: v1Y3.String(),
kubeletVersion: v1Y3.String(),
kubeadmVersion: v1Y3.String(),
stablePatchVersion: v1Y3.String(),
stableVersion: v1Y3.String(),
},
allowRCs: false,
allowExperimental: false,
etcdClient: fakeEtcdClient{mismatchedVersions: true},
expectedUpgrades: []Upgrade{},
errExpected: true,
},
{
name: "offline version getter",
@ -666,7 +640,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
kubeletVersion: v1Y0.String(),
kubeadmVersion: v1Y1.String(),
}, v1Z1.String()),
etcdClient: etcdClient,
beforeDNSType: kubeadmapi.CoreDNS,
beforeDNSVersion: fakeCurrentCoreDNSVersion,
dnsType: kubeadmapi.CoreDNS,
@ -703,7 +676,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
stablePatchVersion: v1Z0.String(),
stableVersion: v1Z0.String(),
},
etcdClient: etcdClient,
beforeDNSType: kubeadmapi.KubeDNS,
beforeDNSVersion: fakeCurrentKubeDNSVersion,
dnsType: kubeadmapi.CoreDNS,
@ -740,7 +712,6 @@ func TestGetAvailableUpgrades(t *testing.T) {
stablePatchVersion: v1Z0.String(),
stableVersion: v1Z0.String(),
},
etcdClient: etcdClient,
beforeDNSType: kubeadmapi.KubeDNS,
beforeDNSVersion: fakeCurrentKubeDNSVersion,
dnsType: kubeadmapi.KubeDNS,
@ -804,13 +775,24 @@ func TestGetAvailableUpgrades(t *testing.T) {
},
})
actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, rt.etcdClient, rt.dnsType, client)
manifestsDir, err := ioutil.TempDir("", "GetAvailableUpgrades-test-manifests")
if err != nil {
t.Fatalf("Unable to create temporary directory: %v", err)
}
defer os.RemoveAll(manifestsDir)
if err = ioutil.WriteFile(constants.GetStaticPodFilepath(constants.Etcd, manifestsDir), []byte(etcdStaticPod), 0644); err != nil {
t.Fatalf("Unable to create test static pod manifest: %v", err)
}
actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, rt.externalEtcd, rt.dnsType, client, manifestsDir)
if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) {
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades)
}
if (actualErr != nil) != rt.errExpected {
fmt.Printf("Hello error")
t.Errorf("failed TestGetAvailableUpgrades\n\texpected error: %t\n\tgot error: %t", rt.errExpected, (actualErr != nil))
if rt.errExpected && actualErr == nil {
t.Error("unexpected success")
} else if !rt.errExpected && actualErr != nil {
t.Errorf("unexpected failure: %v", actualErr)
}
if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) {
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades)

View File

@ -20,7 +20,6 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
@ -39,6 +38,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
"k8s.io/kubernetes/cmd/kubeadm/app/util/image"
"k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
)
@ -282,37 +282,38 @@ func performEtcdStaticPodUpgrade(certsRenewMgr *renewal.Manager, client clientse
return true, errors.Wrap(err, "failed to back up etcd data")
}
// Need to check currently used version and version from constants, if differs then upgrade
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)
// Get the desired etcd version. That's either the one specified by the user in cfg.Etcd.Local.ImageTag
// or the kubeadm preferred one for the desired Kubernetes version
var desiredEtcdVersion *version.Version
if cfg.Etcd.Local.ImageTag != "" {
desiredEtcdVersion, err = version.ParseSemantic(cfg.Etcd.Local.ImageTag)
if err != nil {
return true, errors.Wrapf(err, "failed to parse tag %q as a semantic version", cfg.Etcd.Local.ImageTag)
}
} else {
// Need to check currently used version and version from constants, if differs then upgrade
var warning error
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()
// Get the etcd version of the local/stacked etcd member running on the current machine
currentEtcdVersionStr, err := GetEtcdImageTagFromStaticPod(pathMgr.RealManifestDir())
if err != nil {
return true, errors.Wrap(err, "failed to retrieve the current etcd version")
}
currentEtcdVersionStr, ok := currentEtcdVersions[etcdutil.GetClientURL(&cfg.LocalAPIEndpoint)]
if !ok {
return true, errors.Wrap(err, "failed to retrieve the current etcd version")
}
currentEtcdVersion, err := version.ParseSemantic(currentEtcdVersionStr)
cmpResult, err := desiredEtcdVersion.Compare(currentEtcdVersionStr)
if err != nil {
return true, errors.Wrapf(err, "failed to parse the current etcd version(%s)", currentEtcdVersionStr)
return true, errors.Wrapf(err, "failed comparing the current etcd version %q to the desired one %q", currentEtcdVersionStr, desiredEtcdVersion)
}
// Comparing current etcd version with desired to catch the same version or downgrade condition and fail on them.
if desiredEtcdVersion.LessThan(currentEtcdVersion) {
return false, errors.Errorf("the desired etcd version for this Kubernetes version %q is %q, but the current etcd version is %q. Won't downgrade etcd, instead just continue", cfg.KubernetesVersion, desiredEtcdVersion.String(), currentEtcdVersion.String())
}
// For the case when desired etcd version is the same as current etcd version
if strings.Compare(desiredEtcdVersion.String(), currentEtcdVersion.String()) == 0 {
return false, nil
if cmpResult < 1 {
return false, errors.Errorf("the desired etcd version %q is not newer than the currently installed %q. Skipping etcd upgrade", desiredEtcdVersion, currentEtcdVersionStr)
}
beforeEtcdPodHash, err := waiter.WaitForStaticPodSingleHash(cfg.NodeRegistration.Name, constants.Etcd)
@ -633,3 +634,14 @@ func DryRunStaticPodUpgrade(kustomizeDir string, internalcfg *kubeadmapi.InitCon
return dryrunutil.PrintDryRunFiles(files, os.Stdout)
}
// GetEtcdImageTagFromStaticPod returns the image tag of the local etcd static pod
func GetEtcdImageTagFromStaticPod(manifestDir string) (string, error) {
realPath := constants.GetStaticPodFilepath(constants.Etcd, manifestDir)
pod, err := staticpod.ReadStaticPodFromDisk(realPath)
if err != nil {
return "", err
}
return image.TagFromImage(pod.Spec.Containers[0].Image), nil
}

View File

@ -244,16 +244,6 @@ func (c fakeTLSEtcdClient) CheckClusterHealth() error {
return nil
}
func (c fakeTLSEtcdClient) GetClusterVersions() (map[string]string, error) {
return map[string]string{
"https://1.2.3.4:2379": "3.1.12",
}, nil
}
func (c fakeTLSEtcdClient) GetVersion() (string, error) {
return "3.1.12", nil
}
func (c fakeTLSEtcdClient) Sync() error { return nil }
func (c fakeTLSEtcdClient) AddMember(name string, peerAddrs string) ([]etcdutil.Member, error) {
@ -285,16 +275,6 @@ func (c fakePodManifestEtcdClient) CheckClusterHealth() error {
return err
}
func (c fakePodManifestEtcdClient) GetClusterVersions() (map[string]string, error) {
return map[string]string{
"https://1.2.3.4:2379": "3.1.12",
}, nil
}
func (c fakePodManifestEtcdClient) GetVersion() (string, error) {
return "3.1.12", nil
}
func (c fakePodManifestEtcdClient) Sync() error { return nil }
func (c fakePodManifestEtcdClient) AddMember(name string, peerAddrs string) ([]etcdutil.Member, error) {
@ -989,3 +969,36 @@ func TestGetPathManagerForUpgrade(t *testing.T) {
}
}
func TestGetEtcdImageTagFromStaticPod(t *testing.T) {
const expectedEtcdVersion = "3.1.12"
const etcdStaticPod = `apiVersion: v1
kind: Pod
metadata:
labels:
component: etcd
tier: control-plane
name: etcd
namespace: kube-system
spec:
containers:
- name: etcd
image: k8s.gcr.io/etcd:` + expectedEtcdVersion
manifestsDir, err := ioutil.TempDir("", "GetEtcdImageTagFromStaticPod-test-manifests")
if err != nil {
t.Fatalf("Unable to create temporary directory: %v", err)
}
defer os.RemoveAll(manifestsDir)
if err = ioutil.WriteFile(constants.GetStaticPodFilepath(constants.Etcd, manifestsDir), []byte(etcdStaticPod), 0644); err != nil {
t.Fatalf("Unable to create test static pod manifest: %v", err)
}
got, err := GetEtcdImageTagFromStaticPod(manifestsDir)
if err != nil {
t.Errorf("unexpected error: %v", err)
} else if got != expectedEtcdVersion {
t.Errorf("unexpected result:\n\tgot: %q\n\texpected: %q", got, expectedEtcdVersion)
}
}

View File

@ -83,6 +83,7 @@ filegroup(
"//cmd/kubeadm/app/util/crypto:all-srcs",
"//cmd/kubeadm/app/util/dryrun:all-srcs",
"//cmd/kubeadm/app/util/etcd:all-srcs",
"//cmd/kubeadm/app/util/image:all-srcs",
"//cmd/kubeadm/app/util/initsystem:all-srcs",
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
"//cmd/kubeadm/app/util/kustomize:all-srcs",

View File

@ -53,8 +53,6 @@ var etcdBackoff = wait.Backoff{
// ClusterInterrogator is an interface to get etcd cluster related information
type ClusterInterrogator interface {
CheckClusterHealth() error
GetClusterVersions() (map[string]string, error)
GetVersion() (string, error)
WaitForClusterAvailable(retries int, retryInterval time.Duration) (bool, error)
Sync() error
AddMember(name string, peerAddrs string) ([]Member, error)
@ -409,41 +407,6 @@ func (c *Client) AddMember(name string, peerAddrs string) ([]Member, error) {
return ret, nil
}
// GetVersion returns the etcd version of the cluster.
// An error is returned if the version of all endpoints do not match
func (c *Client) GetVersion() (string, error) {
var clusterVersion string
versions, err := c.GetClusterVersions()
if err != nil {
return "", err
}
for _, v := range versions {
if clusterVersion != "" && clusterVersion != v {
return "", errors.Errorf("etcd cluster contains endpoints with mismatched versions: %v", versions)
}
clusterVersion = v
}
if clusterVersion == "" {
return "", errors.New("could not determine cluster etcd version")
}
return clusterVersion, nil
}
// GetClusterVersions returns a map of the endpoints and their associated versions
func (c *Client) GetClusterVersions() (map[string]string, error) {
versions := make(map[string]string)
statuses, err := c.getClusterStatus()
if err != nil {
return versions, err
}
for ep, status := range statuses {
versions[ep] = status.Version
}
return versions, nil
}
// CheckClusterHealth returns nil for status Up or error for status Down
func (c *Client) CheckClusterHealth() error {
_, err := c.getClusterStatus()

View File

@ -0,0 +1,28 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["image.go"],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/image",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["image_test.go"],
embed = [":go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,38 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package image
import "regexp"
var (
// tagMatcher is the regex used to match a tag.
// Basically we presume an image can be made of `[domain][:port][path]<name>[:tag][@sha256:digest]`
// We are obvously interested only in the tag, but for the purpose of properly matching it, we also match the digest
// (if present). All the parts before the tag we match in a single match everything (but not greedy) group.
// All matched sub-groups, except the tag one, get thrown away. Hence, in a result of FindStringSubmatch, if a tag
// matches, it's going to be the second returned element (after the full match).
tagMatcher = regexp.MustCompile(`^(?U:.*)(?::([[:word:]][[:word:].-]*))?(?:@sha256:[a-fA-F0-9]{64})?$`)
)
// TagFromImage extracts a tag from image. An empty string is returned if no tag is discovered.
func TagFromImage(image string) string {
matches := tagMatcher.FindStringSubmatch(image)
if len(matches) >= 2 {
return matches[1]
}
return ""
}

View File

@ -0,0 +1,47 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package image
import "testing"
func TestTagFromImage(t *testing.T) {
tests := map[string]string{
"kindest/node": "",
"kindest/node:latest": "latest",
"kindest/node:v1.17.0": "v1.17.0",
"kindest/node:v1.17.0@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62": "v1.17.0",
"kindest/node@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62": "",
"example.com/kindest/node": "",
"example.com/kindest/node:latest": "latest",
"example.com/kindest/node:v1.17.0": "v1.17.0",
"example.com/kindest/node:v1.17.0@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62": "v1.17.0",
"example.com/kindest/node@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62": "",
"example.com:3000/kindest/node": "",
"example.com:3000/kindest/node:latest": "latest",
"example.com:3000/kindest/node:v1.17.0": "v1.17.0",
"example.com:3000/kindest/node:v1.17.0@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62": "v1.17.0",
"example.com:3000/kindest/node@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62": "",
}
for in, expected := range tests {
out := TagFromImage(in)
if out != expected {
t.Errorf("TagFromImage(%q) = %q, expected %q instead", in, out, expected)
}
}
}