print nodename

This commit is contained in:
carlory 2024-02-29 16:49:25 +08:00
parent 210378e918
commit 22adf221a1
12 changed files with 709 additions and 415 deletions

View File

@ -49,6 +49,7 @@ type ComponentUpgradePlan struct {
Name string Name string
CurrentVersion string CurrentVersion string
NewVersion string NewVersion string
NodeName string
} }
// ComponentConfigVersionState describes the current and desired version of a component config // ComponentConfigVersionState describes the current and desired version of a component config

View File

@ -26,3 +26,8 @@ import (
func Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { func Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error {
return autoConvert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in, out, s) return autoConvert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in, out, s)
} }
// Convert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan converts a private ComponentUpgradePlan to public ComponentUpgradePlan.
func Convert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(in *output.ComponentUpgradePlan, out *ComponentUpgradePlan, s conversion.Scope) error {
return autoConvert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(in, out, s)
}

View File

@ -61,11 +61,6 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err return err
} }
if err := s.AddGeneratedConversionFunc((*output.ComponentUpgradePlan)(nil), (*ComponentUpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(a.(*output.ComponentUpgradePlan), b.(*ComponentUpgradePlan), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*Images)(nil), (*output.Images)(nil), func(a, b interface{}, scope conversion.Scope) error { if err := s.AddGeneratedConversionFunc((*Images)(nil), (*output.Images)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha2_Images_To_output_Images(a.(*Images), b.(*output.Images), scope) return Convert_v1alpha2_Images_To_output_Images(a.(*Images), b.(*output.Images), scope)
}); err != nil { }); err != nil {
@ -81,6 +76,11 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err return err
} }
if err := s.AddConversionFunc((*output.ComponentUpgradePlan)(nil), (*ComponentUpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(a.(*output.ComponentUpgradePlan), b.(*ComponentUpgradePlan), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*output.UpgradePlan)(nil), (*UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { if err := s.AddConversionFunc((*output.UpgradePlan)(nil), (*UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan(a.(*output.UpgradePlan), b.(*UpgradePlan), scope) return Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan(a.(*output.UpgradePlan), b.(*UpgradePlan), scope)
}); err != nil { }); err != nil {
@ -151,14 +151,10 @@ func autoConvert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(in
out.Name = in.Name out.Name = in.Name
out.CurrentVersion = in.CurrentVersion out.CurrentVersion = in.CurrentVersion
out.NewVersion = in.NewVersion out.NewVersion = in.NewVersion
// WARNING: in.NodeName requires manual conversion: does not exist in peer-type
return nil return nil
} }
// Convert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan is an autogenerated conversion function.
func Convert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(in *output.ComponentUpgradePlan, out *ComponentUpgradePlan, s conversion.Scope) error {
return autoConvert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(in, out, s)
}
func autoConvert_v1alpha2_Images_To_output_Images(in *Images, out *output.Images, s conversion.Scope) error { func autoConvert_v1alpha2_Images_To_output_Images(in *Images, out *output.Images, s conversion.Scope) error {
out.Images = *(*[]string)(unsafe.Pointer(&in.Images)) out.Images = *(*[]string)(unsafe.Pointer(&in.Images))
return nil return nil
@ -180,7 +176,17 @@ func Convert_output_Images_To_v1alpha2_Images(in *output.Images, out *Images, s
} }
func autoConvert_v1alpha2_UpgradePlan_To_output_UpgradePlan(in *UpgradePlan, out *output.UpgradePlan, s conversion.Scope) error { func autoConvert_v1alpha2_UpgradePlan_To_output_UpgradePlan(in *UpgradePlan, out *output.UpgradePlan, s conversion.Scope) error {
out.Components = *(*[]output.ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) if in.Components != nil {
in, out := &in.Components, &out.Components
*out = make([]output.ComponentUpgradePlan, len(*in))
for i := range *in {
if err := Convert_v1alpha2_ComponentUpgradePlan_To_output_ComponentUpgradePlan(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Components = nil
}
out.ConfigVersions = *(*[]output.ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions)) out.ConfigVersions = *(*[]output.ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions))
return nil return nil
} }
@ -193,6 +199,16 @@ func Convert_v1alpha2_UpgradePlan_To_output_UpgradePlan(in *UpgradePlan, out *ou
func autoConvert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { func autoConvert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error {
// WARNING: in.AvailableUpgrades requires manual conversion: does not exist in peer-type // WARNING: in.AvailableUpgrades requires manual conversion: does not exist in peer-type
out.ConfigVersions = *(*[]ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions)) out.ConfigVersions = *(*[]ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions))
out.Components = *(*[]ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) if in.Components != nil {
in, out := &in.Components, &out.Components
*out = make([]ComponentUpgradePlan, len(*in))
for i := range *in {
if err := Convert_output_ComponentUpgradePlan_To_v1alpha2_ComponentUpgradePlan(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Components = nil
}
return nil return nil
} }

View File

@ -49,6 +49,7 @@ type ComponentUpgradePlan struct {
Name string `json:"name"` Name string `json:"name"`
CurrentVersion string `json:"currentVersion"` CurrentVersion string `json:"currentVersion"`
NewVersion string `json:"newVersion"` NewVersion string `json:"newVersion"`
NodeName string `json:"nodeName,omitempty"`
} }
// ComponentConfigVersionState describes the current and desired version of a component config // ComponentConfigVersionState describes the current and desired version of a component config

View File

@ -243,6 +243,7 @@ func autoConvert_v1alpha3_ComponentUpgradePlan_To_output_ComponentUpgradePlan(in
out.Name = in.Name out.Name = in.Name
out.CurrentVersion = in.CurrentVersion out.CurrentVersion = in.CurrentVersion
out.NewVersion = in.NewVersion out.NewVersion = in.NewVersion
out.NodeName = in.NodeName
return nil return nil
} }
@ -255,6 +256,7 @@ func autoConvert_output_ComponentUpgradePlan_To_v1alpha3_ComponentUpgradePlan(in
out.Name = in.Name out.Name = in.Name
out.CurrentVersion = in.CurrentVersion out.CurrentVersion = in.CurrentVersion
out.NewVersion = in.NewVersion out.NewVersion = in.NewVersion
out.NodeName = in.NodeName
return nil return nil
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
package upgrade package upgrade
import ( import (
"fmt"
"io" "io"
"os" "os"
"sort" "sort"
@ -88,11 +87,12 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command {
} }
// newComponentUpgradePlan helper creates outputapiv1alpha3.ComponentUpgradePlan object // newComponentUpgradePlan helper creates outputapiv1alpha3.ComponentUpgradePlan object
func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha3.ComponentUpgradePlan { func newComponentUpgradePlan(name, currentVersion, newVersion, nodeName string) outputapiv1alpha3.ComponentUpgradePlan {
return outputapiv1alpha3.ComponentUpgradePlan{ return outputapiv1alpha3.ComponentUpgradePlan{
Name: name, Name: name,
CurrentVersion: currentVersion, CurrentVersion: currentVersion,
NewVersion: newVersion, NewVersion: newVersion,
NodeName: nodeName,
} }
} }
@ -106,10 +106,6 @@ func runPlan(flagSet *pflag.FlagSet, flags *planFlags, args []string, printer ou
return err return err
} }
// Currently this is the only method we have for distinguishing
// external etcd vs static pod etcd
isExternalEtcd := initCfg.Etcd.External != nil
// Compute which upgrade possibilities there are // Compute which upgrade possibilities there are
klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities") klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities")
@ -124,7 +120,7 @@ func runPlan(flagSet *pflag.FlagSet, flags *planFlags, args []string, printer ou
return cmdutil.TypeMismatchErr("allowExperimentalUpgrades", "bool") return cmdutil.TypeMismatchErr("allowExperimentalUpgrades", "bool")
} }
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, *allowExperimentalUpgrades, *allowRCUpgrades, isExternalEtcd, client, constants.GetStaticPodDirectory(), printer) availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, *allowExperimentalUpgrades, *allowRCUpgrades, client, printer)
if err != nil { if err != nil {
return errors.Wrap(err, "[upgrade/versions] FATAL") return errors.Wrap(err, "[upgrade/versions] FATAL")
} }
@ -143,16 +139,15 @@ func runPlan(flagSet *pflag.FlagSet, flags *planFlags, args []string, printer ou
} }
// Generate and print the upgrade plan // Generate and print the upgrade plan
plan := genUpgradePlan(availUpgrades, configVersionStates, isExternalEtcd) plan := genUpgradePlan(availUpgrades, configVersionStates)
return printer.PrintObj(plan, os.Stdout) return printer.PrintObj(plan, os.Stdout)
} }
// genUpgradePlan generates upgrade plan from available upgrades and component config version states // genUpgradePlan generates upgrade plan from available upgrades and component config version states
func genUpgradePlan(availUpgrades []upgrade.Upgrade, configVersions []outputapiv1alpha3.ComponentConfigVersionState, isExternalEtcd bool) *outputapiv1alpha3.UpgradePlan { func genUpgradePlan(availUpgrades []upgrade.Upgrade, configVersions []outputapiv1alpha3.ComponentConfigVersionState) *outputapiv1alpha3.UpgradePlan {
plan := &outputapiv1alpha3.UpgradePlan{ConfigVersions: configVersions} plan := &outputapiv1alpha3.UpgradePlan{ConfigVersions: configVersions}
for _, up := range availUpgrades { for _, up := range availUpgrades {
au := genAvailableUpgrade(&up, isExternalEtcd) plan.AvailableUpgrades = append(plan.AvailableUpgrades, genAvailableUpgrade(&up))
plan.AvailableUpgrades = append(plan.AvailableUpgrades, au)
} }
return plan return plan
} }
@ -164,7 +159,7 @@ func appendDNSComponent(components []outputapiv1alpha3.ComponentUpgradePlan, up
afterVersion := up.After.DNSVersion afterVersion := up.After.DNSVersion
if beforeVersion != "" || afterVersion != "" { if beforeVersion != "" || afterVersion != "" {
components = append(components, newComponentUpgradePlan(name, beforeVersion, afterVersion)) components = append(components, newComponentUpgradePlan(name, beforeVersion, afterVersion, ""))
} }
return components return components
} }
@ -175,41 +170,64 @@ func appendKubeadmComponent(components []outputapiv1alpha3.ComponentUpgradePlan,
afterVersion := up.After.KubeadmVersion afterVersion := up.After.KubeadmVersion
if beforeVersion != "" || afterVersion != "" { if beforeVersion != "" || afterVersion != "" {
components = append(components, newComponentUpgradePlan(name, beforeVersion, afterVersion)) components = append(components, newComponentUpgradePlan(name, beforeVersion, afterVersion, ""))
} }
return components return components
} }
// genAvailableUpgrade generates available upgrade from upgrade object and external etcd boolean // genAvailableUpgrade generates available upgrade from upgrade object.
func genAvailableUpgrade(up *upgrade.Upgrade, isExternalEtcd bool) outputapiv1alpha3.AvailableUpgrade { func genAvailableUpgrade(up *upgrade.Upgrade) outputapiv1alpha3.AvailableUpgrade {
components := []outputapiv1alpha3.ComponentUpgradePlan{} components := []outputapiv1alpha3.ComponentUpgradePlan{}
if up.CanUpgradeKubelets() { if up.CanUpgradeKubelets() {
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted // The map is of the form <old-version>:<node-names>. 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 // in order to always get the right order. Then the map value is extracted separately
for _, oldVersion := range sortedSliceFromStringIntMap(up.Before.KubeletVersions) { for _, oldVersion := range sortedSliceFromStringStringArrayMap(up.Before.KubeletVersions) {
nodeCount := up.Before.KubeletVersions[oldVersion] nodeNames := up.Before.KubeletVersions[oldVersion]
components = append(components, newComponentUpgradePlan(constants.Kubelet, fmt.Sprintf("%d x %s", nodeCount, oldVersion), up.After.KubeVersion)) for _, nodeName := range nodeNames {
components = append(components, newComponentUpgradePlan(constants.Kubelet, oldVersion, up.After.KubeVersion, nodeName))
}
} }
} }
components = append(components, newComponentUpgradePlan(constants.KubeAPIServer, up.Before.KubeVersion, up.After.KubeVersion)) for _, oldVersion := range sortedSliceFromStringStringArrayMap(up.Before.KubeAPIServerVersions) {
components = append(components, newComponentUpgradePlan(constants.KubeControllerManager, up.Before.KubeVersion, up.After.KubeVersion)) nodeNames := up.Before.KubeAPIServerVersions[oldVersion]
components = append(components, newComponentUpgradePlan(constants.KubeScheduler, up.Before.KubeVersion, up.After.KubeVersion)) for _, nodeName := range nodeNames {
components = append(components, newComponentUpgradePlan(constants.KubeProxy, up.Before.KubeVersion, up.After.KubeVersion)) components = append(components, newComponentUpgradePlan(constants.KubeAPIServer, oldVersion, up.After.KubeVersion, nodeName))
}
}
for _, oldVersion := range sortedSliceFromStringStringArrayMap(up.Before.KubeControllerManagerVersions) {
nodeNames := up.Before.KubeControllerManagerVersions[oldVersion]
for _, nodeName := range nodeNames {
components = append(components, newComponentUpgradePlan(constants.KubeControllerManager, oldVersion, up.After.KubeVersion, nodeName))
}
}
for _, oldVersion := range sortedSliceFromStringStringArrayMap(up.Before.KubeSchedulerVersions) {
nodeNames := up.Before.KubeSchedulerVersions[oldVersion]
for _, nodeName := range nodeNames {
components = append(components, newComponentUpgradePlan(constants.KubeScheduler, oldVersion, up.After.KubeVersion, nodeName))
}
}
components = append(components, newComponentUpgradePlan(constants.KubeProxy, up.Before.KubeVersion, up.After.KubeVersion, ""))
components = appendDNSComponent(components, up, constants.CoreDNS) components = appendDNSComponent(components, up, constants.CoreDNS)
components = appendKubeadmComponent(components, up, constants.Kubeadm) components = appendKubeadmComponent(components, up, constants.Kubeadm)
if !isExternalEtcd { // If etcd is not external, we should include it in the upgrade plan
components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion)) for _, oldVersion := range sortedSliceFromStringStringArrayMap(up.Before.EtcdVersions) {
nodeNames := up.Before.EtcdVersions[oldVersion]
for _, nodeName := range nodeNames {
components = append(components, newComponentUpgradePlan(constants.Etcd, oldVersion, up.After.EtcdVersion, nodeName))
}
} }
return outputapiv1alpha3.AvailableUpgrade{Description: up.Description, Components: components} return outputapiv1alpha3.AvailableUpgrade{Description: up.Description, Components: components}
} }
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically // sortedSliceFromStringStringArrayMap returns a slice of the keys in the map sorted alphabetically
func sortedSliceFromStringIntMap(strMap map[string]uint16) []string { func sortedSliceFromStringStringArrayMap(strMap map[string][]string) []string {
strSlice := []string{} strSlice := []string{}
for k := range strMap { for k := range strMap {
strSlice = append(strSlice, k) strSlice = append(strSlice, k)
@ -280,28 +298,24 @@ func (printer *upgradePlanTextPrinter) printAvailableUpgrade(writer io.Writer, a
_, _ = printer.Fprintln(writer, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':") _, _ = printer.Fprintln(writer, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':")
tabw := tabwriter.NewWriter(writer, 10, 4, 3, ' ', 0) tabw := tabwriter.NewWriter(writer, 10, 4, 3, ' ', 0)
_, _ = printer.Fprintln(tabw, strings.Join([]string{"COMPONENT", "CURRENT", "TARGET"}, "\t")) _, _ = printer.Fprintln(tabw, strings.Join([]string{"COMPONENT", "NODE", "CURRENT", "TARGET"}, "\t"))
for i, component := range au.Components { for _, component := range au.Components {
if component.Name != constants.Kubelet { if component.Name != constants.Kubelet {
continue continue
} }
if i == 0 { _, _ = printer.Fprintf(tabw, "%s\t%s\t%s\t%s\n", component.Name, component.NodeName, component.CurrentVersion, component.NewVersion)
_, _ = printer.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
} else {
_, _ = printer.Fprintf(tabw, "%s\t%s\t%s\n", "", component.CurrentVersion, component.NewVersion)
}
} }
_ = tabw.Flush() _ = tabw.Flush()
_, _ = printer.Fprintln(writer, "") _, _ = printer.Fprintln(writer, "")
_, _ = printer.Fprintf(writer, "Upgrade to the latest %s:\n", au.Description) _, _ = printer.Fprintf(writer, "Upgrade to the latest %s:\n", au.Description)
_, _ = printer.Fprintln(writer, "") _, _ = printer.Fprintln(writer, "")
_, _ = printer.Fprintln(tabw, strings.Join([]string{"COMPONENT", "CURRENT", "TARGET"}, "\t")) _, _ = printer.Fprintln(tabw, strings.Join([]string{"COMPONENT", "NODE", "CURRENT", "TARGET"}, "\t"))
for _, component := range au.Components { for _, component := range au.Components {
if component.Name == constants.Kubelet || component.Name == constants.Kubeadm { if component.Name == constants.Kubelet || component.Name == constants.Kubeadm {
continue continue
} }
_, _ = printer.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion) _, _ = printer.Fprintf(tabw, "%s\t%s\t%s\t%s\n", component.Name, component.NodeName, component.CurrentVersion, component.NewVersion)
} }
_ = tabw.Flush() _ = tabw.Flush()

View File

@ -28,39 +28,39 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/output" "k8s.io/kubernetes/cmd/kubeadm/app/util/output"
) )
func TestSortedSliceFromStringIntMap(t *testing.T) { func TestSortedSliceFromStringStringArrayMap(t *testing.T) {
var tests = []struct { var tests = []struct {
name string name string
strMap map[string]uint16 strMap map[string][]string
expectedSlice []string expectedSlice []string
}{ }{
{ {
name: "the returned slice should be alphabetically sorted based on the string keys in the map", name: "the returned slice should be alphabetically sorted based on the string keys in the map",
strMap: map[string]uint16{"foo": 1, "bar": 2}, strMap: map[string][]string{"foo": {"1"}, "bar": {"1", "2"}},
expectedSlice: []string{"bar", "foo"}, expectedSlice: []string{"bar", "foo"},
}, },
{ {
name: "the int value should not affect this func", name: "the int value should not affect this func",
strMap: map[string]uint16{"foo": 2, "bar": 1}, strMap: map[string][]string{"foo": {"1", "2"}, "bar": {"1"}},
expectedSlice: []string{"bar", "foo"}, expectedSlice: []string{"bar", "foo"},
}, },
{ {
name: "slice with 4 keys and different values", name: "slice with 4 keys and different values",
strMap: map[string]uint16{"b": 2, "a": 1, "cb": 0, "ca": 1000}, strMap: map[string][]string{"b": {"1", "2"}, "a": {"1"}, "cb": {}, "ca": {"1", "2", "3"}},
expectedSlice: []string{"a", "b", "ca", "cb"}, expectedSlice: []string{"a", "b", "ca", "cb"},
}, },
{ {
name: "this should work for version numbers as well; and the lowest version should come first", name: "this should work for version numbers as well; and the lowest version should come first",
strMap: map[string]uint16{"v1.7.0": 1, "v1.6.1": 1, "v1.6.2": 1, "v1.8.0": 1, "v1.8.0-alpha.1": 1}, strMap: map[string][]string{"v1.7.0": {"1"}, "v1.6.1": {"1"}, "v1.6.2": {"1"}, "v1.8.0": {"1"}, "v1.8.0-alpha.1": {"1"}},
expectedSlice: []string{"v1.6.1", "v1.6.2", "v1.7.0", "v1.8.0", "v1.8.0-alpha.1"}, expectedSlice: []string{"v1.6.1", "v1.6.2", "v1.7.0", "v1.8.0", "v1.8.0-alpha.1"},
}, },
} }
for _, rt := range tests { for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) { t.Run(rt.name, func(t *testing.T) {
actualSlice := sortedSliceFromStringIntMap(rt.strMap) actualSlice := sortedSliceFromStringStringArrayMap(rt.strMap)
if !reflect.DeepEqual(actualSlice, rt.expectedSlice) { if !reflect.DeepEqual(actualSlice, rt.expectedSlice) {
t.Errorf( t.Errorf(
"failed SortedSliceFromStringIntMap:\n\texpected: %v\n\t actual: %v", "failed sortedSliceFromStringStringArrayMap:\n\texpected: %v\n\t actual: %v",
rt.expectedSlice, rt.expectedSlice,
actualSlice, actualSlice,
) )
@ -101,12 +101,23 @@ func TestPrintUpgradePlan(t *testing.T) {
Description: "version in the v1.18 series", Description: "version in the v1.18 series",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.18.1", KubeVersion: "v1.18.1",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.18.1": 1, "v1.18.1": {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
"v1.18.1": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.18.1": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.18.1": {"node1"},
},
EtcdVersions: map[string][]string{
"3.4.3-0": {"node1"},
}, },
KubeadmVersion: "v1.18.1", KubeadmVersion: "v1.18.1",
DNSVersion: "1.6.7", DNSVersion: "1.6.7",
EtcdVersion: "3.4.3-0",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.18.4", KubeVersion: "v1.18.4",
@ -118,18 +129,18 @@ func TestPrintUpgradePlan(t *testing.T) {
}, },
versionStates: versionStates, versionStates: versionStates,
expectedBytes: []byte(`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 TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.18.1 v1.18.4 kubelet node1 v1.18.1 v1.18.4
Upgrade to the latest version in the v1.18 series: Upgrade to the latest version in the v1.18 series:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.18.1 v1.18.4 kube-apiserver node1 v1.18.1 v1.18.4
kube-controller-manager v1.18.1 v1.18.4 kube-controller-manager node1 v1.18.1 v1.18.4
kube-scheduler v1.18.1 v1.18.4 kube-scheduler node1 v1.18.1 v1.18.4
kube-proxy v1.18.1 v1.18.4 kube-proxy v1.18.1 v1.18.4
CoreDNS 1.6.7 1.6.7 CoreDNS 1.6.7 1.6.7
etcd 3.4.3-0 3.4.3-0 etcd node1 3.4.3-0 3.4.3-0
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -159,12 +170,23 @@ _____________________________________________________________________
Description: "stable version", Description: "stable version",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.18.4", KubeVersion: "v1.18.4",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.18.4": 1, "v1.18.4": {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
"v1.18.4": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.18.4": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.18.4": {"node1"},
},
EtcdVersions: map[string][]string{
"3.4.3-0": {"node1"},
}, },
KubeadmVersion: "v1.18.4", KubeadmVersion: "v1.18.4",
DNSVersion: "1.6.7", DNSVersion: "1.6.7",
EtcdVersion: "3.4.3-0",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.19.0", KubeVersion: "v1.19.0",
@ -176,18 +198,18 @@ _____________________________________________________________________
}, },
versionStates: versionStates, versionStates: versionStates,
expectedBytes: []byte(`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 TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.18.4 v1.19.0 kubelet node1 v1.18.4 v1.19.0
Upgrade to the latest stable version: Upgrade to the latest stable version:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.18.4 v1.19.0 kube-apiserver node1 v1.18.4 v1.19.0
kube-controller-manager v1.18.4 v1.19.0 kube-controller-manager node1 v1.18.4 v1.19.0
kube-scheduler v1.18.4 v1.19.0 kube-scheduler node1 v1.18.4 v1.19.0
kube-proxy v1.18.4 v1.19.0 kube-proxy v1.18.4 v1.19.0
CoreDNS 1.6.7 1.7.0 CoreDNS 1.6.7 1.7.0
etcd 3.4.3-0 3.4.7-0 etcd node1 3.4.3-0 3.4.7-0
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -217,12 +239,23 @@ _____________________________________________________________________
Description: "version in the v1.18 series", Description: "version in the v1.18 series",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.18.3", KubeVersion: "v1.18.3",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.18.3": 1, "v1.18.3": {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
"v1.18.3": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.18.3": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.18.3": {"node1"},
},
EtcdVersions: map[string][]string{
"3.4.3-0": {"node1"},
}, },
KubeadmVersion: "v1.18.3", KubeadmVersion: "v1.18.3",
DNSVersion: "1.6.7", DNSVersion: "1.6.7",
EtcdVersion: "3.4.3-0",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.18.5", KubeVersion: "v1.18.5",
@ -235,12 +268,23 @@ _____________________________________________________________________
Description: "stable version", Description: "stable version",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.18.3", KubeVersion: "v1.18.3",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.18.3": 1, "v1.18.3": {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
"v1.18.3": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.18.3": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.18.3": {"node1"},
},
EtcdVersions: map[string][]string{
"3.4.3-0": {"node1"},
}, },
KubeadmVersion: "v1.18.3", KubeadmVersion: "v1.18.3",
DNSVersion: "1.6.7", DNSVersion: "1.6.7",
EtcdVersion: "3.4.3-0",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.19.0", KubeVersion: "v1.19.0",
@ -252,18 +296,18 @@ _____________________________________________________________________
}, },
versionStates: versionStates, versionStates: versionStates,
expectedBytes: []byte(`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 TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.18.3 v1.18.5 kubelet node1 v1.18.3 v1.18.5
Upgrade to the latest version in the v1.18 series: Upgrade to the latest version in the v1.18 series:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.18.3 v1.18.5 kube-apiserver node1 v1.18.3 v1.18.5
kube-controller-manager v1.18.3 v1.18.5 kube-controller-manager node1 v1.18.3 v1.18.5
kube-scheduler v1.18.3 v1.18.5 kube-scheduler node1 v1.18.3 v1.18.5
kube-proxy v1.18.3 v1.18.5 kube-proxy v1.18.3 v1.18.5
CoreDNS 1.6.7 1.6.7 CoreDNS 1.6.7 1.6.7
etcd 3.4.3-0 3.4.3-0 etcd node1 3.4.3-0 3.4.3-0
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -272,18 +316,18 @@ You can now apply the upgrade by executing the following command:
_____________________________________________________________________ _____________________________________________________________________
Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.18.3 v1.19.0 kubelet node1 v1.18.3 v1.19.0
Upgrade to the latest stable version: Upgrade to the latest stable version:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.18.3 v1.19.0 kube-apiserver node1 v1.18.3 v1.19.0
kube-controller-manager v1.18.3 v1.19.0 kube-controller-manager node1 v1.18.3 v1.19.0
kube-scheduler v1.18.3 v1.19.0 kube-scheduler node1 v1.18.3 v1.19.0
kube-proxy v1.18.3 v1.19.0 kube-proxy v1.18.3 v1.19.0
CoreDNS 1.6.7 1.7.0 CoreDNS 1.6.7 1.7.0
etcd 3.4.3-0 3.4.7-0 etcd node1 3.4.3-0 3.4.7-0
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -313,12 +357,23 @@ _____________________________________________________________________
Description: "experimental version", Description: "experimental version",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.18.5", KubeVersion: "v1.18.5",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.18.5": 1, "v1.18.5": {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
"v1.18.5": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.18.5": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.18.5": {"node1"},
},
EtcdVersions: map[string][]string{
"3.4.3-0": {"node1"},
}, },
KubeadmVersion: "v1.18.5", KubeadmVersion: "v1.18.5",
DNSVersion: "1.6.7", DNSVersion: "1.6.7",
EtcdVersion: "3.4.3-0",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.19.0-beta.1", KubeVersion: "v1.19.0-beta.1",
@ -330,18 +385,18 @@ _____________________________________________________________________
}, },
versionStates: versionStates, versionStates: versionStates,
expectedBytes: []byte(`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 TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.18.5 v1.19.0-beta.1 kubelet node1 v1.18.5 v1.19.0-beta.1
Upgrade to the latest experimental version: Upgrade to the latest experimental version:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.18.5 v1.19.0-beta.1 kube-apiserver node1 v1.18.5 v1.19.0-beta.1
kube-controller-manager v1.18.5 v1.19.0-beta.1 kube-controller-manager node1 v1.18.5 v1.19.0-beta.1
kube-scheduler v1.18.5 v1.19.0-beta.1 kube-scheduler node1 v1.18.5 v1.19.0-beta.1
kube-proxy v1.18.5 v1.19.0-beta.1 kube-proxy v1.18.5 v1.19.0-beta.1
CoreDNS 1.6.7 1.7.0 CoreDNS 1.6.7 1.7.0
etcd 3.4.3-0 3.4.7-0 etcd node1 3.4.3-0 3.4.7-0
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -371,12 +426,23 @@ _____________________________________________________________________
Description: "release candidate version", Description: "release candidate version",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.18.5", KubeVersion: "v1.18.5",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.18.5": 1, "v1.18.5": {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
"v1.18.5": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.18.5": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.18.5": {"node1"},
},
EtcdVersions: map[string][]string{
"3.4.3-0": {"node1"},
}, },
KubeadmVersion: "v1.18.5", KubeadmVersion: "v1.18.5",
DNSVersion: "1.6.7", DNSVersion: "1.6.7",
EtcdVersion: "3.4.3-0",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.19.0-rc.1", KubeVersion: "v1.19.0-rc.1",
@ -388,18 +454,18 @@ _____________________________________________________________________
}, },
versionStates: versionStates, versionStates: versionStates,
expectedBytes: []byte(`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 TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.18.5 v1.19.0-rc.1 kubelet node1 v1.18.5 v1.19.0-rc.1
Upgrade to the latest release candidate version: Upgrade to the latest release candidate version:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.18.5 v1.19.0-rc.1 kube-apiserver node1 v1.18.5 v1.19.0-rc.1
kube-controller-manager v1.18.5 v1.19.0-rc.1 kube-controller-manager node1 v1.18.5 v1.19.0-rc.1
kube-scheduler v1.18.5 v1.19.0-rc.1 kube-scheduler node1 v1.18.5 v1.19.0-rc.1
kube-proxy v1.18.5 v1.19.0-rc.1 kube-proxy v1.18.5 v1.19.0-rc.1
CoreDNS 1.6.7 1.7.0 CoreDNS 1.6.7 1.7.0
etcd 3.4.3-0 3.4.7-0 etcd node1 3.4.3-0 3.4.7-0
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -429,13 +495,24 @@ _____________________________________________________________________
Description: "version in the v1.19 series", Description: "version in the v1.19 series",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.19.2", KubeVersion: "v1.19.2",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.19.2": 1, "v1.19.2": {"node1"},
"v1.19.3": 2, },
KubeControllerManagerVersions: map[string][]string{
"v1.19.2": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.19.2": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.19.2": {"node1"},
"v1.19.3": {"node2", "node3"},
},
EtcdVersions: map[string][]string{
"3.4.7-0": {"node1"},
}, },
KubeadmVersion: "v1.19.2", KubeadmVersion: "v1.19.2",
DNSVersion: "1.7.0", DNSVersion: "1.7.0",
EtcdVersion: "3.4.7-0",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.19.3", KubeVersion: "v1.19.3",
@ -447,78 +524,20 @@ _____________________________________________________________________
}, },
versionStates: versionStates, versionStates: versionStates,
expectedBytes: []byte(`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 TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.19.2 v1.19.3 kubelet node1 v1.19.2 v1.19.3
2 x v1.19.3 v1.19.3 kubelet node2 v1.19.3 v1.19.3
kubelet node3 v1.19.3 v1.19.3
Upgrade to the latest version in the v1.19 series: Upgrade to the latest version in the v1.19 series:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.19.2 v1.19.3 kube-apiserver node1 v1.19.2 v1.19.3
kube-controller-manager v1.19.2 v1.19.3 kube-controller-manager node1 v1.19.2 v1.19.3
kube-scheduler v1.19.2 v1.19.3 kube-scheduler node1 v1.19.2 v1.19.3
kube-proxy v1.19.2 v1.19.3 kube-proxy v1.19.2 v1.19.3
CoreDNS 1.7.0 1.7.0 CoreDNS 1.7.0 1.7.0
etcd 3.4.7-0 3.4.7-0 etcd node1 3.4.7-0 3.4.7-0
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.19.3
Note: Before you can perform this upgrade, you have to update kubeadm to v1.19.3.
_____________________________________________________________________
The table below shows the current state of component configs as understood by this version of kubeadm.
Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or
resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually
upgrade to is denoted in the "PREFERRED VERSION" column.
API GROUP CURRENT VERSION PREFERRED VERSION MANUAL UPGRADE REQUIRED
kubeproxy.config.k8s.io v1alpha1 v1alpha1 no
kubelet.config.k8s.io v1beta1 v1beta1 no
_____________________________________________________________________
`),
},
{
name: "external etcd upgrade available",
upgrades: []upgrade.Upgrade{
{
Description: "version in the v1.19 series",
Before: upgrade.ClusterState{
KubeVersion: "v1.19.2",
KubeletVersions: map[string]uint16{
"v1.19.2": 1,
},
KubeadmVersion: "v1.19.2",
DNSVersion: "1.7.0",
EtcdVersion: "3.4.7-0",
},
After: upgrade.ClusterState{
KubeVersion: "v1.19.3",
KubeadmVersion: "v1.19.3",
DNSVersion: "1.7.0",
EtcdVersion: "3.4.7-0",
},
},
},
versionStates: versionStates,
externalEtcd: true,
expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT TARGET
kubelet 1 x v1.19.2 v1.19.3
Upgrade to the latest version in the v1.19 series:
COMPONENT CURRENT TARGET
kube-apiserver v1.19.2 v1.19.3
kube-controller-manager v1.19.2 v1.19.3
kube-scheduler v1.19.2 v1.19.3
kube-proxy v1.19.2 v1.19.3
CoreDNS 1.7.0 1.7.0
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -551,7 +570,7 @@ _____________________________________________________________________
t.Errorf("failed ToPrinter, err: %+v", err) t.Errorf("failed ToPrinter, err: %+v", err)
} }
plan := genUpgradePlan(rt.upgrades, rt.versionStates, rt.externalEtcd) plan := genUpgradePlan(rt.upgrades, rt.versionStates)
if err := printer.PrintObj(plan, rt.buf); err != nil { if err := printer.PrintObj(plan, rt.buf); err != nil {
t.Errorf("unexpected error when print object: %v", err) t.Errorf("unexpected error when print object: %v", err)
} }
@ -559,7 +578,7 @@ _____________________________________________________________________
actualBytes := rt.buf.Bytes() actualBytes := rt.buf.Bytes()
if !bytes.Equal(actualBytes, rt.expectedBytes) { if !bytes.Equal(actualBytes, rt.expectedBytes) {
t.Errorf( t.Errorf(
"failed PrintUpgradePlan:\n\texpected: %q\n\n\tactual : %q", "failed PrintUpgradePlan:\n\texpected: %s\n\n\tactual: %s",
string(rt.expectedBytes), string(rt.expectedBytes),
string(actualBytes), string(actualBytes),
) )
@ -574,12 +593,23 @@ func TestPrintUpgradePlanStructured(t *testing.T) {
Description: "version in the v1.8 series", Description: "version in the v1.8 series",
Before: upgrade.ClusterState{ Before: upgrade.ClusterState{
KubeVersion: "v1.8.1", KubeVersion: "v1.8.1",
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
"v1.8.1": 1, "v1.8.1": {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
"v1.8.1": {"node1"},
},
KubeSchedulerVersions: map[string][]string{
"v1.8.1": {"node1"},
},
KubeletVersions: map[string][]string{
"v1.8.1": {"node1"},
},
EtcdVersions: map[string][]string{
"3.0.17": {"node1"},
}, },
KubeadmVersion: "v1.8.2", KubeadmVersion: "v1.8.2",
DNSVersion: "1.14.5", DNSVersion: "1.14.5",
EtcdVersion: "3.0.17",
}, },
After: upgrade.ClusterState{ After: upgrade.ClusterState{
KubeVersion: "v1.8.3", KubeVersion: "v1.8.3",
@ -624,23 +654,27 @@ func TestPrintUpgradePlanStructured(t *testing.T) {
"components": [ "components": [
{ {
"name": "kubelet", "name": "kubelet",
"currentVersion": "1 x v1.8.1", "currentVersion": "v1.8.1",
"newVersion": "v1.8.3" "newVersion": "v1.8.3",
"nodeName": "node1"
}, },
{ {
"name": "kube-apiserver", "name": "kube-apiserver",
"currentVersion": "v1.8.1", "currentVersion": "v1.8.1",
"newVersion": "v1.8.3" "newVersion": "v1.8.3",
"nodeName": "node1"
}, },
{ {
"name": "kube-controller-manager", "name": "kube-controller-manager",
"currentVersion": "v1.8.1", "currentVersion": "v1.8.1",
"newVersion": "v1.8.3" "newVersion": "v1.8.3",
"nodeName": "node1"
}, },
{ {
"name": "kube-scheduler", "name": "kube-scheduler",
"currentVersion": "v1.8.1", "currentVersion": "v1.8.1",
"newVersion": "v1.8.3" "newVersion": "v1.8.3",
"nodeName": "node1"
}, },
{ {
"name": "kube-proxy", "name": "kube-proxy",
@ -660,7 +694,8 @@ func TestPrintUpgradePlanStructured(t *testing.T) {
{ {
"name": "etcd", "name": "etcd",
"currentVersion": "3.0.17", "currentVersion": "3.0.17",
"newVersion": "3.0.17" "newVersion": "3.0.17",
"nodeName": "node1"
} }
] ]
} }
@ -688,18 +723,22 @@ func TestPrintUpgradePlanStructured(t *testing.T) {
expected: `apiVersion: output.kubeadm.k8s.io/v1alpha3 expected: `apiVersion: output.kubeadm.k8s.io/v1alpha3
availableUpgrades: availableUpgrades:
- components: - components:
- currentVersion: 1 x v1.8.1 - currentVersion: v1.8.1
name: kubelet name: kubelet
newVersion: v1.8.3 newVersion: v1.8.3
nodeName: node1
- currentVersion: v1.8.1 - currentVersion: v1.8.1
name: kube-apiserver name: kube-apiserver
newVersion: v1.8.3 newVersion: v1.8.3
nodeName: node1
- currentVersion: v1.8.1 - currentVersion: v1.8.1
name: kube-controller-manager name: kube-controller-manager
newVersion: v1.8.3 newVersion: v1.8.3
nodeName: node1
- currentVersion: v1.8.1 - currentVersion: v1.8.1
name: kube-scheduler name: kube-scheduler
newVersion: v1.8.3 newVersion: v1.8.3
nodeName: node1
- currentVersion: v1.8.1 - currentVersion: v1.8.1
name: kube-proxy name: kube-proxy
newVersion: v1.8.3 newVersion: v1.8.3
@ -712,6 +751,7 @@ availableUpgrades:
- currentVersion: 3.0.17 - currentVersion: 3.0.17
name: etcd name: etcd
newVersion: 3.0.17 newVersion: 3.0.17
nodeName: node1
description: version in the v1.8 series description: version in the v1.8 series
configVersions: configVersions:
- currentVersion: v1alpha1 - currentVersion: v1alpha1
@ -729,18 +769,18 @@ kind: UpgradePlan
name: "Text output", name: "Text output",
outputFormat: "text", outputFormat: "text",
expected: `Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': expected: `Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kubelet 1 x v1.8.1 v1.8.3 kubelet node1 v1.8.1 v1.8.3
Upgrade to the latest version in the v1.8 series: Upgrade to the latest version in the v1.8 series:
COMPONENT CURRENT TARGET COMPONENT NODE CURRENT TARGET
kube-apiserver v1.8.1 v1.8.3 kube-apiserver node1 v1.8.1 v1.8.3
kube-controller-manager v1.8.1 v1.8.3 kube-controller-manager node1 v1.8.1 v1.8.3
kube-scheduler v1.8.1 v1.8.3 kube-scheduler node1 v1.8.1 v1.8.3
kube-proxy v1.8.1 v1.8.3 kube-proxy v1.8.1 v1.8.3
CoreDNS 1.14.5 1.14.5 CoreDNS 1.14.5 1.14.5
etcd 3.0.17 3.0.17 etcd node1 3.0.17 3.0.17
You can now apply the upgrade by executing the following command: You can now apply the upgrade by executing the following command:
@ -774,7 +814,7 @@ _____________________________________________________________________
t.Errorf("failed ToPrinter, err: %+v", err) t.Errorf("failed ToPrinter, err: %+v", err)
} }
plan := genUpgradePlan(upgrades, versionStates, false) plan := genUpgradePlan(upgrades, versionStates)
if err := printer.PrintObj(plan, rt.buf); err != nil { if err := printer.PrintObj(plan, rt.buf); err != nil {
t.Errorf("unexpected error when print object: %v", err) t.Errorf("unexpected error when print object: %v", err)
} }

View File

@ -52,38 +52,55 @@ func (u *Upgrade) CanUpgradeKubelets() bool {
return !sameVersionFound return !sameVersionFound
} }
// CanUpgradeEtcd returns whether an upgrade of etcd is possible // ClusterState describes the state of certain versions for a cluster during an upgrade
func (u *Upgrade) CanUpgradeEtcd() bool {
return u.Before.EtcdVersion != u.After.EtcdVersion
}
// ClusterState describes the state of certain versions for a cluster
type ClusterState struct { type ClusterState struct {
// KubeVersion describes the version of the Kubernetes API Server, Controller Manager, Scheduler and Proxy. // KubeVersion describes the version of latest Kubernetes API Server in the cluster.
KubeVersion string KubeVersion string
// DNSVersion describes the version of the DNS add-on. // DNSVersion describes the version of the DNS add-on.
DNSVersion string DNSVersion string
// KubeadmVersion describes the version of the kubeadm CLI // KubeadmVersion describes the version of the kubeadm CLI
KubeadmVersion string KubeadmVersion string
// KubeletVersions is a map with a version number linked to the amount of kubelets running that version in the cluster
KubeletVersions map[string]uint16
// EtcdVersion represents the version of etcd used in the cluster // EtcdVersion represents the version of etcd used in the cluster
EtcdVersion string EtcdVersion string
// The following maps describe the versions of the different components in the cluster.
// The key is the version string and the value is a list of nodes that have that version.
KubeAPIServerVersions map[string][]string
KubeControllerManagerVersions map[string][]string
KubeSchedulerVersions map[string][]string
EtcdVersions map[string][]string
KubeletVersions map[string][]string
} }
// GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which // GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which
// kinds of upgrades can be performed // kinds of upgrades can be performed
func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed, externalEtcd bool, client clientset.Interface, manifestsDir string, printer output.Printer) ([]Upgrade, error) { func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed bool, client clientset.Interface, printer output.Printer) ([]Upgrade, error) {
printer.Printf("[upgrade] Fetching available versions to upgrade to\n") printer.Printf("[upgrade] Fetching available versions to upgrade to\n")
// Collect the upgrades kubeadm can do in this list // Collect the upgrades kubeadm can do in this list
var upgrades []Upgrade var upgrades []Upgrade
// Get the cluster version // Get the kube-apiserver versions in the cluster
clusterVersionStr, clusterVersion, err := versionGetterImpl.ClusterVersion() kubeAPIServerVersions, err := versionGetterImpl.ComponentVersions(kubeadmconstants.KubeAPIServer)
if err != nil { if err != nil {
return upgrades, err return upgrades, err
} }
if len(kubeAPIServerVersions) > 1 {
verMsg := []string{}
for version, nodes := range kubeAPIServerVersions {
verMsg = append(verMsg, fmt.Sprintf("%s on nodes %v", version, nodes))
}
klog.Warningf("Different API server versions in the cluster were discovered: %v. Please upgrade your control plane"+
" nodes to the same version of Kubernetes", strings.Join(verMsg, ", "))
}
// Get the lastest cluster version
clusterVersion, err := getLatestClusterVersion(kubeAPIServerVersions)
if err != nil {
return upgrades, err
}
clusterVersionStr := clusterVersion.String()
printer.Printf("[upgrade/versions] Cluster version: %s\n", clusterVersionStr) printer.Printf("[upgrade/versions] Cluster version: %s\n", clusterVersionStr)
// Get current kubeadm CLI version // Get current kubeadm CLI version
@ -109,15 +126,25 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
return upgrades, err return upgrades, err
} }
// Get current stacked etcd version on the local node // Get the kube-controller-manager versions in the cluster
var etcdVersion string kubeControllerManagerVersions, err := versionGetterImpl.ComponentVersions(kubeadmconstants.KubeControllerManager)
if !externalEtcd { if err != nil {
etcdVersion, err = GetEtcdImageTagFromStaticPod(manifestsDir) return upgrades, err
if err != nil {
return upgrades, err
}
} }
// Get the kube-scheduler versions in the cluster
kubeSchedulerVersions, err := versionGetterImpl.ComponentVersions(kubeadmconstants.KubeScheduler)
if err != nil {
return upgrades, err
}
// Get the etcd versions in the cluster
etcdVersions, err := versionGetterImpl.ComponentVersions(kubeadmconstants.Etcd)
if err != nil {
return upgrades, err
}
isExternalEtcd := len(etcdVersions) == 0
dnsVersion, err := dns.DeployedDNSAddon(client) dnsVersion, err := dns.DeployedDNSAddon(client)
if err != nil { if err != nil {
return nil, err return nil, err
@ -125,11 +152,14 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
// Construct a descriptor for the current state of the world // Construct a descriptor for the current state of the world
beforeState := ClusterState{ beforeState := ClusterState{
KubeVersion: clusterVersionStr, KubeVersion: clusterVersionStr,
DNSVersion: dnsVersion, DNSVersion: dnsVersion,
KubeadmVersion: kubeadmVersionStr, KubeadmVersion: kubeadmVersionStr,
KubeletVersions: kubeletVersions, KubeAPIServerVersions: kubeAPIServerVersions,
EtcdVersion: etcdVersion, KubeControllerManagerVersions: kubeControllerManagerVersions,
KubeSchedulerVersions: kubeSchedulerVersions,
KubeletVersions: kubeletVersions,
EtcdVersions: etcdVersions,
} }
// Do a "dumb guess" that a new minor upgrade is available just because the latest stable version is higher than the cluster version // Do a "dumb guess" that a new minor upgrade is available just because the latest stable version is higher than the cluster version
@ -173,8 +203,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
KubeVersion: patchVersionStr, KubeVersion: patchVersionStr,
DNSVersion: kubeadmconstants.CoreDNSVersion, DNSVersion: kubeadmconstants.CoreDNSVersion,
KubeadmVersion: newKubeadmVer, KubeadmVersion: newKubeadmVer,
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, patchVersionStr), EtcdVersion: getSuggestedEtcdVersion(isExternalEtcd, patchVersionStr),
// KubeletVersions is unset here as it is not used anywhere in .After
}, },
}) })
} }
@ -189,8 +218,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
KubeVersion: stableVersionStr, KubeVersion: stableVersionStr,
DNSVersion: kubeadmconstants.CoreDNSVersion, DNSVersion: kubeadmconstants.CoreDNSVersion,
KubeadmVersion: stableVersionStr, KubeadmVersion: stableVersionStr,
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, stableVersionStr), EtcdVersion: getSuggestedEtcdVersion(isExternalEtcd, stableVersionStr),
// KubeletVersions is unset here as it is not used anywhere in .After
}, },
}) })
} }
@ -236,8 +264,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
KubeVersion: previousBranchLatestVersionStr, KubeVersion: previousBranchLatestVersionStr,
DNSVersion: kubeadmconstants.CoreDNSVersion, DNSVersion: kubeadmconstants.CoreDNSVersion,
KubeadmVersion: previousBranchLatestVersionStr, KubeadmVersion: previousBranchLatestVersionStr,
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, previousBranchLatestVersionStr), EtcdVersion: getSuggestedEtcdVersion(isExternalEtcd, previousBranchLatestVersionStr),
// KubeletVersions is unset here as it is not used anywhere in .After
}, },
}) })
} }
@ -260,8 +287,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
KubeVersion: unstableKubeVersion, KubeVersion: unstableKubeVersion,
DNSVersion: kubeadmconstants.CoreDNSVersion, DNSVersion: kubeadmconstants.CoreDNSVersion,
KubeadmVersion: unstableKubeVersion, KubeadmVersion: unstableKubeVersion,
EtcdVersion: getSuggestedEtcdVersion(externalEtcd, unstableKubeVersion), EtcdVersion: getSuggestedEtcdVersion(isExternalEtcd, unstableKubeVersion),
// KubeletVersions is unset here as it is not used anywhere in .After
}, },
}) })
} }
@ -294,8 +320,8 @@ func minorUpgradePossibleWithPatchRelease(stableVersion, patchVersion *versionut
return patchVersion.LessThan(stableVersion) return patchVersion.LessThan(stableVersion)
} }
func getSuggestedEtcdVersion(externalEtcd bool, kubernetesVersion string) string { func getSuggestedEtcdVersion(isExternalEtcd bool, kubernetesVersion string) string {
if externalEtcd { if isExternalEtcd {
return "" return ""
} }
etcdVersion, warning, err := kubeadmconstants.EtcdSupportedVersion(kubeadmconstants.SupportedEtcdVersion, kubernetesVersion) etcdVersion, warning, err := kubeadmconstants.EtcdSupportedVersion(kubeadmconstants.SupportedEtcdVersion, kubernetesVersion)
@ -308,3 +334,18 @@ func getSuggestedEtcdVersion(externalEtcd bool, kubernetesVersion string) string
} }
return etcdVersion.String() return etcdVersion.String()
} }
func getLatestClusterVersion(kubeAPIServerVersions map[string][]string) (*versionutil.Version, error) {
var latestVersion *versionutil.Version
for versionStr, nodes := range kubeAPIServerVersions {
ver, err := versionutil.ParseSemantic(versionStr)
if err != nil {
return nil, fmt.Errorf("couldn't parse kube-apiserver version %s from nodes %v", versionStr, nodes)
}
if latestVersion == nil || ver.AtLeast(latestVersion) {
latestVersion = ver
}
}
return latestVersion, nil
}

View File

@ -18,11 +18,10 @@ package upgrade
import ( import (
"fmt" "fmt"
"os"
"reflect"
"strings" "strings"
"testing" "testing"
"github.com/google/go-cmp/cmp"
apps "k8s.io/api/apps/v1" apps "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -34,7 +33,16 @@ import (
) )
type fakeVersionGetter struct { type fakeVersionGetter struct {
clusterVersion, kubeadmVersion, stableVersion, latestVersion, latestDevBranchVersion, stablePatchVersion, kubeletVersion string clusterVersion string
kubeadmVersion string
stableVersion string
latestVersion string
latestDevBranchVersion string
stablePatchVersion string
kubeletVersion string
componentVersion string
etcdVersion string
isExternalEtcd bool
} }
var _ VersionGetter = &fakeVersionGetter{} var _ VersionGetter = &fakeVersionGetter{}
@ -63,26 +71,30 @@ func (f *fakeVersionGetter) VersionFromCILabel(ciVersionLabel, _ string) (string
return f.stablePatchVersion, versionutil.MustParseSemantic(f.stablePatchVersion), nil return f.stablePatchVersion, versionutil.MustParseSemantic(f.stablePatchVersion), nil
} }
// KubeletVersions gets the versions of the kubelets in the cluster // KubeletVersions should return a map with a version and a list of node names that describes how many kubelets there are for that version
func (f *fakeVersionGetter) KubeletVersions() (map[string]uint16, error) { func (f *fakeVersionGetter) KubeletVersions() (map[string][]string, error) {
return map[string]uint16{ return map[string][]string{
f.kubeletVersion: 1, f.kubeletVersion: {"node1"},
}, nil
}
// ComponentVersions should return a map with a version and a list of node names that describes how many a given control-plane components there are for that version
func (f *fakeVersionGetter) ComponentVersions(name string) (map[string][]string, error) {
if name == constants.Etcd {
if f.isExternalEtcd {
return map[string][]string{}, nil
}
return map[string][]string{
f.etcdVersion: {"node1"},
}, nil
}
return map[string][]string{
f.componentVersion: {"node1"},
}, nil }, nil
} }
const fakeCurrentEtcdVersion = "3.1.12" const fakeCurrentEtcdVersion = "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: registry.k8s.io/etcd:` + fakeCurrentEtcdVersion
func getEtcdVersion(v *versionutil.Version) string { func getEtcdVersion(v *versionutil.Version) string {
etcdVer, _, _ := constants.EtcdSupportedVersion(constants.SupportedEtcdVersion, v.String()) etcdVer, _, _ := constants.EtcdSupportedVersion(constants.SupportedEtcdVersion, v.String())
@ -123,15 +135,16 @@ func TestGetAvailableUpgrades(t *testing.T) {
expectedUpgrades []Upgrade expectedUpgrades []Upgrade
allowExperimental, allowRCs bool allowExperimental, allowRCs bool
errExpected bool errExpected bool
externalEtcd bool
beforeDNSVersion string beforeDNSVersion string
}{ }{
{ {
name: "no action needed, already up-to-date", name: "no action needed, already up-to-date",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Y0.String(), clusterVersion: v1Y0.String(),
kubeletVersion: v1Y0.String(), componentVersion: v1Y0.String(),
kubeadmVersion: v1Y0.String(), kubeletVersion: v1Y0.String(),
kubeadmVersion: v1Y0.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y0.String(), stablePatchVersion: v1Y0.String(),
stableVersion: v1Y0.String(), stableVersion: v1Y0.String(),
@ -144,9 +157,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "simple patch version upgrade", name: "simple patch version upgrade",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Y1.String(), clusterVersion: v1Y1.String(),
kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane componentVersion: v1Y1.String(),
kubeadmVersion: v1Y2.String(), kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane
kubeadmVersion: v1Y2.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y3.String(), stablePatchVersion: v1Y3.String(),
stableVersion: v1Y3.String(), stableVersion: v1Y3.String(),
@ -157,12 +172,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y1.String(), KubeVersion: v1Y1.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y1.String(): 1, v1Y1.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y1.String(): {"node1"},
}, },
KubeadmVersion: v1Y2.String(), KubeadmVersion: v1Y2.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Y3.String(), KubeVersion: v1Y3.String(),
@ -178,26 +202,36 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "simple patch version upgrade with external etcd", name: "simple patch version upgrade with external etcd",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Y1.String(), clusterVersion: v1Y1.String(),
kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane componentVersion: v1Y1.String(),
kubeadmVersion: v1Y2.String(), kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane
kubeadmVersion: v1Y2.String(),
isExternalEtcd: true,
stablePatchVersion: v1Y3.String(), stablePatchVersion: v1Y3.String(),
stableVersion: v1Y3.String(), stableVersion: v1Y3.String(),
}, },
beforeDNSVersion: fakeCurrentCoreDNSVersion, beforeDNSVersion: fakeCurrentCoreDNSVersion,
externalEtcd: true,
expectedUpgrades: []Upgrade{ expectedUpgrades: []Upgrade{
{ {
Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y1.String(), KubeVersion: v1Y1.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y1.String(): 1, v1Y1.String(): {"node1"},
}, },
KubeControllerManagerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
EtcdVersions: map[string][]string{},
KubeadmVersion: v1Y2.String(), KubeadmVersion: v1Y2.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: "",
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Y3.String(), KubeVersion: v1Y3.String(),
@ -213,9 +247,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "no version provided to offline version getter does not change behavior", name: "no version provided to offline version getter does not change behavior",
vg: NewOfflineVersionGetter(&fakeVersionGetter{ vg: NewOfflineVersionGetter(&fakeVersionGetter{
clusterVersion: v1Y1.String(), clusterVersion: v1Y1.String(),
kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane componentVersion: v1Y1.String(),
kubeadmVersion: v1Y2.String(), kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane
kubeadmVersion: v1Y2.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y3.String(), stablePatchVersion: v1Y3.String(),
stableVersion: v1Y3.String(), stableVersion: v1Y3.String(),
@ -226,12 +262,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y1.String(), KubeVersion: v1Y1.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y1.String(): 1, v1Y1.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y1.String(): {"node1"},
}, },
KubeadmVersion: v1Y2.String(), KubeadmVersion: v1Y2.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Y3.String(), KubeVersion: v1Y3.String(),
@ -247,9 +292,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "minor version upgrade only", name: "minor version upgrade only",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Y1.String(), clusterVersion: v1Y1.String(),
kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane componentVersion: v1Y1.String(),
kubeadmVersion: v1Z0.String(), kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane
kubeadmVersion: v1Z0.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y1.String(), stablePatchVersion: v1Y1.String(),
stableVersion: v1Z0.String(), stableVersion: v1Z0.String(),
@ -260,12 +307,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "stable version", Description: "stable version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y1.String(), KubeVersion: v1Y1.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y1.String(): 1, v1Y1.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y1.String(): {"node1"},
}, },
KubeadmVersion: v1Z0.String(), KubeadmVersion: v1Z0.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z0.String(), KubeVersion: v1Z0.String(),
@ -281,9 +337,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "both minor version upgrade and patch version upgrade available", name: "both minor version upgrade and patch version upgrade available",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Y3.String(), clusterVersion: v1Y3.String(),
kubeletVersion: v1Y3.String(), // the kubelet are on the same version as the control plane componentVersion: v1Y3.String(),
kubeadmVersion: v1Y5.String(), kubeletVersion: v1Y3.String(), // the kubelet are on the same version as the control plane
kubeadmVersion: v1Y5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y5.String(), stablePatchVersion: v1Y5.String(),
stableVersion: v1Z1.String(), stableVersion: v1Z1.String(),
@ -294,12 +352,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y3.String(), KubeVersion: v1Y3.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y3.String(): 1, v1Y3.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Y3.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y3.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y3.String(): {"node1"},
}, },
KubeadmVersion: v1Y5.String(), KubeadmVersion: v1Y5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Y5.String(), KubeVersion: v1Y5.String(),
@ -312,12 +379,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "stable version", Description: "stable version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y3.String(), KubeVersion: v1Y3.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y3.String(): 1, v1Y3.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Y3.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y3.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y3.String(): {"node1"},
}, },
KubeadmVersion: v1Y5.String(), KubeadmVersion: v1Y5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z1.String(), KubeVersion: v1Z1.String(),
@ -333,9 +409,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "allow experimental upgrades, but no upgrade available", name: "allow experimental upgrades, but no upgrade available",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Z0alpha2.String(), clusterVersion: v1Z0alpha2.String(),
kubeletVersion: v1Y5.String(), componentVersion: v1Z0alpha2.String(),
kubeadmVersion: v1Y5.String(), kubeletVersion: v1Y5.String(),
kubeadmVersion: v1Y5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y5.String(), stablePatchVersion: v1Y5.String(),
stableVersion: v1Y5.String(), stableVersion: v1Y5.String(),
@ -349,9 +427,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "upgrade to an unstable version should be supported", name: "upgrade to an unstable version should be supported",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Y5.String(), clusterVersion: v1Y5.String(),
kubeletVersion: v1Y5.String(), componentVersion: v1Y5.String(),
kubeadmVersion: v1Y5.String(), kubeletVersion: v1Y5.String(),
kubeadmVersion: v1Y5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y5.String(), stablePatchVersion: v1Y5.String(),
stableVersion: v1Y5.String(), stableVersion: v1Y5.String(),
@ -363,12 +443,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "experimental version", Description: "experimental version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y5.String(), KubeVersion: v1Y5.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y5.String(): 1, v1Y5.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Y5.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y5.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y5.String(): {"node1"},
}, },
KubeadmVersion: v1Y5.String(), KubeadmVersion: v1Y5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z0alpha2.String(), KubeVersion: v1Z0alpha2.String(),
@ -384,9 +473,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "upgrade from an unstable version to an unstable version should be supported", name: "upgrade from an unstable version to an unstable version should be supported",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1Z0alpha1.String(), clusterVersion: v1Z0alpha1.String(),
kubeletVersion: v1Y5.String(), componentVersion: v1Z0alpha1.String(),
kubeadmVersion: v1Y5.String(), kubeletVersion: v1Y5.String(),
kubeadmVersion: v1Y5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1Y5.String(), stablePatchVersion: v1Y5.String(),
stableVersion: v1Y5.String(), stableVersion: v1Y5.String(),
@ -398,12 +489,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "experimental version", Description: "experimental version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Z0alpha1.String(), KubeVersion: v1Z0alpha1.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y5.String(): 1, v1Z0alpha1.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Z0alpha1.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Z0alpha1.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y5.String(): {"node1"},
}, },
KubeadmVersion: v1Y5.String(), KubeadmVersion: v1Y5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z0alpha2.String(), KubeVersion: v1Z0alpha2.String(),
@ -419,9 +519,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "v1.X.0-alpha.0 should be ignored", name: "v1.X.0-alpha.0 should be ignored",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1X5.String(), clusterVersion: v1X5.String(),
kubeletVersion: v1X5.String(), componentVersion: v1X5.String(),
kubeadmVersion: v1X5.String(), kubeletVersion: v1X5.String(),
kubeadmVersion: v1X5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1X5.String(), stablePatchVersion: v1X5.String(),
stableVersion: v1X5.String(), stableVersion: v1X5.String(),
@ -434,12 +536,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "experimental version", Description: "experimental version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1X5.String(), KubeVersion: v1X5.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1X5.String(): 1, v1X5.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1X5.String(): {"node1"},
}, },
KubeadmVersion: v1X5.String(), KubeadmVersion: v1X5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z0beta1.String(), KubeVersion: v1Z0beta1.String(),
@ -455,9 +566,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "upgrade to an RC version should be supported", name: "upgrade to an RC version should be supported",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1X5.String(), clusterVersion: v1X5.String(),
kubeletVersion: v1X5.String(), componentVersion: v1X5.String(),
kubeadmVersion: v1X5.String(), kubeletVersion: v1X5.String(),
kubeadmVersion: v1X5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1X5.String(), stablePatchVersion: v1X5.String(),
stableVersion: v1X5.String(), stableVersion: v1X5.String(),
@ -470,12 +583,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "release candidate version", Description: "release candidate version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1X5.String(), KubeVersion: v1X5.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1X5.String(): 1, v1X5.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1X5.String(): {"node1"},
}, },
KubeadmVersion: v1X5.String(), KubeadmVersion: v1X5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z0rc1.String(), KubeVersion: v1Z0rc1.String(),
@ -491,9 +613,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
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", 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",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1X5.String(), clusterVersion: v1X5.String(),
kubeletVersion: v1X5.String(), componentVersion: v1X5.String(),
kubeadmVersion: v1X5.String(), kubeletVersion: v1X5.String(),
kubeadmVersion: v1X5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1X5.String(), stablePatchVersion: v1X5.String(),
stableVersion: v1X5.String(), stableVersion: v1X5.String(),
@ -506,12 +630,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "experimental version", // Note that this is considered an experimental version in this uncommon scenario Description: "experimental version", // Note that this is considered an experimental version in this uncommon scenario
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1X5.String(), KubeVersion: v1X5.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1X5.String(): 1, v1X5.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1X5.String(): {"node1"},
}, },
KubeadmVersion: v1X5.String(), KubeadmVersion: v1X5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z0rc1.String(), KubeVersion: v1Z0rc1.String(),
@ -527,9 +660,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "upgrade to an RC version should be supported. There may also be an even newer unstable version.", name: "upgrade to an RC version should be supported. There may also be an even newer unstable version.",
vg: &fakeVersionGetter{ vg: &fakeVersionGetter{
clusterVersion: v1X5.String(), clusterVersion: v1X5.String(),
kubeletVersion: v1X5.String(), componentVersion: v1X5.String(),
kubeadmVersion: v1X5.String(), kubeletVersion: v1X5.String(),
kubeadmVersion: v1X5.String(),
etcdVersion: fakeCurrentEtcdVersion,
stablePatchVersion: v1X5.String(), stablePatchVersion: v1X5.String(),
stableVersion: v1X5.String(), stableVersion: v1X5.String(),
@ -542,12 +677,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "release candidate version", Description: "release candidate version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1X5.String(), KubeVersion: v1X5.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1X5.String(): 1, v1X5.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1X5.String(): {"node1"},
}, },
KubeadmVersion: v1X5.String(), KubeadmVersion: v1X5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z0rc1.String(), KubeVersion: v1Z0rc1.String(),
@ -560,12 +704,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: "experimental version", Description: "experimental version",
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1X5.String(), KubeVersion: v1X5.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1X5.String(): 1, v1X5.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1X5.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1X5.String(): {"node1"},
}, },
KubeadmVersion: v1X5.String(), KubeadmVersion: v1X5.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Y0alpha1.String(), KubeVersion: v1Y0alpha1.String(),
@ -582,9 +735,11 @@ func TestGetAvailableUpgrades(t *testing.T) {
{ {
name: "offline version getter", name: "offline version getter",
vg: NewOfflineVersionGetter(&fakeVersionGetter{ vg: NewOfflineVersionGetter(&fakeVersionGetter{
clusterVersion: v1Y1.String(), clusterVersion: v1Y1.String(),
kubeletVersion: v1Y0.String(), componentVersion: v1Y1.String(),
kubeadmVersion: v1Y1.String(), kubeletVersion: v1Y0.String(),
kubeadmVersion: v1Y1.String(),
etcdVersion: fakeCurrentEtcdVersion,
}, v1Z1.String()), }, v1Z1.String()),
beforeDNSVersion: fakeCurrentCoreDNSVersion, beforeDNSVersion: fakeCurrentCoreDNSVersion,
expectedUpgrades: []Upgrade{ expectedUpgrades: []Upgrade{
@ -592,12 +747,21 @@ func TestGetAvailableUpgrades(t *testing.T) {
Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()),
Before: ClusterState{ Before: ClusterState{
KubeVersion: v1Y1.String(), KubeVersion: v1Y1.String(),
KubeletVersions: map[string]uint16{ KubeAPIServerVersions: map[string][]string{
v1Y0.String(): 1, v1Y1.String(): {"node1"},
},
KubeControllerManagerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeSchedulerVersions: map[string][]string{
v1Y1.String(): {"node1"},
},
KubeletVersions: map[string][]string{
v1Y0.String(): {"node1"},
}, },
KubeadmVersion: v1Y1.String(), KubeadmVersion: v1Y1.String(),
DNSVersion: fakeCurrentCoreDNSVersion, DNSVersion: fakeCurrentCoreDNSVersion,
EtcdVersion: fakeCurrentEtcdVersion, EtcdVersions: map[string][]string{fakeCurrentEtcdVersion: {"node1"}},
}, },
After: ClusterState{ After: ClusterState{
KubeVersion: v1Z1.String(), KubeVersion: v1Z1.String(),
@ -642,27 +806,18 @@ func TestGetAvailableUpgrades(t *testing.T) {
}, },
}) })
manifestsDir, err := os.MkdirTemp("", "GetAvailableUpgrades-test-manifests") actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, client, &output.TextPrinter{})
if err != nil { if diff := cmp.Diff(rt.expectedUpgrades, actualUpgrades); len(diff) > 0 {
t.Fatalf("Unable to create temporary directory: %v", err) t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades:\n%v\n\tgot:\n%v\n\tdiff:\n%v", rt.expectedUpgrades, actualUpgrades, diff)
}
defer os.RemoveAll(manifestsDir)
if err = os.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, client, manifestsDir, &output.TextPrinter{})
if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) {
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades)
} }
if rt.errExpected && actualErr == nil { if rt.errExpected && actualErr == nil {
t.Error("unexpected success") t.Error("unexpected success")
} else if !rt.errExpected && actualErr != nil { } else if !rt.errExpected && actualErr != nil {
t.Errorf("unexpected failure: %v", actualErr) t.Errorf("unexpected failure: %v", actualErr)
} }
if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) { if diff := cmp.Diff(rt.expectedUpgrades, actualUpgrades); len(diff) > 0 {
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades) t.Logf("diff: %s", cmp.Diff(rt.expectedUpgrades, actualUpgrades))
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades:\n%v\n\tgot:\n%v\n\tdiff:\n%v", rt.expectedUpgrades, actualUpgrades, diff)
} }
}) })
} }
@ -671,46 +826,46 @@ func TestGetAvailableUpgrades(t *testing.T) {
func TestKubeletUpgrade(t *testing.T) { func TestKubeletUpgrade(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
before map[string]uint16 before map[string][]string
after string after string
expected bool expected bool
}{ }{
{ {
name: "upgrade from v1.10.1 to v1.10.3 is available", name: "upgrade from v1.10.1 to v1.10.3 is available",
before: map[string]uint16{ before: map[string][]string{
"v1.10.1": 1, "v1.10.1": {"node1"},
}, },
after: "v1.10.3", after: "v1.10.3",
expected: true, expected: true,
}, },
{ {
name: "upgrade from v1.10.1 and v1.10.3/100 to v1.10.3 is available", name: "upgrade from v1.10.1 and v1.10.3/2 to v1.10.3 is available",
before: map[string]uint16{ before: map[string][]string{
"v1.10.1": 1, "v1.10.1": {"node1"},
"v1.10.3": 100, "v1.10.3": {"node2", "node3"},
}, },
after: "v1.10.3", after: "v1.10.3",
expected: true, expected: true,
}, },
{ {
name: "upgrade from v1.10.3 to v1.10.3 is not available", name: "upgrade from v1.10.3 to v1.10.3 is not available",
before: map[string]uint16{ before: map[string][]string{
"v1.10.3": 1, "v1.10.3": {"node1"},
}, },
after: "v1.10.3", after: "v1.10.3",
expected: false, expected: false,
}, },
{ {
name: "upgrade from v1.10.3/100 to v1.10.3 is not available", name: "upgrade from v1.10.3/2 to v1.10.3 is not available",
before: map[string]uint16{ before: map[string][]string{
"v1.10.3": 100, "v1.10.3": {"node1", "node2"},
}, },
after: "v1.10.3", after: "v1.10.3",
expected: false, expected: false,
}, },
{ {
name: "upgrade is not available if we don't know anything about the earlier state", name: "upgrade is not available if we don't know anything about the earlier state",
before: map[string]uint16{}, before: map[string][]string{},
after: "v1.10.3", after: "v1.10.3",
expected: false, expected: false,
}, },

View File

@ -168,7 +168,7 @@ func detectUnstableVersionError(newK8sVersion *version.Version, newK8sVersionStr
} }
// detectTooOldKubelets errors out if the kubelet versions are so old that an unsupported skew would happen if the cluster was upgraded // detectTooOldKubelets errors out if the kubelet versions are so old that an unsupported skew would happen if the cluster was upgraded
func detectTooOldKubelets(newK8sVersion *version.Version, kubeletVersions map[string]uint16) error { func detectTooOldKubelets(newK8sVersion *version.Version, kubeletVersions map[string][]string) error {
var tooOldKubeletVersions []string var tooOldKubeletVersions []string
for versionStr := range kubeletVersions { for versionStr := range kubeletVersions {

View File

@ -18,10 +18,10 @@ package upgrade
import ( import (
"context" "context"
"fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
versionutil "k8s.io/apimachinery/pkg/util/version" versionutil "k8s.io/apimachinery/pkg/util/version"
pkgversion "k8s.io/apimachinery/pkg/version" pkgversion "k8s.io/apimachinery/pkg/version"
@ -29,7 +29,9 @@ import (
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/component-base/version" "k8s.io/component-base/version"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/image"
) )
// VersionGetter defines an interface for fetching different versions. // VersionGetter defines an interface for fetching different versions.
@ -41,8 +43,10 @@ type VersionGetter interface {
KubeadmVersion() (string, *versionutil.Version, error) KubeadmVersion() (string, *versionutil.Version, error)
// VersionFromCILabel should resolve CI labels like `latest`, `stable`, `stable-1.8`, etc. to real versions // VersionFromCILabel should resolve CI labels like `latest`, `stable`, `stable-1.8`, etc. to real versions
VersionFromCILabel(string, string) (string, *versionutil.Version, error) VersionFromCILabel(string, string) (string, *versionutil.Version, error)
// KubeletVersions should return a map with a version and a number that describes how many kubelets there are for that version // KubeletVersions should return a map with a version and a list of node names that describes how many kubelets there are for that version
KubeletVersions() (map[string]uint16, error) KubeletVersions() (map[string][]string, error)
// ComponentVersions should return a map with a version and a list of node names that describes how many a given control-plane components there are for that version
ComponentVersions(string) (map[string][]string, error)
} }
// KubeVersionGetter handles the version-fetching mechanism from external sources // KubeVersionGetter handles the version-fetching mechanism from external sources
@ -111,27 +115,42 @@ func (g *KubeVersionGetter) VersionFromCILabel(ciVersionLabel, description strin
return versionStr, ver, nil return versionStr, ver, nil
} }
// KubeletVersions gets the versions of the kubelets in the cluster // KubeletVersions gets the versions of the kubelets in the cluster.
func (g *KubeVersionGetter) KubeletVersions() (map[string]uint16, error) { func (g *KubeVersionGetter) KubeletVersions() (map[string][]string, error) {
nodes, err := g.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) nodes, err := g.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil { if err != nil {
return nil, errors.New("couldn't list all nodes in cluster") return nil, errors.New("couldn't list all nodes in cluster")
} }
return computeKubeletVersions(nodes.Items), nil
// map kubelet version to a list of node names
kubeletVersions := make(map[string][]string)
for _, node := range nodes.Items {
kver := node.Status.NodeInfo.KubeletVersion
kubeletVersions[kver] = append(kubeletVersions[kver], node.Name)
}
return kubeletVersions, nil
} }
// computeKubeletVersions returns a string-int map that describes how many nodes are of a specific version // ComponentVersions gets the versions of the control-plane components in the cluster.
func computeKubeletVersions(nodes []v1.Node) map[string]uint16 { // The name parameter is the name of the component to get the versions for.
kubeletVersions := make(map[string]uint16) // The function returns a map with the version as the key and a list of node names as the value.
for _, node := range nodes { func (g *KubeVersionGetter) ComponentVersions(name string) (map[string][]string, error) {
kver := node.Status.NodeInfo.KubeletVersion podList, err := g.client.CoreV1().Pods(metav1.NamespaceSystem).List(
if _, found := kubeletVersions[kver]; !found { context.TODO(),
kubeletVersions[kver] = 1 metav1.ListOptions{
continue LabelSelector: fmt.Sprintf("component=%s,tier=%s", name, constants.ControlPlaneTier),
} },
kubeletVersions[kver]++ )
if err != nil {
return nil, errors.Wrap(err, "couldn't list pods in cluster")
} }
return kubeletVersions
componentVersions := make(map[string][]string)
for _, pod := range podList.Items {
tag := convertImageTagMetadataToSemver(image.TagFromImage(pod.Spec.Containers[0].Image))
componentVersions[tag] = append(componentVersions[tag], pod.Spec.NodeName)
}
return componentVersions, nil
} }
// OfflineVersionGetter will use the version provided or // OfflineVersionGetter will use the version provided or

View File

@ -148,7 +148,7 @@ func TestKubeVersionGetterKubeletVersions(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
nodes *v1.NodeList nodes *v1.NodeList
want map[string]uint16 want map[string][]string
wantErr bool wantErr bool
}{ }{
{ {
@ -181,9 +181,9 @@ func TestKubeVersionGetterKubeletVersions(t *testing.T) {
}, },
}, },
}, },
want: map[string]uint16{ want: map[string][]string{
"v1.28.0": 1, "v1.28.0": {"node1"},
"v1.28.1": 2, "v1.28.1": {"node2", "node3"},
}, },
wantErr: false, wantErr: false,
}, },
@ -201,15 +201,15 @@ func TestKubeVersionGetterKubeletVersions(t *testing.T) {
}, },
}, },
}, },
want: map[string]uint16{ want: map[string][]string{
"": 2, "": {"node2", "node3"},
}, },
wantErr: false, wantErr: false,
}, },
{ {
name: "node list is empty", name: "node list is empty",
nodes: &v1.NodeList{}, nodes: &v1.NodeList{},
want: map[string]uint16{}, want: map[string][]string{},
wantErr: false, wantErr: false,
}, },
} }