cleanup: kubeadm upgrade plan supports json/yaml output

Co-authored-by: Lubomir I. Ivanov <neolit123@gmail.com>
This commit is contained in:
Paco Xu 2022-03-17 11:49:08 +08:00
parent 79ecd60208
commit 22fb3be96d
18 changed files with 106 additions and 99 deletions

View File

@ -78,7 +78,7 @@ type ComponentConfigVersionState struct {
type UpgradePlan struct { type UpgradePlan struct {
metav1.TypeMeta metav1.TypeMeta
Components []*ComponentUpgradePlan Components []ComponentUpgradePlan
ConfigVersions []ComponentConfigVersionState ConfigVersions []ComponentConfigVersionState
} }

View File

@ -78,7 +78,7 @@ type ComponentConfigVersionState struct {
type UpgradePlan struct { type UpgradePlan struct {
metav1.TypeMeta metav1.TypeMeta
Components []*ComponentUpgradePlan `json:"components"` Components []ComponentUpgradePlan `json:"components"`
ConfigVersions []ComponentConfigVersionState `json:"configVersions"` ConfigVersions []ComponentConfigVersionState `json:"configVersions"`
} }

View File

@ -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 { 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)) out.ConfigVersions = *(*[]output.ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions))
return nil 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 { 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)) out.ConfigVersions = *(*[]ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions))
return nil return nil
} }

View File

@ -128,14 +128,8 @@ func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) {
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
if in.Components != nil { if in.Components != nil {
in, out := &in.Components, &out.Components in, out := &in.Components, &out.Components
*out = make([]*ComponentUpgradePlan, len(*in)) *out = make([]ComponentUpgradePlan, len(*in))
for i := range *in { copy(*out, *in)
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(ComponentUpgradePlan)
**out = **in
}
}
} }
if in.ConfigVersions != nil { if in.ConfigVersions != nil {
in, out := &in.ConfigVersions, &out.ConfigVersions in, out := &in.ConfigVersions, &out.ConfigVersions

View File

@ -128,14 +128,8 @@ func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) {
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
if in.Components != nil { if in.Components != nil {
in, out := &in.Components, &out.Components in, out := &in.Components, &out.Components
*out = make([]*ComponentUpgradePlan, len(*in)) *out = make([]ComponentUpgradePlan, len(*in))
for i := range *in { copy(*out, *in)
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(ComponentUpgradePlan)
**out = **in
}
}
} }
if in.ConfigVersions != nil { if in.ConfigVersions != nil {
in, out := &in.ConfigVersions, &out.ConfigVersions in, out := &in.ConfigVersions, &out.ConfigVersions

View File

@ -39,7 +39,6 @@ import (
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
) )
var ( var (
@ -333,7 +332,7 @@ func getInternalCfg(cfgPath string, kubeconfigPath string, cfg kubeadmapiv1.Clus
if cfgPath == "" { if cfgPath == "" {
client, err := kubeconfigutil.ClientSetFromFile(kubeconfigPath) client, err := kubeconfigutil.ClientSetFromFile(kubeconfigPath)
if err == nil { 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 { if err == nil {
fmt.Println() // add empty line to separate the FetchInitConfigurationFromCluster output from the command output fmt.Println() // add empty line to separate the FetchInitConfigurationFromCluster output from the command output
return internalcfg, nil return internalcfg, nil

View File

@ -371,7 +371,7 @@ func newCmdConfigImagesList(out io.Writer, mockK8sVersion *string) *cobra.Comman
printer, err := outputFlags.ToPrinter() printer, err := outputFlags.ToPrinter()
if err != nil { if err != nil {
return err return errors.Wrap(err, "could not construct output printer")
} }
imagesList, err := NewImagesList(cfgPath, externalcfg) 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. // imageTextPrintFlags provides flags necessary for printing image in a text form.
type imageTextPrintFlags struct{} 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) { func (ipf *imageTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
if outputFormat == output.TextOutput { if outputFormat == output.TextOutput {
return &imageTextPrinter{}, nil return &imageTextPrinter{}, nil

View File

@ -47,7 +47,6 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/discovery" "k8s.io/kubernetes/cmd/kubeadm/app/discovery"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
) )
var ( var (
@ -619,7 +618,7 @@ func fetchInitConfiguration(tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.I
} }
// Fetches the init configuration // 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 { if err != nil {
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap") return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
} }

View File

@ -37,7 +37,6 @@ import (
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" 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" 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) client, err := getClientset(options.kubeconfigPath, false)
if err == nil { if err == nil {
klog.V(1).Infof("[reset] Loaded client set from kubeconfig file: %s", options.kubeconfigPath) 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 { if err != nil {
klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap from cluster: %v", err) klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap from cluster: %v", err)
} }

View File

@ -169,7 +169,7 @@ func newCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
printer, err := outputFlags.ToPrinter() printer, err := outputFlags.ToPrinter()
if err != nil { if err != nil {
return err return errors.Wrap(err, "could not construct output printer")
} }
return RunListTokens(out, errW, client, 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. // tokenTextPrintFlags provides flags necessary for printing bootstrap token in a text form.
type tokenTextPrintFlags struct{} 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) { func (tpf *tokenTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
if outputFormat == output.TextOutput { if outputFormat == output.TextOutput {
return &tokenTextPrinter{columns: []string{"TOKEN", "TTL", "EXPIRES", "USAGES", "DESCRIPTION", "EXTRA GROUPS"}}, nil return &tokenTextPrinter{columns: []string{"TOKEN", "TTL", "EXPIRES", "USAGES", "DESCRIPTION", "EXTRA GROUPS"}}, nil

View File

@ -137,12 +137,12 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr
if apierrors.IsNotFound(err) { 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] 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("[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("[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 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 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("\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) 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") return nil, nil, nil, errors.Wrap(err, "[upgrade/config] FATAL")

View File

@ -37,7 +37,6 @@ import (
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
) )
type diffFlags struct { type diffFlags struct {
@ -119,7 +118,7 @@ func runDiff(flags *diffFlags, args []string) error {
if err != nil { if err != nil {
return errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath) 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 { if err != nil {
return err return err

View File

@ -33,7 +33,6 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" 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. // 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 // 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 // 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 // (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 { if err != nil {
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap") return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
} }

View File

@ -34,6 +34,7 @@ import (
"k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/printers"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2" "k8s.io/klog/v2"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme" outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme"
outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2" 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 { RunE: func(_ *cobra.Command, args []string) error {
printer, err := outputFlags.ToPrinter() printer, err := outputFlags.ToPrinter()
if err != nil { if err != nil {
return err return errors.Wrap(err, "could not construct output printer")
} }
return runPlan(flags, args, printer) return runPlan(flags, args, printer)
@ -77,21 +78,21 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command {
} }
// newComponentUpgradePlan helper creates outputapiv1alpha2.ComponentUpgradePlan object // newComponentUpgradePlan helper creates outputapiv1alpha2.ComponentUpgradePlan object
func newComponentUpgradePlan(name, currentVersion, newVersion string) *outputapiv1alpha2.ComponentUpgradePlan { func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha2.ComponentUpgradePlan {
return &outputapiv1alpha2.ComponentUpgradePlan{ return outputapiv1alpha2.ComponentUpgradePlan{
Name: name, Name: name,
CurrentVersion: currentVersion, CurrentVersion: currentVersion,
NewVersion: newVersion, 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 // upgrade plan kubeadm command and provides a method
// of retrieving a known printer based on flag values provided. // of retrieving a known printer based on flag values provided.
type upgradePlanPrintFlags struct { type upgradePlanPrintFlags struct {
// JSONYamlPrintFlags provides default flags necessary for json/yaml printing. // JSONYamlPrintFlags provides default flags necessary for json/yaml printing
JSONYamlPrintFlags *upgradePlanJSONYamlPrintFlags JSONYamlPrintFlags *upgradePlanJSONYamlPrintFlags
// TextPrintFlags provides default flags necessary for text printing. // TextPrintFlags provides default flags necessary for text printing
TextPrintFlags *upgradePlanTextPrintFlags TextPrintFlags *upgradePlanTextPrintFlags
// TypeSetterPrinter is an implementation of ResourcePrinter that wraps another printer with types set on the objects // TypeSetterPrinter is an implementation of ResourcePrinter that wraps another printer with types set on the objects
TypeSetterPrinter *printers.TypeSetterPrinter 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 { func (pf *upgradePlanPrintFlags) AllowedFormats() []string {
ret := pf.TextPrintFlags.AllowedFormats() ret := pf.TextPrintFlags.AllowedFormats()
return append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...) return append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...)
@ -119,7 +120,9 @@ func (pf *upgradePlanPrintFlags) AllowedFormats() []string {
func (pf *upgradePlanPrintFlags) AddFlags(cmd *cobra.Command) { func (pf *upgradePlanPrintFlags) AddFlags(cmd *cobra.Command) {
pf.TextPrintFlags.AddFlags(cmd) pf.TextPrintFlags.AddFlags(cmd)
pf.JSONYamlPrintFlags.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 // ToPrinter receives an outputFormat and returns a printer capable of
@ -142,7 +145,7 @@ type upgradePlanJSONYamlPrintFlags struct {
genericclioptions.JSONYamlPrintFlags genericclioptions.JSONYamlPrintFlags
} }
// AllowedFormats returns list of allowed output formats // AllowedFormats returns a list of allowed output formats
func (pf *upgradePlanJSONYamlPrintFlags) AllowedFormats() []string { func (pf *upgradePlanJSONYamlPrintFlags) AllowedFormats() []string {
return []string{output.JSONOutput, output.YAMLOutput} 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 // upgradePlanJSONYAMLPrinter prints upgrade plan in a JSON or YAML format
type upgradePlanJSONYAMLPrinter struct { type upgradePlanJSONYAMLPrinter struct {
output.ResourcePrinterWrapper 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) { func newUpgradePlanJSONYAMLPrinter(resourcePrinter printers.ResourcePrinter, err error) (output.Printer, error) {
if err != nil { if err != nil {
return nil, err return nil, err
@ -165,18 +168,29 @@ func newUpgradePlanJSONYAMLPrinter(resourcePrinter printers.ResourcePrinter, err
func (p *upgradePlanJSONYAMLPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { func (p *upgradePlanJSONYAMLPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan) item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan)
if !ok { 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 return nil
} }
// Close writes any buffered data and empties list of buffered components // Flush writes any buffered data once last object is added
func (p *upgradePlanJSONYAMLPrinter) Close(writer io.Writer) { func (p *upgradePlanJSONYAMLPrinter) Flush(writer io.Writer, last bool) {
if !last {
return
}
if len(p.Buffer) == 0 {
return
}
plan := &outputapiv1alpha2.UpgradePlan{Components: p.Buffer} plan := &outputapiv1alpha2.UpgradePlan{Components: p.Buffer}
// p.ResourcePrinterWrapper.Printer.PrintObj(plan, writer) if err := p.Printer.PrintObj(plan, writer); err != nil {
p.Printer.PrintObj(plan, writer) fmt.Fprintf(os.Stderr, "could not flush output buffer: %v\n", err)
p.Buffer = []*outputapiv1alpha2.ComponentUpgradePlan{} }
}
// 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 // upgradePlanTextPrinter prints upgrade plan in a text form
@ -187,10 +201,11 @@ type upgradePlanTextPrinter struct {
} }
// Flush writes any buffered data // 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 { if p.tabwriter != nil {
p.tabwriter.Flush() p.tabwriter.Flush()
p.tabwriter = nil 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) item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan)
if !ok { if !ok {
return fmt.Errorf("expected ComponentUpgradePlan, but got %+v", obj) return errors.Errorf("expected ComponentUpgradePlan, but got %+v", obj)
} }
// Print item // Print item
fmt.Fprintf(p.tabwriter, "%s\t%s\t%s\n", item.Name, item.CurrentVersion, item.NewVersion) fmt.Fprintf(p.tabwriter, "%s\t%s\t%s\n", item.Name, item.CurrentVersion, item.NewVersion)
return nil 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{} type upgradePlanTextPrintFlags struct{}
func (pf *upgradePlanTextPrintFlags) AddFlags(cmd *cobra.Command) {} 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 { func (pf *upgradePlanTextPrintFlags) AllowedFormats() []string {
return []string{output.TextOutput} 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) { func (pf *upgradePlanTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) {
if outputFormat == output.TextOutput { if outputFormat == output.TextOutput {
return &upgradePlanTextPrinter{columns: []string{"COMPONENT", "CURRENT", "TARGET"}}, nil 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 // Finally, print the component config state table
printComponentConfigVersionStates(configVersionStates, os.Stdout, printer) 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 return nil
} }
// TODO There is currently no way to cleanly output upgrades that involve adding, removing, or changing components // 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. // 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 beforeVersion := up.Before.DNSVersion
afterVersion := up.After.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() { if up.CanUpgradeKubelets() {
// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted // The map is of the form <old-version>:<node-count>. 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 { } else if component.Name == constants.Kubelet {
if printManualUpgradeHeader { if printManualUpgradeHeader {
printer.Fprintln(writer, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':") 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 printManualUpgradeHeader = false
} else { } else {
printer.PrintObj(newComponentUpgradePlan("", component.CurrentVersion, component.NewVersion), writer) plan := newComponentUpgradePlan("", component.CurrentVersion, component.NewVersion)
printer.PrintObj(&plan, writer)
} }
} else { } else {
if printHeader { if printHeader {
// End of manual upgrades table // End of manual upgrades table
printer.Flush(writer) printer.Flush(writer, false)
printer.Fprintln(writer, "")
printer.Fprintf(writer, "Upgrade to the latest %s:\n", up.Description) printer.Fprintf(writer, "Upgrade to the latest %s:\n", up.Description)
printer.Fprintln(writer, "") printer.Fprintln(writer, "")
printHeader = false 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.Flush(writer, true)
printer.Fprintln(writer, "")
printer.Fprintln(writer, "You can now apply the upgrade by executing the following command:") printer.Fprintln(writer, "You can now apply the upgrade by executing the following command:")
printer.Fprintln(writer, "") printer.Fprintln(writer, "")
@ -430,8 +441,7 @@ func yesOrNo(b bool) string {
} }
func printLineSeparator(w io.Writer, printer output.Printer) { func printLineSeparator(w io.Writer, printer output.Printer) {
printer.Fprintln(w, "_____________________________________________________________________") printer.Fprintf(w, "_____________________________________________________________________\n\n")
printer.Fprintln(w, "")
} }
func printComponentConfigVersionStates(versionStates []outputapiv1alpha2.ComponentConfigVersionState, w io.Writer, printer output.Printer) { func printComponentConfigVersionStates(versionStates []outputapiv1alpha2.ComponentConfigVersionState, w io.Writer, printer output.Printer) {

View File

@ -26,9 +26,9 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2" "k8s.io/klog/v2"
outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 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" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict" "k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"

View File

@ -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 // 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 return upgrades, nil
} }

View File

@ -46,6 +46,9 @@ import (
// FetchInitConfigurationFromCluster fetches configuration from a ConfigMap in the cluster // 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) { 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] 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) 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)

View File

@ -28,14 +28,16 @@ import (
"k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/printers"
) )
// TextOutput describes the plain text output const (
const TextOutput = "text" // TextOutput describes the plain text output
TextOutput = "text"
// JSONOutput describes the JSON output // JSONOutput describes the JSON output
const JSONOutput = "json" JSONOutput = "json"
// YAMLOutput describes the YAML output // YAMLOutput describes the YAML output
const YAMLOutput = "yaml" YAMLOutput = "yaml"
)
// TextPrintFlags is an interface to handle custom text output // TextPrintFlags is an interface to handle custom text output
type TextPrintFlags interface { type TextPrintFlags interface {
@ -58,7 +60,7 @@ type PrintFlags struct {
OutputFormat *string OutputFormat *string
} }
// AllowedFormats returns list of allowed output formats // AllowedFormats returns a list of allowed output formats
func (pf *PrintFlags) AllowedFormats() []string { func (pf *PrintFlags) AllowedFormats() []string {
ret := []string{TextOutput} ret := []string{TextOutput}
ret = append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...) 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) Fprintf(writer io.Writer, format string, args ...interface{}) (n int, err error)
Fprintln(writer io.Writer, 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) 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) Close(writer io.Writer)
} }
@ -150,16 +153,6 @@ type Printer interface {
type TextPrinter struct { 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 // PrintObj is an implementation of ResourcePrinter.PrintObj that prints object
func (tp *TextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { func (tp *TextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
_, err := fmt.Fprintf(writer, "%+v\n", obj) _, 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...) 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 // ResourcePrinterWrapper wraps ResourcePrinter and implements Printer interface
type ResourcePrinterWrapper struct { type ResourcePrinterWrapper struct {
Printer printers.ResourcePrinter Printer printers.ResourcePrinter
@ -195,13 +201,11 @@ func NewResourcePrinterWrapper(resourcePrinter printers.ResourcePrinter, err err
} }
// Flush writes any buffered data // Flush writes any buffered data
func (rpw *ResourcePrinterWrapper) Flush(writer io.Writer) { func (rpw *ResourcePrinterWrapper) Flush(writer io.Writer, last bool) {
return
} }
// Close flushes any buffered data and closes the printer // Close flushes any buffered data and closes the printer
func (rpw *ResourcePrinterWrapper) Close(writer io.Writer) { func (rpw *ResourcePrinterWrapper) Close(writer io.Writer) {
return
} }
// PrintObj is an implementation of ResourcePrinter.PrintObj that calls underlying printer API // 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 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 // and silent info printing for structured output
// This method is usually redefined for the text output // This method is usually redefined for the text output
func (rpw *ResourcePrinterWrapper) Fprintln(writer io.Writer, args ...interface{}) (n int, err error) { 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) { func (rpw *ResourcePrinterWrapper) Printf(format string, args ...interface{}) (n int, err error) {
return 0, nil 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
}