From e5d6536ade058ce5df6afc83a2329f23fab64860 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Mon, 14 Oct 2019 15:43:51 +0300 Subject: [PATCH 1/2] add UpgradePlan to the kubeadm.output API group --- cmd/kubeadm/app/apis/output/register.go | 1 + cmd/kubeadm/app/apis/output/types.go | 17 +++++ .../app/apis/output/v1alpha1/register.go | 1 + cmd/kubeadm/app/apis/output/v1alpha1/types.go | 17 +++++ .../v1alpha1/zz_generated.conversion.go | 64 +++++++++++++++++++ .../output/v1alpha1/zz_generated.deepcopy.go | 46 +++++++++++++ .../app/apis/output/zz_generated.deepcopy.go | 46 +++++++++++++ 7 files changed, 192 insertions(+) diff --git a/cmd/kubeadm/app/apis/output/register.go b/cmd/kubeadm/app/apis/output/register.go index ada0599efee..d7b7fcdd8e9 100644 --- a/cmd/kubeadm/app/apis/output/register.go +++ b/cmd/kubeadm/app/apis/output/register.go @@ -48,6 +48,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &BootstrapToken{}, &Images{}, + &UpgradePlan{}, ) return nil } diff --git a/cmd/kubeadm/app/apis/output/types.go b/cmd/kubeadm/app/apis/output/types.go index 033836303d0..a71ddae5f80 100644 --- a/cmd/kubeadm/app/apis/output/types.go +++ b/cmd/kubeadm/app/apis/output/types.go @@ -40,3 +40,20 @@ type Images struct { Images []string } + +// ComponentUpgradePlan represents information about upgrade plan for one component +type ComponentUpgradePlan struct { + Name string + CurrentVersion string + NewVersion string +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// UpgradePlan represents information about upgrade plan for the output +// produced by 'kubeadm upgrade plan' +type UpgradePlan struct { + metav1.TypeMeta + + Components []ComponentUpgradePlan +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/register.go b/cmd/kubeadm/app/apis/output/v1alpha1/register.go index 0d30c22278e..a1126859210 100644 --- a/cmd/kubeadm/app/apis/output/v1alpha1/register.go +++ b/cmd/kubeadm/app/apis/output/v1alpha1/register.go @@ -60,6 +60,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &BootstrapToken{}, &Images{}, + &UpgradePlan{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/types.go b/cmd/kubeadm/app/apis/output/v1alpha1/types.go index d20e14026e9..1ea18ae455e 100644 --- a/cmd/kubeadm/app/apis/output/v1alpha1/types.go +++ b/cmd/kubeadm/app/apis/output/v1alpha1/types.go @@ -40,3 +40,20 @@ type Images struct { Images []string `json:"images"` } + +// ComponentUpgradePlan represents information about upgrade plan for one component +type ComponentUpgradePlan struct { + Name string `json:"name"` + CurrentVersion string `json:"currentVersion"` + NewVersion string `json:"newVersion"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// UpgradePlan represents information about upgrade plan for the output +// produced by 'kubeadm upgrade plan' +type UpgradePlan struct { + metav1.TypeMeta + + Components []ComponentUpgradePlan `json:"components"` +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go index 512f3dbde91..c675fde12d0 100644 --- a/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go @@ -45,6 +45,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*ComponentUpgradePlan)(nil), (*output.ComponentUpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ComponentUpgradePlan_To_output_ComponentUpgradePlan(a.(*ComponentUpgradePlan), b.(*output.ComponentUpgradePlan), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*output.ComponentUpgradePlan)(nil), (*ComponentUpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_output_ComponentUpgradePlan_To_v1alpha1_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 { return Convert_v1alpha1_Images_To_output_Images(a.(*Images), b.(*output.Images), scope) }); err != nil { @@ -55,6 +65,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*UpgradePlan)(nil), (*output.UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_UpgradePlan_To_output_UpgradePlan(a.(*UpgradePlan), b.(*output.UpgradePlan), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*output.UpgradePlan)(nil), (*UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_output_UpgradePlan_To_v1alpha1_UpgradePlan(a.(*output.UpgradePlan), b.(*UpgradePlan), scope) + }); err != nil { + return err + } return nil } @@ -78,6 +98,30 @@ func Convert_output_BootstrapToken_To_v1alpha1_BootstrapToken(in *output.Bootstr return autoConvert_output_BootstrapToken_To_v1alpha1_BootstrapToken(in, out, s) } +func autoConvert_v1alpha1_ComponentUpgradePlan_To_output_ComponentUpgradePlan(in *ComponentUpgradePlan, out *output.ComponentUpgradePlan, s conversion.Scope) error { + out.Name = in.Name + out.CurrentVersion = in.CurrentVersion + out.NewVersion = in.NewVersion + return nil +} + +// Convert_v1alpha1_ComponentUpgradePlan_To_output_ComponentUpgradePlan is an autogenerated conversion function. +func Convert_v1alpha1_ComponentUpgradePlan_To_output_ComponentUpgradePlan(in *ComponentUpgradePlan, out *output.ComponentUpgradePlan, s conversion.Scope) error { + return autoConvert_v1alpha1_ComponentUpgradePlan_To_output_ComponentUpgradePlan(in, out, s) +} + +func autoConvert_output_ComponentUpgradePlan_To_v1alpha1_ComponentUpgradePlan(in *output.ComponentUpgradePlan, out *ComponentUpgradePlan, s conversion.Scope) error { + out.Name = in.Name + out.CurrentVersion = in.CurrentVersion + out.NewVersion = in.NewVersion + return nil +} + +// Convert_output_ComponentUpgradePlan_To_v1alpha1_ComponentUpgradePlan is an autogenerated conversion function. +func Convert_output_ComponentUpgradePlan_To_v1alpha1_ComponentUpgradePlan(in *output.ComponentUpgradePlan, out *ComponentUpgradePlan, s conversion.Scope) error { + return autoConvert_output_ComponentUpgradePlan_To_v1alpha1_ComponentUpgradePlan(in, out, s) +} + func autoConvert_v1alpha1_Images_To_output_Images(in *Images, out *output.Images, s conversion.Scope) error { out.Images = *(*[]string)(unsafe.Pointer(&in.Images)) return nil @@ -97,3 +141,23 @@ func autoConvert_output_Images_To_v1alpha1_Images(in *output.Images, out *Images func Convert_output_Images_To_v1alpha1_Images(in *output.Images, out *Images, s conversion.Scope) error { return autoConvert_output_Images_To_v1alpha1_Images(in, out, s) } + +func autoConvert_v1alpha1_UpgradePlan_To_output_UpgradePlan(in *UpgradePlan, out *output.UpgradePlan, s conversion.Scope) error { + out.Components = *(*[]output.ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) + return nil +} + +// Convert_v1alpha1_UpgradePlan_To_output_UpgradePlan is an autogenerated conversion function. +func Convert_v1alpha1_UpgradePlan_To_output_UpgradePlan(in *UpgradePlan, out *output.UpgradePlan, s conversion.Scope) error { + return autoConvert_v1alpha1_UpgradePlan_To_output_UpgradePlan(in, out, s) +} + +func autoConvert_output_UpgradePlan_To_v1alpha1_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { + out.Components = *(*[]ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) + return nil +} + +// Convert_output_UpgradePlan_To_v1alpha1_UpgradePlan is an autogenerated conversion function. +func Convert_output_UpgradePlan_To_v1alpha1_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { + return autoConvert_output_UpgradePlan_To_v1alpha1_UpgradePlan(in, out, s) +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go index 418114c7a9a..b388605a3db 100644 --- a/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go @@ -50,6 +50,22 @@ func (in *BootstrapToken) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComponentUpgradePlan) DeepCopyInto(out *ComponentUpgradePlan) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentUpgradePlan. +func (in *ComponentUpgradePlan) DeepCopy() *ComponentUpgradePlan { + if in == nil { + return nil + } + out := new(ComponentUpgradePlan) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Images) DeepCopyInto(out *Images) { *out = *in @@ -79,3 +95,33 @@ func (in *Images) DeepCopyObject() runtime.Object { } return nil } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]ComponentUpgradePlan, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradePlan. +func (in *UpgradePlan) DeepCopy() *UpgradePlan { + if in == nil { + return nil + } + out := new(UpgradePlan) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UpgradePlan) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go index fb19e22ed7c..f4a57a4859b 100644 --- a/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go @@ -50,6 +50,22 @@ func (in *BootstrapToken) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComponentUpgradePlan) DeepCopyInto(out *ComponentUpgradePlan) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentUpgradePlan. +func (in *ComponentUpgradePlan) DeepCopy() *ComponentUpgradePlan { + if in == nil { + return nil + } + out := new(ComponentUpgradePlan) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Images) DeepCopyInto(out *Images) { *out = *in @@ -79,3 +95,33 @@ func (in *Images) DeepCopyObject() runtime.Object { } return nil } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]ComponentUpgradePlan, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradePlan. +func (in *UpgradePlan) DeepCopy() *UpgradePlan { + if in == nil { + return nil + } + out := new(UpgradePlan) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UpgradePlan) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} From 0eac66d647f3e0559305cb9dfb4d246192c8012d Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Wed, 4 Mar 2020 15:04:22 +0200 Subject: [PATCH 2/2] kubeadm: refactor printAvailableUpgrades Split printAvailableUpgrades into 2 functions: - genUpgradePlan that handles business logic - printUpgradePlan that outputs upgrade plan --- cmd/kubeadm/app/cmd/upgrade/BUILD | 1 + cmd/kubeadm/app/cmd/upgrade/plan.go | 247 +++++++++++++---------- cmd/kubeadm/app/cmd/upgrade/plan_test.go | 204 +++++++++---------- cmd/kubeadm/app/constants/constants.go | 6 + 4 files changed, 242 insertions(+), 216 deletions(-) diff --git a/cmd/kubeadm/app/cmd/upgrade/BUILD b/cmd/kubeadm/app/cmd/upgrade/BUILD index eab8937bad0..958b345a937 100644 --- a/cmd/kubeadm/app/cmd/upgrade/BUILD +++ b/cmd/kubeadm/app/cmd/upgrade/BUILD @@ -15,6 +15,7 @@ go_library( deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", + "//cmd/kubeadm/app/apis/output/v1alpha1:go_default_library", "//cmd/kubeadm/app/cmd/options:go_default_library", "//cmd/kubeadm/app/cmd/phases/upgrade/node:go_default_library", "//cmd/kubeadm/app/cmd/phases/workflow:go_default_library", diff --git a/cmd/kubeadm/app/cmd/upgrade/plan.go b/cmd/kubeadm/app/cmd/upgrade/plan.go index a4cb2be73fd..a07d8646a7d 100644 --- a/cmd/kubeadm/app/cmd/upgrade/plan.go +++ b/cmd/kubeadm/app/cmd/upgrade/plan.go @@ -29,6 +29,8 @@ import ( "k8s.io/apimachinery/pkg/util/version" "k8s.io/klog" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + 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" ) @@ -97,131 +99,154 @@ func runPlan(flags *planFlags, userVersion string) error { return errors.Wrap(err, "[upgrade/versions] FATAL") } - // Tell the user which upgrades are available - printAvailableUpgrades(availUpgrades, os.Stdout, isExternalEtcd) + // No upgrades available + if len(availUpgrades) == 0 { + klog.V(1).Infoln("[upgrade/plan] Awesome, you're up-to-date! Enjoy!") + return nil + } + + // Generate and print upgrade plans + for _, up := range availUpgrades { + plan, unstableVersionFlag, err := genUpgradePlan(&up, isExternalEtcd) + if err != nil { + return err + } + + printUpgradePlan(&up, plan, unstableVersionFlag, isExternalEtcd, os.Stdout) + } return nil } -// printAvailableUpgrades prints a UX-friendly overview of what versions are available to upgrade to -// TODO look into columnize or some other formatter when time permits instead of using the tabwriter -func printAvailableUpgrades(upgrades []upgrade.Upgrade, w io.Writer, isExternalEtcd bool) { - - // Return quickly if no upgrades can be made - if len(upgrades) == 0 { - fmt.Fprintln(w, "Awesome, you're up-to-date! Enjoy!") - return +// newComponentUpgradePlan helper creates outputapiv1alpha1.ComponentUpgradePlan object +func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha1.ComponentUpgradePlan { + return outputapiv1alpha1.ComponentUpgradePlan{ + Name: name, + CurrentVersion: currentVersion, + NewVersion: newVersion, } +} + +// TODO There is currently no way to cleanly output upgrades that involve adding, removing, or changing components +// https://github.com/kubernetes/kubeadm/issues/810 was created to track addressing this. +func appendDNSComponent(components []outputapiv1alpha1.ComponentUpgradePlan, up *upgrade.Upgrade, DNSType kubeadmapi.DNSAddOnType, name string) []outputapiv1alpha1.ComponentUpgradePlan { + beforeVersion, afterVersion := "", "" + if up.Before.DNSType == DNSType { + beforeVersion = up.Before.DNSVersion + } + if up.After.DNSType == DNSType { + afterVersion = up.After.DNSVersion + } + + if beforeVersion != "" || afterVersion != "" { + components = append(components, newComponentUpgradePlan(name, beforeVersion, afterVersion)) + } + return components +} + +// genUpgradePlan generates output-friendly upgrade plan out of upgrade.Upgrade structure +func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha1.UpgradePlan, string, error) { + newK8sVersion, err := version.ParseSemantic(up.After.KubeVersion) + if err != nil { + return nil, "", errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", up.After.KubeVersion) + } + + unstableVersionFlag := "" + if len(newK8sVersion.PreRelease()) != 0 { + if strings.HasPrefix(newK8sVersion.PreRelease(), "rc") { + unstableVersionFlag = " --allow-release-candidate-upgrades" + } else { + unstableVersionFlag = " --allow-experimental-upgrades" + } + } + + 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 :. 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 + for _, oldVersion := range sortedSliceFromStringIntMap(up.Before.KubeletVersions) { + nodeCount := up.Before.KubeletVersions[oldVersion] + components = append(components, newComponentUpgradePlan(constants.Kubelet, fmt.Sprintf("%d x %s", nodeCount, oldVersion), up.After.KubeVersion)) + } + } + + components = append(components, newComponentUpgradePlan(constants.KubeAPIServer, up.Before.KubeVersion, up.After.KubeVersion)) + components = append(components, newComponentUpgradePlan(constants.KubeControllerManager, up.Before.KubeVersion, up.After.KubeVersion)) + components = append(components, newComponentUpgradePlan(constants.KubeScheduler, up.Before.KubeVersion, up.After.KubeVersion)) + components = append(components, newComponentUpgradePlan(constants.KubeProxy, up.Before.KubeVersion, up.After.KubeVersion)) + + components = appendDNSComponent(components, up, kubeadmapi.CoreDNS, constants.CoreDNS) + components = appendDNSComponent(components, up, kubeadmapi.KubeDNS, constants.KubeDNS) + + if !isExternalEtcd { + components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion)) + } + + return &outputapiv1alpha1.UpgradePlan{Components: components}, unstableVersionFlag, nil +} + +// printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to +func printUpgradePlan(up *upgrade.Upgrade, plan *outputapiv1alpha1.UpgradePlan, unstableVersionFlag string, isExternalEtcd bool, w io.Writer) { // The tab writer writes to the "real" writer w tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0) - // Loop through the upgrade possibilities and output text to the command line - for _, upgrade := range upgrades { - - newK8sVersion, err := version.ParseSemantic(upgrade.After.KubeVersion) - if err != nil { - fmt.Fprintf(w, "Unable to parse normalized version %q as a semantic version\n", upgrade.After.KubeVersion) - continue - } - - UnstableVersionFlag := "" - if len(newK8sVersion.PreRelease()) != 0 { - if strings.HasPrefix(newK8sVersion.PreRelease(), "rc") { - UnstableVersionFlag = " --allow-release-candidate-upgrades" - } else { - UnstableVersionFlag = " --allow-experimental-upgrades" - } - } - - if isExternalEtcd && upgrade.CanUpgradeEtcd() { - 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, "Etcd\t%s\t%s\n", upgrade.Before.EtcdVersion, upgrade.After.EtcdVersion) - - // We should flush the writer here at this stage; as the columns will now be of the right size, adjusted to the above content - tabw.Flush() - fmt.Fprintln(w, "") - } - - if upgrade.CanUpgradeKubelets() { - fmt.Fprintln(w, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':") - fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE") - firstPrinted := false - - // The map is of the form :. 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 - for _, oldVersion := range sortedSliceFromStringIntMap(upgrade.Before.KubeletVersions) { - nodeCount := upgrade.Before.KubeletVersions[oldVersion] - if !firstPrinted { - // Output the Kubelet header only on the first version pair - fmt.Fprintf(tabw, "Kubelet\t%d x %s\t%s\n", nodeCount, oldVersion, upgrade.After.KubeVersion) - firstPrinted = true - continue - } - fmt.Fprintf(tabw, "\t%d x %s\t%s\n", nodeCount, oldVersion, upgrade.After.KubeVersion) - } - // We should flush the writer here at this stage; as the columns will now be of the right size, adjusted to the above content - tabw.Flush() - fmt.Fprintln(w, "") - } - - fmt.Fprintf(w, "Upgrade to the latest %s:\n", upgrade.Description) - fmt.Fprintln(w, "") - fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE") - fmt.Fprintf(tabw, "API Server\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion) - fmt.Fprintf(tabw, "Controller Manager\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion) - fmt.Fprintf(tabw, "Scheduler\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion) - fmt.Fprintf(tabw, "Kube Proxy\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion) - - // TODO There is currently no way to cleanly output upgrades that involve adding, removing, or changing components - // https://github.com/kubernetes/kubeadm/issues/810 was created to track addressing this. - printCoreDNS, printKubeDNS := false, false - coreDNSBeforeVersion, coreDNSAfterVersion, kubeDNSBeforeVersion, kubeDNSAfterVersion := "", "", "", "" - - switch upgrade.Before.DNSType { - case kubeadmapi.CoreDNS: - printCoreDNS = true - coreDNSBeforeVersion = upgrade.Before.DNSVersion - case kubeadmapi.KubeDNS: - printKubeDNS = true - kubeDNSBeforeVersion = upgrade.Before.DNSVersion - } - - switch upgrade.After.DNSType { - case kubeadmapi.CoreDNS: - printCoreDNS = true - coreDNSAfterVersion = upgrade.After.DNSVersion - case kubeadmapi.KubeDNS: - printKubeDNS = true - kubeDNSAfterVersion = upgrade.After.DNSVersion - } - - if printCoreDNS { - fmt.Fprintf(tabw, "CoreDNS\t%s\t%s\n", coreDNSBeforeVersion, coreDNSAfterVersion) - } - if printKubeDNS { - fmt.Fprintf(tabw, "Kube DNS\t%s\t%s\n", kubeDNSBeforeVersion, kubeDNSAfterVersion) - } - - if !isExternalEtcd { - fmt.Fprintf(tabw, "Etcd\t%s\t%s\n", upgrade.Before.EtcdVersion, upgrade.After.EtcdVersion) - } - - // The tabwriter should be flushed at this stage as we have now put in all the required content for this time. This is required for the tabs' size to be correct. + // endOfTable helper function flashes table writer + endOfTable := func() { tabw.Flush() fmt.Fprintln(w, "") - fmt.Fprintln(w, "You can now apply the upgrade by executing the following command:") - fmt.Fprintln(w, "") - fmt.Fprintf(w, "\tkubeadm upgrade apply %s%s\n", upgrade.After.KubeVersion, UnstableVersionFlag) - fmt.Fprintln(w, "") + } - if upgrade.Before.KubeadmVersion != upgrade.After.KubeadmVersion { - fmt.Fprintf(w, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", upgrade.After.KubeadmVersion) - fmt.Fprintln(w, "") + printHeader := true + 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() + } 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':") + fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE") + fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion) + printManualUpgradeHeader = false + } else { + fmt.Fprintf(tabw, "%s\t%s\t%s\n", "", component.CurrentVersion, component.NewVersion) + } + } else { + if printHeader { + // End of manual upgrades table + endOfTable() + + fmt.Fprintf(w, "Upgrade to the latest %s:\n", up.Description) + fmt.Fprintln(w, "") + fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE") + printHeader = false + } + fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion) } + } + // End of control plane table + endOfTable() - fmt.Fprintln(w, "_____________________________________________________________________") + //fmt.Fprintln(w, "") + fmt.Fprintln(w, "You can now apply the upgrade by executing the following command:") + fmt.Fprintln(w, "") + fmt.Fprintf(w, "\tkubeadm upgrade apply %s%s\n", up.After.KubeVersion, unstableVersionFlag) + fmt.Fprintln(w, "") + + if up.Before.KubeadmVersion != up.After.KubeadmVersion { + fmt.Fprintf(w, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", up.After.KubeadmVersion) fmt.Fprintln(w, "") } + + fmt.Fprintln(w, "_____________________________________________________________________") + fmt.Fprintln(w, "") } // sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically diff --git a/cmd/kubeadm/app/cmd/upgrade/plan_test.go b/cmd/kubeadm/app/cmd/upgrade/plan_test.go index 7f4c703607f..560e6aa6120 100644 --- a/cmd/kubeadm/app/cmd/upgrade/plan_test.go +++ b/cmd/kubeadm/app/cmd/upgrade/plan_test.go @@ -75,19 +75,6 @@ func TestPrintAvailableUpgrades(t *testing.T) { expectedBytes []byte externalEtcd bool }{ - { - name: "Up to date", - upgrades: []upgrade.Upgrade{}, - expectedBytes: []byte(`Awesome, you're up-to-date! Enjoy! -`), - }, - { - name: "Up to date external etcd", - externalEtcd: true, - upgrades: []upgrade.Upgrade{}, - expectedBytes: []byte(`Awesome, you're up-to-date! Enjoy! -`), - }, { name: "Patch version available", upgrades: []upgrade.Upgrade{ @@ -114,17 +101,17 @@ func TestPrintAvailableUpgrades(t *testing.T) { }, 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.8.1 v1.8.3 +kubelet 1 x v1.8.1 v1.8.3 Upgrade to the latest version in the v1.8 series: -COMPONENT CURRENT AVAILABLE -API Server v1.8.1 v1.8.3 -Controller Manager v1.8.1 v1.8.3 -Scheduler v1.8.1 v1.8.3 -Kube Proxy v1.8.1 v1.8.3 -Kube DNS 1.14.5 1.14.5 -Etcd 3.0.17 3.0.17 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.8.1 v1.8.3 +kube-controller-manager v1.8.1 v1.8.3 +kube-scheduler v1.8.1 v1.8.3 +kube-proxy v1.8.1 v1.8.3 +kube-dns 1.14.5 1.14.5 +etcd 3.0.17 3.0.17 You can now apply the upgrade by executing the following command: @@ -162,17 +149,17 @@ _____________________________________________________________________ }, 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.8.3 v1.9.0 +kubelet 1 x v1.8.3 v1.9.0 Upgrade to the latest stable version: -COMPONENT CURRENT AVAILABLE -API Server v1.8.3 v1.9.0 -Controller Manager v1.8.3 v1.9.0 -Scheduler v1.8.3 v1.9.0 -Kube Proxy v1.8.3 v1.9.0 -Kube DNS 1.14.5 1.14.13 -Etcd 3.0.17 3.1.12 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.8.3 v1.9.0 +kube-controller-manager v1.8.3 v1.9.0 +kube-scheduler v1.8.3 v1.9.0 +kube-proxy v1.8.3 v1.9.0 +kube-dns 1.14.5 1.14.13 +etcd 3.0.17 3.1.12 You can now apply the upgrade by executing the following command: @@ -228,17 +215,17 @@ _____________________________________________________________________ }, 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.8.3 v1.8.5 +kubelet 1 x v1.8.3 v1.8.5 Upgrade to the latest version in the v1.8 series: -COMPONENT CURRENT AVAILABLE -API Server v1.8.3 v1.8.5 -Controller Manager v1.8.3 v1.8.5 -Scheduler v1.8.3 v1.8.5 -Kube Proxy v1.8.3 v1.8.5 -Kube DNS 1.14.5 1.14.5 -Etcd 3.0.17 3.0.17 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.8.3 v1.8.5 +kube-controller-manager v1.8.3 v1.8.5 +kube-scheduler v1.8.3 v1.8.5 +kube-proxy v1.8.3 v1.8.5 +kube-dns 1.14.5 1.14.5 +etcd 3.0.17 3.0.17 You can now apply the upgrade by executing the following command: @@ -248,17 +235,17 @@ _____________________________________________________________________ Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': COMPONENT CURRENT AVAILABLE -Kubelet 1 x v1.8.3 v1.9.0 +kubelet 1 x v1.8.3 v1.9.0 Upgrade to the latest stable version: -COMPONENT CURRENT AVAILABLE -API Server v1.8.3 v1.9.0 -Controller Manager v1.8.3 v1.9.0 -Scheduler v1.8.3 v1.9.0 -Kube Proxy v1.8.3 v1.9.0 -Kube DNS 1.14.5 1.14.13 -Etcd 3.0.17 3.1.12 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.8.3 v1.9.0 +kube-controller-manager v1.8.3 v1.9.0 +kube-scheduler v1.8.3 v1.9.0 +kube-proxy v1.8.3 v1.9.0 +kube-dns 1.14.5 1.14.13 +etcd 3.0.17 3.1.12 You can now apply the upgrade by executing the following command: @@ -296,17 +283,17 @@ _____________________________________________________________________ }, 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.8.5 v1.9.0-beta.1 +kubelet 1 x v1.8.5 v1.9.0-beta.1 Upgrade to the latest experimental version: -COMPONENT CURRENT AVAILABLE -API Server v1.8.5 v1.9.0-beta.1 -Controller Manager v1.8.5 v1.9.0-beta.1 -Scheduler v1.8.5 v1.9.0-beta.1 -Kube Proxy v1.8.5 v1.9.0-beta.1 -Kube DNS 1.14.5 1.14.13 -Etcd 3.0.17 3.1.12 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.8.5 v1.9.0-beta.1 +kube-controller-manager v1.8.5 v1.9.0-beta.1 +kube-scheduler v1.8.5 v1.9.0-beta.1 +kube-proxy v1.8.5 v1.9.0-beta.1 +kube-dns 1.14.5 1.14.13 +etcd 3.0.17 3.1.12 You can now apply the upgrade by executing the following command: @@ -344,17 +331,17 @@ _____________________________________________________________________ }, 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.8.5 v1.9.0-rc.1 +kubelet 1 x v1.8.5 v1.9.0-rc.1 Upgrade to the latest release candidate version: -COMPONENT CURRENT AVAILABLE -API Server v1.8.5 v1.9.0-rc.1 -Controller Manager v1.8.5 v1.9.0-rc.1 -Scheduler v1.8.5 v1.9.0-rc.1 -Kube Proxy v1.8.5 v1.9.0-rc.1 -Kube DNS 1.14.5 1.14.13 -Etcd 3.0.17 3.1.12 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.8.5 v1.9.0-rc.1 +kube-controller-manager v1.8.5 v1.9.0-rc.1 +kube-scheduler v1.8.5 v1.9.0-rc.1 +kube-proxy v1.8.5 v1.9.0-rc.1 +kube-dns 1.14.5 1.14.13 +etcd 3.0.17 3.1.12 You can now apply the upgrade by executing the following command: @@ -393,18 +380,18 @@ _____________________________________________________________________ }, 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 +kubelet 1 x v1.9.2 v1.9.3 2 x v1.9.3 v1.9.3 Upgrade to the latest version in the v1.9 series: -COMPONENT CURRENT AVAILABLE -API Server v1.9.2 v1.9.3 -Controller Manager v1.9.2 v1.9.3 -Scheduler v1.9.2 v1.9.3 -Kube Proxy v1.9.2 v1.9.3 -Kube DNS 1.14.5 1.14.8 -Etcd 3.0.17 3.1.12 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.9.2 v1.9.3 +kube-controller-manager v1.9.2 v1.9.3 +kube-scheduler v1.9.2 v1.9.3 +kube-proxy v1.9.2 v1.9.3 +kube-dns 1.14.5 1.14.8 +etcd 3.0.17 3.1.12 You can now apply the upgrade by executing the following command: @@ -444,20 +431,20 @@ _____________________________________________________________________ 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 +etcd 3.0.17 3.1.12 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 +kubelet 1 x v1.9.2 v1.9.3 Upgrade to the latest version in the v1.9 series: -COMPONENT CURRENT AVAILABLE -API Server v1.9.2 v1.9.3 -Controller Manager v1.9.2 v1.9.3 -Scheduler v1.9.2 v1.9.3 -Kube Proxy v1.9.2 v1.9.3 -Kube DNS 1.14.5 1.14.8 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.9.2 v1.9.3 +kube-controller-manager v1.9.2 v1.9.3 +kube-scheduler v1.9.2 v1.9.3 +kube-proxy v1.9.2 v1.9.3 +kube-dns 1.14.5 1.14.8 You can now apply the upgrade by executing the following command: @@ -495,18 +482,18 @@ _____________________________________________________________________ }, 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.10.2 v1.11.0 +kubelet 1 x v1.10.2 v1.11.0 Upgrade to the latest kubedns to coredns: -COMPONENT CURRENT AVAILABLE -API Server v1.10.2 v1.11.0 -Controller Manager v1.10.2 v1.11.0 -Scheduler v1.10.2 v1.11.0 -Kube Proxy v1.10.2 v1.11.0 -CoreDNS 1.0.6 -Kube DNS 1.14.7 -Etcd 3.1.11 3.2.18 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.10.2 v1.11.0 +kube-controller-manager v1.10.2 v1.11.0 +kube-scheduler v1.10.2 v1.11.0 +kube-proxy v1.10.2 v1.11.0 +CoreDNS 1.0.6 +kube-dns 1.14.7 +etcd 3.1.11 3.2.18 You can now apply the upgrade by executing the following command: @@ -542,17 +529,17 @@ _____________________________________________________________________ }, 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.10.2 v1.11.0 +kubelet 1 x v1.10.2 v1.11.0 Upgrade to the latest coredns: -COMPONENT CURRENT AVAILABLE -API Server v1.10.2 v1.11.0 -Controller Manager v1.10.2 v1.11.0 -Scheduler v1.10.2 v1.11.0 -Kube Proxy v1.10.2 v1.11.0 -CoreDNS 1.0.5 1.0.6 -Etcd 3.1.11 3.2.18 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.10.2 v1.11.0 +kube-controller-manager v1.10.2 v1.11.0 +kube-scheduler v1.10.2 v1.11.0 +kube-proxy v1.10.2 v1.11.0 +CoreDNS 1.0.5 1.0.6 +etcd 3.1.11 3.2.18 You can now apply the upgrade by executing the following command: @@ -588,18 +575,18 @@ _____________________________________________________________________ }, 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.10.2 v1.11.0 +kubelet 1 x v1.10.2 v1.11.0 Upgrade to the latest coredns to kubedns: -COMPONENT CURRENT AVAILABLE -API Server v1.10.2 v1.11.0 -Controller Manager v1.10.2 v1.11.0 -Scheduler v1.10.2 v1.11.0 -Kube Proxy v1.10.2 v1.11.0 -CoreDNS 1.0.6 -Kube DNS 1.14.9 -Etcd 3.1.11 3.2.18 +COMPONENT CURRENT AVAILABLE +kube-apiserver v1.10.2 v1.11.0 +kube-controller-manager v1.10.2 v1.11.0 +kube-scheduler v1.10.2 v1.11.0 +kube-proxy v1.10.2 v1.11.0 +CoreDNS 1.0.6 +kube-dns 1.14.9 +etcd 3.1.11 3.2.18 You can now apply the upgrade by executing the following command: @@ -613,11 +600,18 @@ _____________________________________________________________________ for _, rt := range tests { t.Run(rt.name, func(t *testing.T) { rt.buf = bytes.NewBufferString("") - printAvailableUpgrades(rt.upgrades, rt.buf, rt.externalEtcd) + // Generate and print upgrade plans + for _, up := range rt.upgrades { + plan, unstableVersionFlag, err := genUpgradePlan(&up, rt.externalEtcd) + if err != nil { + t.Errorf("failed genUpgradePlan, err: %+v", err) + } + printUpgradePlan(&up, plan, unstableVersionFlag, rt.externalEtcd, rt.buf) + } actualBytes := rt.buf.Bytes() if !bytes.Equal(actualBytes, rt.expectedBytes) { t.Errorf( - "failed PrintAvailableUpgrades:\n\texpected: %q\n\t actual: %q", + "failed PrintAvailableUpgrades:\n\texpected: %q\n\n\tactual : %q", string(rt.expectedBytes), string(actualBytes), ) diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index a309708c119..34508d1d784 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -279,6 +279,12 @@ const ( KubeProxy = "kube-proxy" // HyperKube defines variable used internally when referring to the hyperkube image HyperKube = "hyperkube" + // CoreDNS defines variable used internally when referring to the CoreDNS component + CoreDNS = "CoreDNS" + // KubeDNS defines variable used internally when referring to the KubeDNS component + KubeDNS = "kube-dns" + // Kubelet defines variable used internally when referring to the Kubelet + Kubelet = "kubelet" // SelfHostingPrefix describes the prefix workloads that are self-hosted by kubeadm has SelfHostingPrefix = "self-hosted-"