Skip to content

Commit 0eac66d

Browse files
committed
kubeadm: refactor printAvailableUpgrades
Split printAvailableUpgrades into 2 functions: - genUpgradePlan that handles business logic - printUpgradePlan that outputs upgrade plan
1 parent e5d6536 commit 0eac66d

File tree

4 files changed

+231
-205
lines changed

4 files changed

+231
-205
lines changed

cmd/kubeadm/app/cmd/upgrade/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ go_library(
1515
deps = [
1616
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
1717
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
18+
"//cmd/kubeadm/app/apis/output/v1alpha1:go_default_library",
1819
"//cmd/kubeadm/app/cmd/options:go_default_library",
1920
"//cmd/kubeadm/app/cmd/phases/upgrade/node:go_default_library",
2021
"//cmd/kubeadm/app/cmd/phases/workflow:go_default_library",

cmd/kubeadm/app/cmd/upgrade/plan.go

Lines changed: 125 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
"k8s.io/apimachinery/pkg/util/version"
3030
"k8s.io/klog"
3131
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
32+
outputapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha1"
33+
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
3234
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
3335
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
3436
)
@@ -97,131 +99,154 @@ func runPlan(flags *planFlags, userVersion string) error {
9799
return errors.Wrap(err, "[upgrade/versions] FATAL")
98100
}
99101

100-
// Tell the user which upgrades are available
101-
printAvailableUpgrades(availUpgrades, os.Stdout, isExternalEtcd)
102+
// No upgrades available
103+
if len(availUpgrades) == 0 {
104+
klog.V(1).Infoln("[upgrade/plan] Awesome, you're up-to-date! Enjoy!")
105+
return nil
106+
}
107+
108+
// Generate and print upgrade plans
109+
for _, up := range availUpgrades {
110+
plan, unstableVersionFlag, err := genUpgradePlan(&up, isExternalEtcd)
111+
if err != nil {
112+
return err
113+
}
114+
115+
printUpgradePlan(&up, plan, unstableVersionFlag, isExternalEtcd, os.Stdout)
116+
}
102117
return nil
103118
}
104119

105-
// printAvailableUpgrades prints a UX-friendly overview of what versions are available to upgrade to
106-
// TODO look into columnize or some other formatter when time permits instead of using the tabwriter
107-
func printAvailableUpgrades(upgrades []upgrade.Upgrade, w io.Writer, isExternalEtcd bool) {
120+
// newComponentUpgradePlan helper creates outputapiv1alpha1.ComponentUpgradePlan object
121+
func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha1.ComponentUpgradePlan {
122+
return outputapiv1alpha1.ComponentUpgradePlan{
123+
Name: name,
124+
CurrentVersion: currentVersion,
125+
NewVersion: newVersion,
126+
}
127+
}
108128

109-
// Return quickly if no upgrades can be made
110-
if len(upgrades) == 0 {
111-
fmt.Fprintln(w, "Awesome, you're up-to-date! Enjoy!")
112-
return
129+
// TODO There is currently no way to cleanly output upgrades that involve adding, removing, or changing components
130+
// https://github.com/kubernetes/kubeadm/issues/810 was created to track addressing this.
131+
func appendDNSComponent(components []outputapiv1alpha1.ComponentUpgradePlan, up *upgrade.Upgrade, DNSType kubeadmapi.DNSAddOnType, name string) []outputapiv1alpha1.ComponentUpgradePlan {
132+
beforeVersion, afterVersion := "", ""
133+
if up.Before.DNSType == DNSType {
134+
beforeVersion = up.Before.DNSVersion
135+
}
136+
if up.After.DNSType == DNSType {
137+
afterVersion = up.After.DNSVersion
113138
}
114-
// The tab writer writes to the "real" writer w
115-
tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0)
116139

117-
// Loop through the upgrade possibilities and output text to the command line
118-
for _, upgrade := range upgrades {
140+
if beforeVersion != "" || afterVersion != "" {
141+
components = append(components, newComponentUpgradePlan(name, beforeVersion, afterVersion))
142+
}
143+
return components
144+
}
119145

120-
newK8sVersion, err := version.ParseSemantic(upgrade.After.KubeVersion)
121-
if err != nil {
122-
fmt.Fprintf(w, "Unable to parse normalized version %q as a semantic version\n", upgrade.After.KubeVersion)
123-
continue
124-
}
146+
// genUpgradePlan generates output-friendly upgrade plan out of upgrade.Upgrade structure
147+
func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha1.UpgradePlan, string, error) {
148+
newK8sVersion, err := version.ParseSemantic(up.After.KubeVersion)
149+
if err != nil {
150+
return nil, "", errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", up.After.KubeVersion)
151+
}
125152

126-
UnstableVersionFlag := ""
127-
if len(newK8sVersion.PreRelease()) != 0 {
128-
if strings.HasPrefix(newK8sVersion.PreRelease(), "rc") {
129-
UnstableVersionFlag = " --allow-release-candidate-upgrades"
130-
} else {
131-
UnstableVersionFlag = " --allow-experimental-upgrades"
132-
}
153+
unstableVersionFlag := ""
154+
if len(newK8sVersion.PreRelease()) != 0 {
155+
if strings.HasPrefix(newK8sVersion.PreRelease(), "rc") {
156+
unstableVersionFlag = " --allow-release-candidate-upgrades"
157+
} else {
158+
unstableVersionFlag = " --allow-experimental-upgrades"
133159
}
160+
}
134161

135-
if isExternalEtcd && upgrade.CanUpgradeEtcd() {
136-
fmt.Fprintln(w, "External components that should be upgraded manually before you upgrade the control plane with 'kubeadm upgrade apply':")
137-
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
138-
fmt.Fprintf(tabw, "Etcd\t%s\t%s\n", upgrade.Before.EtcdVersion, upgrade.After.EtcdVersion)
162+
components := []outputapiv1alpha1.ComponentUpgradePlan{}
139163

140-
// We should flush the writer here at this stage; as the columns will now be of the right size, adjusted to the above content
141-
tabw.Flush()
142-
fmt.Fprintln(w, "")
143-
}
164+
if isExternalEtcd && up.CanUpgradeEtcd() {
165+
components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion))
166+
}
144167

145-
if upgrade.CanUpgradeKubelets() {
146-
fmt.Fprintln(w, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':")
147-
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
148-
firstPrinted := false
149-
150-
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted
151-
// in order to always get the right order. Then the map value is extracted separately
152-
for _, oldVersion := range sortedSliceFromStringIntMap(upgrade.Before.KubeletVersions) {
153-
nodeCount := upgrade.Before.KubeletVersions[oldVersion]
154-
if !firstPrinted {
155-
// Output the Kubelet header only on the first version pair
156-
fmt.Fprintf(tabw, "Kubelet\t%d x %s\t%s\n", nodeCount, oldVersion, upgrade.After.KubeVersion)
157-
firstPrinted = true
158-
continue
159-
}
160-
fmt.Fprintf(tabw, "\t%d x %s\t%s\n", nodeCount, oldVersion, upgrade.After.KubeVersion)
161-
}
162-
// We should flush the writer here at this stage; as the columns will now be of the right size, adjusted to the above content
163-
tabw.Flush()
164-
fmt.Fprintln(w, "")
168+
if up.CanUpgradeKubelets() {
169+
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted
170+
// in order to always get the right order. Then the map value is extracted separately
171+
for _, oldVersion := range sortedSliceFromStringIntMap(up.Before.KubeletVersions) {
172+
nodeCount := up.Before.KubeletVersions[oldVersion]
173+
components = append(components, newComponentUpgradePlan(constants.Kubelet, fmt.Sprintf("%d x %s", nodeCount, oldVersion), up.After.KubeVersion))
165174
}
175+
}
166176

167-
fmt.Fprintf(w, "Upgrade to the latest %s:\n", upgrade.Description)
168-
fmt.Fprintln(w, "")
169-
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
170-
fmt.Fprintf(tabw, "API Server\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
171-
fmt.Fprintf(tabw, "Controller Manager\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
172-
fmt.Fprintf(tabw, "Scheduler\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
173-
fmt.Fprintf(tabw, "Kube Proxy\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion)
174-
175-
// TODO There is currently no way to cleanly output upgrades that involve adding, removing, or changing components
176-
// https://github.com/kubernetes/kubeadm/issues/810 was created to track addressing this.
177-
printCoreDNS, printKubeDNS := false, false
178-
coreDNSBeforeVersion, coreDNSAfterVersion, kubeDNSBeforeVersion, kubeDNSAfterVersion := "", "", "", ""
179-
180-
switch upgrade.Before.DNSType {
181-
case kubeadmapi.CoreDNS:
182-
printCoreDNS = true
183-
coreDNSBeforeVersion = upgrade.Before.DNSVersion
184-
case kubeadmapi.KubeDNS:
185-
printKubeDNS = true
186-
kubeDNSBeforeVersion = upgrade.Before.DNSVersion
187-
}
177+
components = append(components, newComponentUpgradePlan(constants.KubeAPIServer, up.Before.KubeVersion, up.After.KubeVersion))
178+
components = append(components, newComponentUpgradePlan(constants.KubeControllerManager, up.Before.KubeVersion, up.After.KubeVersion))
179+
components = append(components, newComponentUpgradePlan(constants.KubeScheduler, up.Before.KubeVersion, up.After.KubeVersion))
180+
components = append(components, newComponentUpgradePlan(constants.KubeProxy, up.Before.KubeVersion, up.After.KubeVersion))
188181

189-
switch upgrade.After.DNSType {
190-
case kubeadmapi.CoreDNS:
191-
printCoreDNS = true
192-
coreDNSAfterVersion = upgrade.After.DNSVersion
193-
case kubeadmapi.KubeDNS:
194-
printKubeDNS = true
195-
kubeDNSAfterVersion = upgrade.After.DNSVersion
196-
}
182+
components = appendDNSComponent(components, up, kubeadmapi.CoreDNS, constants.CoreDNS)
183+
components = appendDNSComponent(components, up, kubeadmapi.KubeDNS, constants.KubeDNS)
197184

198-
if printCoreDNS {
199-
fmt.Fprintf(tabw, "CoreDNS\t%s\t%s\n", coreDNSBeforeVersion, coreDNSAfterVersion)
200-
}
201-
if printKubeDNS {
202-
fmt.Fprintf(tabw, "Kube DNS\t%s\t%s\n", kubeDNSBeforeVersion, kubeDNSAfterVersion)
203-
}
185+
if !isExternalEtcd {
186+
components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion))
187+
}
204188

205-
if !isExternalEtcd {
206-
fmt.Fprintf(tabw, "Etcd\t%s\t%s\n", upgrade.Before.EtcdVersion, upgrade.After.EtcdVersion)
207-
}
189+
return &outputapiv1alpha1.UpgradePlan{Components: components}, unstableVersionFlag, nil
190+
}
191+
192+
// printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to
193+
func printUpgradePlan(up *upgrade.Upgrade, plan *outputapiv1alpha1.UpgradePlan, unstableVersionFlag string, isExternalEtcd bool, w io.Writer) {
194+
// The tab writer writes to the "real" writer w
195+
tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0)
208196

209-
// 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.
197+
// endOfTable helper function flashes table writer
198+
endOfTable := func() {
210199
tabw.Flush()
211200
fmt.Fprintln(w, "")
212-
fmt.Fprintln(w, "You can now apply the upgrade by executing the following command:")
213-
fmt.Fprintln(w, "")
214-
fmt.Fprintf(w, "\tkubeadm upgrade apply %s%s\n", upgrade.After.KubeVersion, UnstableVersionFlag)
215-
fmt.Fprintln(w, "")
201+
}
216202

217-
if upgrade.Before.KubeadmVersion != upgrade.After.KubeadmVersion {
218-
fmt.Fprintf(w, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", upgrade.After.KubeadmVersion)
219-
fmt.Fprintln(w, "")
203+
printHeader := true
204+
printManualUpgradeHeader := true
205+
for _, component := range plan.Components {
206+
if isExternalEtcd && component.Name == constants.Etcd {
207+
fmt.Fprintln(w, "External components that should be upgraded manually before you upgrade the control plane with 'kubeadm upgrade apply':")
208+
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
209+
fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
210+
// end of external components table
211+
endOfTable()
212+
} else if component.Name == constants.Kubelet {
213+
if printManualUpgradeHeader {
214+
fmt.Fprintln(w, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':")
215+
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
216+
fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
217+
printManualUpgradeHeader = false
218+
} else {
219+
fmt.Fprintf(tabw, "%s\t%s\t%s\n", "", component.CurrentVersion, component.NewVersion)
220+
}
221+
} else {
222+
if printHeader {
223+
// End of manual upgrades table
224+
endOfTable()
225+
226+
fmt.Fprintf(w, "Upgrade to the latest %s:\n", up.Description)
227+
fmt.Fprintln(w, "")
228+
fmt.Fprintln(tabw, "COMPONENT\tCURRENT\tAVAILABLE")
229+
printHeader = false
230+
}
231+
fmt.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
220232
}
233+
}
234+
// End of control plane table
235+
endOfTable()
236+
237+
//fmt.Fprintln(w, "")
238+
fmt.Fprintln(w, "You can now apply the upgrade by executing the following command:")
239+
fmt.Fprintln(w, "")
240+
fmt.Fprintf(w, "\tkubeadm upgrade apply %s%s\n", up.After.KubeVersion, unstableVersionFlag)
241+
fmt.Fprintln(w, "")
221242

222-
fmt.Fprintln(w, "_____________________________________________________________________")
243+
if up.Before.KubeadmVersion != up.After.KubeadmVersion {
244+
fmt.Fprintf(w, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", up.After.KubeadmVersion)
223245
fmt.Fprintln(w, "")
224246
}
247+
248+
fmt.Fprintln(w, "_____________________________________________________________________")
249+
fmt.Fprintln(w, "")
225250
}
226251

227252
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically

0 commit comments

Comments
 (0)