From 22fb3be96d31583cf6faba470a085c01135e733f Mon Sep 17 00:00:00 2001 From: Paco Xu Date: Thu, 17 Mar 2022 11:49:08 +0800 Subject: [PATCH] cleanup: kubeadm upgrade plan supports json/yaml output Co-authored-by: Lubomir I. Ivanov --- cmd/kubeadm/app/apis/output/types.go | 2 +- cmd/kubeadm/app/apis/output/v1alpha2/types.go | 2 +- .../v1alpha2/zz_generated.conversion.go | 4 +- .../output/v1alpha2/zz_generated.deepcopy.go | 10 +-- .../app/apis/output/zz_generated.deepcopy.go | 10 +-- cmd/kubeadm/app/cmd/certs.go | 3 +- cmd/kubeadm/app/cmd/config.go | 4 +- cmd/kubeadm/app/cmd/join.go | 3 +- cmd/kubeadm/app/cmd/reset.go | 3 +- cmd/kubeadm/app/cmd/token.go | 4 +- cmd/kubeadm/app/cmd/upgrade/common.go | 4 +- cmd/kubeadm/app/cmd/upgrade/diff.go | 3 +- cmd/kubeadm/app/cmd/upgrade/node.go | 3 +- cmd/kubeadm/app/cmd/upgrade/plan.go | 88 +++++++++++-------- cmd/kubeadm/app/componentconfigs/configset.go | 2 +- cmd/kubeadm/app/phases/upgrade/compute.go | 2 +- cmd/kubeadm/app/util/config/cluster.go | 3 + cmd/kubeadm/app/util/output/output.go | 55 +++++++----- 18 files changed, 106 insertions(+), 99 deletions(-) diff --git a/cmd/kubeadm/app/apis/output/types.go b/cmd/kubeadm/app/apis/output/types.go index d0229a28e04..9dda9491425 100644 --- a/cmd/kubeadm/app/apis/output/types.go +++ b/cmd/kubeadm/app/apis/output/types.go @@ -78,7 +78,7 @@ type ComponentConfigVersionState struct { type UpgradePlan struct { metav1.TypeMeta - Components []*ComponentUpgradePlan + Components []ComponentUpgradePlan ConfigVersions []ComponentConfigVersionState } diff --git a/cmd/kubeadm/app/apis/output/v1alpha2/types.go b/cmd/kubeadm/app/apis/output/v1alpha2/types.go index 7938849a356..529c029dd59 100644 --- a/cmd/kubeadm/app/apis/output/v1alpha2/types.go +++ b/cmd/kubeadm/app/apis/output/v1alpha2/types.go @@ -78,7 +78,7 @@ type ComponentConfigVersionState struct { type UpgradePlan struct { metav1.TypeMeta - Components []*ComponentUpgradePlan `json:"components"` + Components []ComponentUpgradePlan `json:"components"` ConfigVersions []ComponentConfigVersionState `json:"configVersions"` } diff --git a/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.conversion.go b/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.conversion.go index 005b39773fb..5893ba6c309 100644 --- a/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.conversion.go @@ -180,7 +180,7 @@ 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 { - out.Components = *(*[]*output.ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) + out.Components = *(*[]output.ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) out.ConfigVersions = *(*[]output.ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions)) return nil } @@ -191,7 +191,7 @@ 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 { - out.Components = *(*[]*ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) + out.Components = *(*[]ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) out.ConfigVersions = *(*[]ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions)) return nil } diff --git a/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.deepcopy.go index dec35b0d537..73e8c32def2 100644 --- a/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/output/v1alpha2/zz_generated.deepcopy.go @@ -128,14 +128,8 @@ func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) { out.TypeMeta = in.TypeMeta if in.Components != nil { in, out := &in.Components, &out.Components - *out = make([]*ComponentUpgradePlan, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(ComponentUpgradePlan) - **out = **in - } - } + *out = make([]ComponentUpgradePlan, len(*in)) + copy(*out, *in) } if in.ConfigVersions != nil { in, out := &in.ConfigVersions, &out.ConfigVersions diff --git a/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go index d09de881e1c..8d553d35c15 100644 --- a/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go @@ -128,14 +128,8 @@ func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) { out.TypeMeta = in.TypeMeta if in.Components != nil { in, out := &in.Components, &out.Components - *out = make([]*ComponentUpgradePlan, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(ComponentUpgradePlan) - **out = **in - } - } + *out = make([]ComponentUpgradePlan, len(*in)) + copy(*out, *in) } if in.ConfigVersions != nil { in, out := &in.ConfigVersions, &out.ConfigVersions diff --git a/cmd/kubeadm/app/cmd/certs.go b/cmd/kubeadm/app/cmd/certs.go index a5265b4b1c3..62cf2fcdf0c 100644 --- a/cmd/kubeadm/app/cmd/certs.go +++ b/cmd/kubeadm/app/cmd/certs.go @@ -39,7 +39,6 @@ import ( kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" - "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) var ( @@ -333,7 +332,7 @@ func getInternalCfg(cfgPath string, kubeconfigPath string, cfg kubeadmapiv1.Clus if cfgPath == "" { client, err := kubeconfigutil.ClientSetFromFile(kubeconfigPath) if err == nil { - internalcfg, err := configutil.FetchInitConfigurationFromCluster(client, &output.TextPrinter{}, logPrefix, false, false) + internalcfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, logPrefix, false, false) if err == nil { fmt.Println() // add empty line to separate the FetchInitConfigurationFromCluster output from the command output return internalcfg, nil diff --git a/cmd/kubeadm/app/cmd/config.go b/cmd/kubeadm/app/cmd/config.go index 9994b80d79d..99e68706b4f 100644 --- a/cmd/kubeadm/app/cmd/config.go +++ b/cmd/kubeadm/app/cmd/config.go @@ -371,7 +371,7 @@ func newCmdConfigImagesList(out io.Writer, mockK8sVersion *string) *cobra.Comman printer, err := outputFlags.ToPrinter() if err != nil { - return err + return errors.Wrap(err, "could not construct output printer") } imagesList, err := NewImagesList(cfgPath, externalcfg) @@ -424,7 +424,7 @@ func (itp *imageTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) erro // imageTextPrintFlags provides flags necessary for printing image in a text form. type imageTextPrintFlags struct{} -// ToPrinter returns kubeadm printer for the text output format +// ToPrinter returns a kubeadm printer for the text output format func (ipf *imageTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) { if outputFormat == output.TextOutput { return &imageTextPrinter{}, nil diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index 61c6c36c5b1..9a78a94866a 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -47,7 +47,6 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/discovery" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" - "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) var ( @@ -619,7 +618,7 @@ func fetchInitConfiguration(tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.I } // Fetches the init configuration - initConfiguration, err := configutil.FetchInitConfigurationFromCluster(tlsClient, &output.TextPrinter{}, "preflight", true, false) + initConfiguration, err := configutil.FetchInitConfigurationFromCluster(tlsClient, nil, "preflight", true, false) if err != nil { return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap") } diff --git a/cmd/kubeadm/app/cmd/reset.go b/cmd/kubeadm/app/cmd/reset.go index ef33b1ad8b8..407603d1def 100644 --- a/cmd/kubeadm/app/cmd/reset.go +++ b/cmd/kubeadm/app/cmd/reset.go @@ -37,7 +37,6 @@ import ( cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" - "k8s.io/kubernetes/cmd/kubeadm/app/util/output" utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime" ) @@ -99,7 +98,7 @@ func newResetData(cmd *cobra.Command, options *resetOptions, in io.Reader, out i client, err := getClientset(options.kubeconfigPath, false) if err == nil { klog.V(1).Infof("[reset] Loaded client set from kubeconfig file: %s", options.kubeconfigPath) - cfg, err = configutil.FetchInitConfigurationFromCluster(client, &output.TextPrinter{}, "reset", false, false) + cfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "reset", false, false) if err != nil { klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap from cluster: %v", err) } diff --git a/cmd/kubeadm/app/cmd/token.go b/cmd/kubeadm/app/cmd/token.go index 529efac51af..85e81eae6a3 100644 --- a/cmd/kubeadm/app/cmd/token.go +++ b/cmd/kubeadm/app/cmd/token.go @@ -169,7 +169,7 @@ func newCmdToken(out io.Writer, errW io.Writer) *cobra.Command { printer, err := outputFlags.ToPrinter() if err != nil { - return err + return errors.Wrap(err, "could not construct output printer") } return RunListTokens(out, errW, client, printer) @@ -354,7 +354,7 @@ func (ttp *tokenTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) erro // tokenTextPrintFlags provides flags necessary for printing bootstrap token in a text form. type tokenTextPrintFlags struct{} -// ToPrinter returns kubeadm printer for the text output format +// ToPrinter returns a kubeadm printer for the text output format func (tpf *tokenTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) { if outputFormat == output.TextOutput { return &tokenTextPrinter{columns: []string{"TOKEN", "TTL", "EXPIRES", "USAGES", "DESCRIPTION", "EXTRA GROUPS"}}, nil diff --git a/cmd/kubeadm/app/cmd/upgrade/common.go b/cmd/kubeadm/app/cmd/upgrade/common.go index 7c8d4e5fb3a..2623b823487 100644 --- a/cmd/kubeadm/app/cmd/upgrade/common.go +++ b/cmd/kubeadm/app/cmd/upgrade/common.go @@ -137,12 +137,12 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr if apierrors.IsNotFound(err) { printer.Printf("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %s namespace must exist.\n", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem) printer.Printf("[upgrade/config] Without this information, 'kubeadm upgrade' won't know how to configure your upgraded cluster.\n") - printer.Printf("\n") + printer.Println() printer.Printf("[upgrade/config] Next steps:\n") printer.Printf("\t- OPTION 1: Run 'kubeadm config upload from-flags' and specify the same CLI arguments you passed to 'kubeadm init' when you created your control-plane.\n") printer.Printf("\t- OPTION 2: Run 'kubeadm config upload from-file' and specify the same config file you passed to 'kubeadm init' when you created your control-plane.\n") printer.Printf("\t- OPTION 3: Pass a config file to 'kubeadm upgrade' using the --config flag.\n") - printer.Printf("\n") + printer.Println() err = errors.Errorf("the ConfigMap %q in the %s namespace used for getting configuration information was not found", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem) } return nil, nil, nil, errors.Wrap(err, "[upgrade/config] FATAL") diff --git a/cmd/kubeadm/app/cmd/upgrade/diff.go b/cmd/kubeadm/app/cmd/upgrade/diff.go index fe421e94811..8d0ebbc3703 100644 --- a/cmd/kubeadm/app/cmd/upgrade/diff.go +++ b/cmd/kubeadm/app/cmd/upgrade/diff.go @@ -37,7 +37,6 @@ import ( kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" - "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) type diffFlags struct { @@ -119,7 +118,7 @@ func runDiff(flags *diffFlags, args []string) error { if err != nil { return errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath) } - cfg, err = configutil.FetchInitConfigurationFromCluster(client, &output.TextPrinter{}, "upgrade/diff", false, false) + cfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade/diff", false, false) } if err != nil { return err diff --git a/cmd/kubeadm/app/cmd/upgrade/node.go b/cmd/kubeadm/app/cmd/upgrade/node.go index 5224de86afb..25adaa48fce 100644 --- a/cmd/kubeadm/app/cmd/upgrade/node.go +++ b/cmd/kubeadm/app/cmd/upgrade/node.go @@ -33,7 +33,6 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" "k8s.io/kubernetes/cmd/kubeadm/app/constants" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" - "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) // nodeOptions defines all the options exposed via flags by kubeadm upgrade node. @@ -140,7 +139,7 @@ func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions) (*node // Fetches the cluster configuration // NB in case of control-plane node, we are reading all the info for the node; in case of NOT control-plane node // (worker node), we are not reading local API address and the CRI socket from the node object - cfg, err := configutil.FetchInitConfigurationFromCluster(client, &output.TextPrinter{}, "upgrade", !isControlPlaneNode, false) + cfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", !isControlPlaneNode, false) if err != nil { return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap") } diff --git a/cmd/kubeadm/app/cmd/upgrade/plan.go b/cmd/kubeadm/app/cmd/upgrade/plan.go index b707d1d96d2..ad8812bb918 100644 --- a/cmd/kubeadm/app/cmd/upgrade/plan.go +++ b/cmd/kubeadm/app/cmd/upgrade/plan.go @@ -34,6 +34,7 @@ import ( "k8s.io/cli-runtime/pkg/printers" clientset "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme" outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2" @@ -62,7 +63,7 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command { RunE: func(_ *cobra.Command, args []string) error { printer, err := outputFlags.ToPrinter() if err != nil { - return err + return errors.Wrap(err, "could not construct output printer") } return runPlan(flags, args, printer) @@ -77,21 +78,21 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command { } // newComponentUpgradePlan helper creates outputapiv1alpha2.ComponentUpgradePlan object -func newComponentUpgradePlan(name, currentVersion, newVersion string) *outputapiv1alpha2.ComponentUpgradePlan { - return &outputapiv1alpha2.ComponentUpgradePlan{ +func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha2.ComponentUpgradePlan { + return outputapiv1alpha2.ComponentUpgradePlan{ Name: name, CurrentVersion: currentVersion, NewVersion: newVersion, } } -// upgradePlanPrintFlags defines printer flag structure for the +// upgradePlanPrintFlags defines a printer flag structure for the // upgrade plan kubeadm command and provides a method // of retrieving a known printer based on flag values provided. type upgradePlanPrintFlags struct { - // JSONYamlPrintFlags provides default flags necessary for json/yaml printing. + // JSONYamlPrintFlags provides default flags necessary for json/yaml printing JSONYamlPrintFlags *upgradePlanJSONYamlPrintFlags - // TextPrintFlags provides default flags necessary for text printing. + // TextPrintFlags provides default flags necessary for text printing TextPrintFlags *upgradePlanTextPrintFlags // TypeSetterPrinter is an implementation of ResourcePrinter that wraps another printer with types set on the objects TypeSetterPrinter *printers.TypeSetterPrinter @@ -108,7 +109,7 @@ func newUpgradePlanPrintFlags(outputFormat string) *upgradePlanPrintFlags { } } -// AllowedFormats returns list of allowed output formats +// AllowedFormats returns a list of allowed output formats func (pf *upgradePlanPrintFlags) AllowedFormats() []string { ret := pf.TextPrintFlags.AllowedFormats() return append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...) @@ -119,7 +120,9 @@ func (pf *upgradePlanPrintFlags) AllowedFormats() []string { func (pf *upgradePlanPrintFlags) AddFlags(cmd *cobra.Command) { pf.TextPrintFlags.AddFlags(cmd) pf.JSONYamlPrintFlags.AddFlags(cmd) - cmd.Flags().StringVarP(&pf.OutputFormat, "experimental-output", "o", pf.OutputFormat, fmt.Sprintf("Output format. One of: %s.", strings.Join(pf.AllowedFormats(), "|"))) + // TODO: once we are confident the feature is graduated we should remove the EXPERIMENTAL text below: + // https://github.com/kubernetes/kubeadm/issues/494 + cmd.Flags().StringVarP(&pf.OutputFormat, "output", "o", pf.OutputFormat, fmt.Sprintf("EXPERIMENTAL: Output format. One of: %s.", strings.Join(pf.AllowedFormats(), "|"))) } // ToPrinter receives an outputFormat and returns a printer capable of @@ -142,7 +145,7 @@ type upgradePlanJSONYamlPrintFlags struct { genericclioptions.JSONYamlPrintFlags } -// AllowedFormats returns list of allowed output formats +// AllowedFormats returns a list of allowed output formats func (pf *upgradePlanJSONYamlPrintFlags) AllowedFormats() []string { return []string{output.JSONOutput, output.YAMLOutput} } @@ -150,10 +153,10 @@ func (pf *upgradePlanJSONYamlPrintFlags) AllowedFormats() []string { // upgradePlanJSONYAMLPrinter prints upgrade plan in a JSON or YAML format type upgradePlanJSONYAMLPrinter struct { output.ResourcePrinterWrapper - Buffer []*outputapiv1alpha2.ComponentUpgradePlan + Buffer []outputapiv1alpha2.ComponentUpgradePlan } -// newUpgradePlanJSONYAMLPrinter creates new upgradePlanJSONYAMLPrinter object +// newUpgradePlanJSONYAMLPrinter creates a new upgradePlanJSONYAMLPrinter object func newUpgradePlanJSONYAMLPrinter(resourcePrinter printers.ResourcePrinter, err error) (output.Printer, error) { if err != nil { return nil, err @@ -165,18 +168,29 @@ func newUpgradePlanJSONYAMLPrinter(resourcePrinter printers.ResourcePrinter, err func (p *upgradePlanJSONYAMLPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan) if !ok { - return fmt.Errorf("expected ComponentUpgradePlan, but got %+v", obj) + return errors.Errorf("expected ComponentUpgradePlan, but got %+v", obj) } - p.Buffer = append(p.Buffer, item) + p.Buffer = append(p.Buffer, *item) return nil } -// Close writes any buffered data and empties list of buffered components -func (p *upgradePlanJSONYAMLPrinter) Close(writer io.Writer) { +// Flush writes any buffered data once last object is added +func (p *upgradePlanJSONYAMLPrinter) Flush(writer io.Writer, last bool) { + if !last { + return + } + if len(p.Buffer) == 0 { + return + } plan := &outputapiv1alpha2.UpgradePlan{Components: p.Buffer} - // p.ResourcePrinterWrapper.Printer.PrintObj(plan, writer) - p.Printer.PrintObj(plan, writer) - p.Buffer = []*outputapiv1alpha2.ComponentUpgradePlan{} + if err := p.Printer.PrintObj(plan, writer); err != nil { + fmt.Fprintf(os.Stderr, "could not flush output buffer: %v\n", err) + } +} + +// Close empties the list of buffered components +func (p *upgradePlanJSONYAMLPrinter) Close(writer io.Writer) { + p.Buffer = p.Buffer[:0] } // upgradePlanTextPrinter prints upgrade plan in a text form @@ -187,10 +201,11 @@ type upgradePlanTextPrinter struct { } // Flush writes any buffered data -func (p *upgradePlanTextPrinter) Flush(writer io.Writer) { +func (p *upgradePlanTextPrinter) Flush(writer io.Writer, last bool) { if p.tabwriter != nil { p.tabwriter.Flush() p.tabwriter = nil + p.Fprintln(writer, "") } } @@ -204,26 +219,25 @@ func (p *upgradePlanTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan) if !ok { - return fmt.Errorf("expected ComponentUpgradePlan, but got %+v", obj) + return errors.Errorf("expected ComponentUpgradePlan, but got %+v", obj) } // Print item fmt.Fprintf(p.tabwriter, "%s\t%s\t%s\n", item.Name, item.CurrentVersion, item.NewVersion) - return nil } -// upgradePlanTextPrintFlags provides flags necessary for printing upgrade plan in a text form. +// upgradePlanTextPrintFlags provides flags necessary for printing upgrade plan in a text form type upgradePlanTextPrintFlags struct{} func (pf *upgradePlanTextPrintFlags) AddFlags(cmd *cobra.Command) {} -// AllowedFormats returns list of allowed output formats +// AllowedFormats returns a list of allowed output formats func (pf *upgradePlanTextPrintFlags) AllowedFormats() []string { return []string{output.TextOutput} } -// ToPrinter returns kubeadm printer for the text output format +// ToPrinter returns a kubeadm printer for the text output format func (pf *upgradePlanTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) { if outputFormat == output.TextOutput { return &upgradePlanTextPrinter{columns: []string{"COMPONENT", "CURRENT", "TARGET"}}, nil @@ -282,15 +296,12 @@ func runPlan(flags *planFlags, args []string, printer output.Printer) error { // Finally, print the component config state table printComponentConfigVersionStates(configVersionStates, os.Stdout, printer) - - // Add a newline in the end of this output to leave some space to the next output section - klog.V(1).Infoln() return nil } // 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 []*outputapiv1alpha2.ComponentUpgradePlan, up *upgrade.Upgrade, name string) []*outputapiv1alpha2.ComponentUpgradePlan { +func appendDNSComponent(components []outputapiv1alpha2.ComponentUpgradePlan, up *upgrade.Upgrade, name string) []outputapiv1alpha2.ComponentUpgradePlan { beforeVersion := up.Before.DNSVersion afterVersion := up.After.DNSVersion @@ -316,7 +327,7 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha } } - components := []*outputapiv1alpha2.ComponentUpgradePlan{} + components := []outputapiv1alpha2.ComponentUpgradePlan{} if up.CanUpgradeKubelets() { // The map is of the form :. Here all the keys are put into a slice and sorted @@ -370,26 +381,26 @@ func printUpgradePlan(up *upgrade.Upgrade, plan *outputapiv1alpha2.UpgradePlan, } else if component.Name == constants.Kubelet { if printManualUpgradeHeader { printer.Fprintln(writer, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':") - printer.PrintObj(newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion), writer) + plan := newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion) + printer.PrintObj(&plan, writer) printManualUpgradeHeader = false } else { - printer.PrintObj(newComponentUpgradePlan("", component.CurrentVersion, component.NewVersion), writer) + plan := newComponentUpgradePlan("", component.CurrentVersion, component.NewVersion) + printer.PrintObj(&plan, writer) } } else { if printHeader { // End of manual upgrades table - printer.Flush(writer) - - printer.Fprintln(writer, "") + printer.Flush(writer, false) printer.Fprintf(writer, "Upgrade to the latest %s:\n", up.Description) printer.Fprintln(writer, "") printHeader = false } - printer.PrintObj(newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion), writer) + plan := newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion) + printer.PrintObj(&plan, writer) } } - printer.Flush(writer) - printer.Fprintln(writer, "") + printer.Flush(writer, true) printer.Fprintln(writer, "You can now apply the upgrade by executing the following command:") printer.Fprintln(writer, "") @@ -430,8 +441,7 @@ func yesOrNo(b bool) string { } func printLineSeparator(w io.Writer, printer output.Printer) { - printer.Fprintln(w, "_____________________________________________________________________") - printer.Fprintln(w, "") + printer.Fprintf(w, "_____________________________________________________________________\n\n") } func printComponentConfigVersionStates(versionStates []outputapiv1alpha2.ComponentConfigVersionState, w io.Writer, printer output.Printer) { diff --git a/cmd/kubeadm/app/componentconfigs/configset.go b/cmd/kubeadm/app/componentconfigs/configset.go index 3ad5cfad108..6c2f8e74b80 100644 --- a/cmd/kubeadm/app/componentconfigs/configset.go +++ b/cmd/kubeadm/app/componentconfigs/configset.go @@ -26,9 +26,9 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" clientset "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" - outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict" diff --git a/cmd/kubeadm/app/phases/upgrade/compute.go b/cmd/kubeadm/app/phases/upgrade/compute.go index 8a54a1081a0..71fde4770af 100644 --- a/cmd/kubeadm/app/phases/upgrade/compute.go +++ b/cmd/kubeadm/app/phases/upgrade/compute.go @@ -268,7 +268,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA } // Add a newline in the end of this output to leave some space to the next output section - printer.Printf("\n") + printer.Println() return upgrades, nil } diff --git a/cmd/kubeadm/app/util/config/cluster.go b/cmd/kubeadm/app/util/config/cluster.go index ed9648f61e0..937832ea7ac 100644 --- a/cmd/kubeadm/app/util/config/cluster.go +++ b/cmd/kubeadm/app/util/config/cluster.go @@ -46,6 +46,9 @@ import ( // FetchInitConfigurationFromCluster fetches configuration from a ConfigMap in the cluster func FetchInitConfigurationFromCluster(client clientset.Interface, printer output.Printer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) { + if printer == nil { + printer = &output.TextPrinter{} + } printer.Printf("[%s] Reading configuration from the cluster...\n", logPrefix) printer.Printf("[%s] FYI: You can look at this config file with 'kubectl -n %s get cm %s -o yaml'\n", logPrefix, metav1.NamespaceSystem, constants.KubeadmConfigConfigMap) diff --git a/cmd/kubeadm/app/util/output/output.go b/cmd/kubeadm/app/util/output/output.go index b99f51eaac7..e662c943abe 100644 --- a/cmd/kubeadm/app/util/output/output.go +++ b/cmd/kubeadm/app/util/output/output.go @@ -28,14 +28,16 @@ import ( "k8s.io/cli-runtime/pkg/printers" ) -// TextOutput describes the plain text output -const TextOutput = "text" +const ( + // TextOutput describes the plain text output + TextOutput = "text" -// JSONOutput describes the JSON output -const JSONOutput = "json" + // JSONOutput describes the JSON output + JSONOutput = "json" -// YAMLOutput describes the YAML output -const YAMLOutput = "yaml" + // YAMLOutput describes the YAML output + YAMLOutput = "yaml" +) // TextPrintFlags is an interface to handle custom text output type TextPrintFlags interface { @@ -58,7 +60,7 @@ type PrintFlags struct { OutputFormat *string } -// AllowedFormats returns list of allowed output formats +// AllowedFormats returns a list of allowed output formats func (pf *PrintFlags) AllowedFormats() []string { ret := []string{TextOutput} ret = append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...) @@ -141,8 +143,9 @@ type Printer interface { Fprintf(writer io.Writer, format string, args ...interface{}) (n int, err error) Fprintln(writer io.Writer, args ...interface{}) (n int, err error) Printf(format string, args ...interface{}) (n int, err error) + Println(args ...interface{}) (n int, err error) - Flush(writer io.Writer) + Flush(writer io.Writer, last bool) Close(writer io.Writer) } @@ -150,16 +153,6 @@ type Printer interface { type TextPrinter struct { } -// Flush writes any buffered data -func (tp *TextPrinter) Flush(writer io.Writer) { - return -} - -// Close flushes any buffered data and closes the printer -func (tp *TextPrinter) Close(writer io.Writer) { - return -} - // PrintObj is an implementation of ResourcePrinter.PrintObj that prints object func (tp *TextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { _, err := fmt.Fprintf(writer, "%+v\n", obj) @@ -181,6 +174,19 @@ func (tp *TextPrinter) Printf(format string, args ...interface{}) (n int, err er return fmt.Printf(format, args...) } +// Println is a wrapper around fmt.Printf +func (tp *TextPrinter) Println(args ...interface{}) (n int, err error) { + return fmt.Println(args...) +} + +// Flush writes any buffered data +func (tp *TextPrinter) Flush(writer io.Writer, last bool) { +} + +// Close flushes any buffered data and closes the printer +func (tp *TextPrinter) Close(writer io.Writer) { +} + // ResourcePrinterWrapper wraps ResourcePrinter and implements Printer interface type ResourcePrinterWrapper struct { Printer printers.ResourcePrinter @@ -195,13 +201,11 @@ func NewResourcePrinterWrapper(resourcePrinter printers.ResourcePrinter, err err } // Flush writes any buffered data -func (rpw *ResourcePrinterWrapper) Flush(writer io.Writer) { - return +func (rpw *ResourcePrinterWrapper) Flush(writer io.Writer, last bool) { } // Close flushes any buffered data and closes the printer func (rpw *ResourcePrinterWrapper) Close(writer io.Writer) { - return } // PrintObj is an implementation of ResourcePrinter.PrintObj that calls underlying printer API @@ -216,7 +220,7 @@ func (rpw *ResourcePrinterWrapper) Fprintf(writer io.Writer, format string, args return 0, nil } -// Fprintln is an empty method to satisfy Printer interface +// Fprintln is an empty method to satisfy the Printer interface // and silent info printing for structured output // This method is usually redefined for the text output func (rpw *ResourcePrinterWrapper) Fprintln(writer io.Writer, args ...interface{}) (n int, err error) { @@ -229,3 +233,10 @@ func (rpw *ResourcePrinterWrapper) Fprintln(writer io.Writer, args ...interface{ func (rpw *ResourcePrinterWrapper) Printf(format string, args ...interface{}) (n int, err error) { return 0, nil } + +// Println is an empty method to satisfy Printer interface +// and silent info printing for structured output +// This method is usually redefined for the text output +func (rpw *ResourcePrinterWrapper) Println(args ...interface{}) (n int, err error) { + return 0, nil +}