@@ -19,20 +19,25 @@ package upgrade
19
19
import (
20
20
"fmt"
21
21
"io"
22
+ "io/ioutil"
22
23
"os"
23
24
"sort"
24
25
"strings"
25
26
"text/tabwriter"
26
27
28
+ "github.com/lithammer/dedent"
27
29
"github.com/pkg/errors"
28
30
"github.com/spf13/cobra"
29
31
30
32
"k8s.io/apimachinery/pkg/util/version"
33
+ clientset "k8s.io/client-go/kubernetes"
31
34
"k8s.io/klog/v2"
32
35
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
33
36
outputapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
37
+ "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
34
38
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
35
39
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
40
+ kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
36
41
)
37
42
38
43
type planFlags struct {
@@ -79,6 +84,13 @@ func runPlan(flags *planFlags, args []string) error {
79
84
return errors .Wrap (err , "[upgrade/versions] FATAL" )
80
85
}
81
86
87
+ // Fetch the current state of the component configs
88
+ klog .V (1 ).Infoln ("[upgrade/plan] analysing component config version states" )
89
+ configVersionStates , err := getComponentConfigVersionStates (& cfg .ClusterConfiguration , client , flags .cfgPath )
90
+ if err != nil {
91
+ return errors .WithMessage (err , "[upgrade/versions] FATAL" )
92
+ }
93
+
82
94
// No upgrades available
83
95
if len (availUpgrades ) == 0 {
84
96
klog .V (1 ).Infoln ("[upgrade/plan] Awesome, you're up-to-date! Enjoy!" )
@@ -92,8 +104,17 @@ func runPlan(flags *planFlags, args []string) error {
92
104
return err
93
105
}
94
106
107
+ // Actually, this is needed for machine readable output only.
108
+ // printUpgradePlan won't output the configVersionStates as it will simply print the same table several times
109
+ // in the human readable output if it did so
110
+ plan .ConfigVersions = configVersionStates
111
+
95
112
printUpgradePlan (& up , plan , unstableVersionFlag , isExternalEtcd , os .Stdout )
96
113
}
114
+
115
+ // Finally, print the component config state table
116
+ printComponentConfigVersionStates (configVersionStates , os .Stdout )
117
+
97
118
return nil
98
119
}
99
120
@@ -165,6 +186,24 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapi.Upgrad
165
186
return & outputapi.UpgradePlan {Components : components }, unstableVersionFlag , nil
166
187
}
167
188
189
+ func getComponentConfigVersionStates (cfg * kubeadmapi.ClusterConfiguration , client clientset.Interface , cfgPath string ) ([]outputapi.ComponentConfigVersionState , error ) {
190
+ docmap := kubeadmapi.DocumentMap {}
191
+
192
+ if cfgPath != "" {
193
+ bytes , err := ioutil .ReadFile (cfgPath )
194
+ if err != nil {
195
+ return nil , errors .Wrapf (err , "unable to read config file %q" , cfgPath )
196
+ }
197
+
198
+ docmap , err = kubeadmutil .SplitYAMLDocuments (bytes )
199
+ if err != nil {
200
+ return nil , err
201
+ }
202
+ }
203
+
204
+ return componentconfigs .GetVersionStates (cfg , client , docmap )
205
+ }
206
+
168
207
// printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to
169
208
func printUpgradePlan (up * upgrade.Upgrade , plan * outputapi.UpgradePlan , unstableVersionFlag string , isExternalEtcd bool , w io.Writer ) {
170
209
// The tab writer writes to the "real" writer w
@@ -218,8 +257,7 @@ func printUpgradePlan(up *upgrade.Upgrade, plan *outputapi.UpgradePlan, unstable
218
257
fmt .Fprintln (w , "" )
219
258
}
220
259
221
- fmt .Fprintln (w , "_____________________________________________________________________" )
222
- fmt .Fprintln (w , "" )
260
+ printLineSeparator (w )
223
261
}
224
262
225
263
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically
@@ -231,3 +269,52 @@ func sortedSliceFromStringIntMap(strMap map[string]uint16) []string {
231
269
sort .Strings (strSlice )
232
270
return strSlice
233
271
}
272
+
273
+ func strOrDash (s string ) string {
274
+ if s != "" {
275
+ return s
276
+ }
277
+ return "-"
278
+ }
279
+
280
+ func yesOrNo (b bool ) string {
281
+ if b {
282
+ return "yes"
283
+ }
284
+ return "no"
285
+ }
286
+
287
+ func printLineSeparator (w io.Writer ) {
288
+ fmt .Fprintln (w , "_____________________________________________________________________" )
289
+ fmt .Fprintln (w , "" )
290
+ }
291
+
292
+ func printComponentConfigVersionStates (versionStates []outputapi.ComponentConfigVersionState , w io.Writer ) {
293
+ if len (versionStates ) == 0 {
294
+ fmt .Fprintln (w , "No information available on component configs." )
295
+ return
296
+ }
297
+
298
+ fmt .Fprintln (w , dedent .Dedent (`
299
+ The table below shows the current state of component configs as understood by this version of kubeadm.
300
+ Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or
301
+ resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually
302
+ upgrade to is denoted in the "PREFERRED VERSION" column.
303
+ ` ))
304
+
305
+ tabw := tabwriter .NewWriter (w , 10 , 4 , 3 , ' ' , 0 )
306
+ fmt .Fprintln (tabw , "API GROUP\t CURRENT VERSION\t PREFERRED VERSION\t MANUAL UPGRADE REQUIRED" )
307
+
308
+ for _ , state := range versionStates {
309
+ fmt .Fprintf (tabw ,
310
+ "%s\t %s\t %s\t %s\n " ,
311
+ state .Group ,
312
+ strOrDash (state .CurrentVersion ),
313
+ strOrDash (state .PreferredVersion ),
314
+ yesOrNo (state .ManualUpgradeRequired ),
315
+ )
316
+ }
317
+
318
+ tabw .Flush ()
319
+ printLineSeparator (w )
320
+ }
0 commit comments