@@ -29,6 +29,8 @@ import (
29
29
"k8s.io/apimachinery/pkg/util/version"
30
30
"k8s.io/klog"
31
31
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"
32
34
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
33
35
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
34
36
)
@@ -97,131 +99,154 @@ func runPlan(flags *planFlags, userVersion string) error {
97
99
return errors .Wrap (err , "[upgrade/versions] FATAL" )
98
100
}
99
101
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
+ }
102
117
return nil
103
118
}
104
119
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
+ }
108
128
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
113
138
}
114
- // The tab writer writes to the "real" writer w
115
- tabw := tabwriter .NewWriter (w , 10 , 4 , 3 , ' ' , 0 )
116
139
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
+ }
119
145
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
+ }
125
152
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"
133
159
}
160
+ }
134
161
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\t CURRENT\t AVAILABLE" )
138
- fmt .Fprintf (tabw , "Etcd\t %s\t %s\n " , upgrade .Before .EtcdVersion , upgrade .After .EtcdVersion )
162
+ components := []outputapiv1alpha1.ComponentUpgradePlan {}
139
163
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
+ }
144
167
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\t CURRENT\t AVAILABLE" )
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 ))
165
174
}
175
+ }
166
176
167
- fmt .Fprintf (w , "Upgrade to the latest %s:\n " , upgrade .Description )
168
- fmt .Fprintln (w , "" )
169
- fmt .Fprintln (tabw , "COMPONENT\t CURRENT\t AVAILABLE" )
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 ))
188
181
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 )
197
184
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
+ }
204
188
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 )
208
196
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 () {
210
199
tabw .Flush ()
211
200
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 , "\t kubeadm upgrade apply %s%s\n " , upgrade .After .KubeVersion , UnstableVersionFlag )
215
- fmt .Fprintln (w , "" )
201
+ }
216
202
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\t CURRENT\t AVAILABLE" )
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\t CURRENT\t AVAILABLE" )
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\t CURRENT\t AVAILABLE" )
229
+ printHeader = false
230
+ }
231
+ fmt .Fprintf (tabw , "%s\t %s\t %s\n " , component .Name , component .CurrentVersion , component .NewVersion )
220
232
}
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 , "\t kubeadm upgrade apply %s%s\n " , up .After .KubeVersion , unstableVersionFlag )
241
+ fmt .Fprintln (w , "" )
221
242
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 )
223
245
fmt .Fprintln (w , "" )
224
246
}
247
+
248
+ fmt .Fprintln (w , "_____________________________________________________________________" )
249
+ fmt .Fprintln (w , "" )
225
250
}
226
251
227
252
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically
0 commit comments