bugfix(kubeadm): Fix UT and log optmize

This commit is contained in:
Paco Xu 2022-03-02 19:28:42 +08:00
parent 93daef6e77
commit 79ecd60208
13 changed files with 262 additions and 86 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,8 +128,14 @@ 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))
copy(*out, *in) for i := range *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,8 +128,14 @@ 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))
copy(*out, *in) for i := range *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

@ -135,14 +135,14 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr
cfg, legacyReconfigure, err := loadConfig(flags.cfgPath, client, !upgradeApply, printer) cfg, legacyReconfigure, err := loadConfig(flags.cfgPath, client, !upgradeApply, printer)
if err != nil { if err != nil {
if apierrors.IsNotFound(err) { if apierrors.IsNotFound(err) {
fmt.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)
fmt.Println("[upgrade/config] Without this information, 'kubeadm upgrade' won't know how to configure your upgraded cluster.") printer.Printf("[upgrade/config] Without this information, 'kubeadm upgrade' won't know how to configure your upgraded cluster.\n")
fmt.Println("") printer.Printf("\n")
fmt.Println("[upgrade/config] Next steps:") printer.Printf("[upgrade/config] Next steps:\n")
fmt.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")
fmt.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")
fmt.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")
fmt.Println("") printer.Printf("\n")
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")
@ -203,14 +203,14 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr
// Check if feature gate flags used in the cluster are consistent with the set of features currently supported by kubeadm // Check if feature gate flags used in the cluster are consistent with the set of features currently supported by kubeadm
if msg := features.CheckDeprecatedFlags(&features.InitFeatureGates, cfg.FeatureGates); len(msg) > 0 { if msg := features.CheckDeprecatedFlags(&features.InitFeatureGates, cfg.FeatureGates); len(msg) > 0 {
for _, m := range msg { for _, m := range msg {
fmt.Printf("[upgrade/config] %s\n", m) printer.Printf("[upgrade/config] %s\n", m)
} }
return nil, nil, nil, errors.New("[upgrade/config] FATAL. Unable to upgrade a cluster using deprecated feature-gate flags. Please see the release notes") return nil, nil, nil, errors.New("[upgrade/config] FATAL. Unable to upgrade a cluster using deprecated feature-gate flags. Please see the release notes")
} }
// If the user told us to print this information out; do it! // If the user told us to print this information out; do it!
if flags.printConfig { if flags.printConfig {
printConfiguration(&cfg.ClusterConfiguration, os.Stdout) printConfiguration(&cfg.ClusterConfiguration, os.Stdout, printer)
} }
// Use a real version getter interface that queries the API server, the kubeadm client and the Kubernetes CI system for latest versions // Use a real version getter interface that queries the API server, the kubeadm client and the Kubernetes CI system for latest versions
@ -218,7 +218,7 @@ func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgr
} }
// printConfiguration prints the external version of the API to yaml // printConfiguration prints the external version of the API to yaml
func printConfiguration(clustercfg *kubeadmapi.ClusterConfiguration, w io.Writer) { func printConfiguration(clustercfg *kubeadmapi.ClusterConfiguration, w io.Writer, printer output.Printer) {
// Short-circuit if cfg is nil, so we can safely get the value of the pointer below // Short-circuit if cfg is nil, so we can safely get the value of the pointer below
if clustercfg == nil { if clustercfg == nil {
return return
@ -226,11 +226,11 @@ func printConfiguration(clustercfg *kubeadmapi.ClusterConfiguration, w io.Writer
cfgYaml, err := configutil.MarshalKubeadmConfigObject(clustercfg) cfgYaml, err := configutil.MarshalKubeadmConfigObject(clustercfg)
if err == nil { if err == nil {
fmt.Fprintln(w, "[upgrade/config] Configuration used:") printer.Fprintln(w, "[upgrade/config] Configuration used:")
scanner := bufio.NewScanner(bytes.NewReader(cfgYaml)) scanner := bufio.NewScanner(bytes.NewReader(cfgYaml))
for scanner.Scan() { for scanner.Scan() {
fmt.Fprintf(w, "\t%s\n", scanner.Text()) printer.Fprintf(w, "\t%s\n", scanner.Text())
} }
} }
} }

View File

@ -139,7 +139,7 @@ func TestPrintConfiguration(t *testing.T) {
for _, rt := range tests { for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) { t.Run(rt.name, func(t *testing.T) {
rt.buf = bytes.NewBufferString("") rt.buf = bytes.NewBufferString("")
printConfiguration(rt.cfg, rt.buf) printConfiguration(rt.cfg, rt.buf, &output.TextPrinter{})
actualBytes := rt.buf.Bytes() actualBytes := rt.buf.Bytes()
if !bytes.Equal(actualBytes, rt.expectedBytes) { if !bytes.Equal(actualBytes, rt.expectedBytes) {
t.Errorf( t.Errorf(

View File

@ -35,7 +35,6 @@ import (
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"
outputapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
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"
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs" "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
@ -77,9 +76,9 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command {
return cmd return cmd
} }
// newComponentUpgradePlan helper creates outputapiv1alpha1.ComponentUpgradePlan object // newComponentUpgradePlan helper creates outputapiv1alpha2.ComponentUpgradePlan object
func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapi.ComponentUpgradePlan { func newComponentUpgradePlan(name, currentVersion, newVersion string) *outputapiv1alpha2.ComponentUpgradePlan {
return outputapi.ComponentUpgradePlan{ return &outputapiv1alpha2.ComponentUpgradePlan{
Name: name, Name: name,
CurrentVersion: currentVersion, CurrentVersion: currentVersion,
NewVersion: newVersion, NewVersion: newVersion,
@ -151,7 +150,7 @@ 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 new upgradePlanJSONYAMLPrinter object
@ -168,15 +167,16 @@ func (p *upgradePlanJSONYAMLPrinter) PrintObj(obj runtime.Object, writer io.Writ
if !ok { if !ok {
return fmt.Errorf("expected ComponentUpgradePlan, but got %+v", obj) return fmt.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 // Close writes any buffered data and empties list of buffered components
func (p *upgradePlanJSONYAMLPrinter) Close(writer io.Writer) { func (p *upgradePlanJSONYAMLPrinter) Close(writer io.Writer) {
plan := &outputapiv1alpha2.UpgradePlan{Components: p.Buffer} plan := &outputapiv1alpha2.UpgradePlan{Components: p.Buffer}
// p.ResourcePrinterWrapper.Printer.PrintObj(plan, writer)
p.Printer.PrintObj(plan, writer) p.Printer.PrintObj(plan, writer)
p.Buffer = []outputapiv1alpha2.ComponentUpgradePlan{} p.Buffer = []*outputapiv1alpha2.ComponentUpgradePlan{}
} }
// upgradePlanTextPrinter prints upgrade plan in a text form // upgradePlanTextPrinter prints upgrade plan in a text form
@ -226,9 +226,9 @@ func (pf *upgradePlanTextPrintFlags) AllowedFormats() []string {
// ToPrinter returns kubeadm printer for the text output format // ToPrinter returns 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", "AVAILABLE"}}, nil return &upgradePlanTextPrinter{columns: []string{"COMPONENT", "CURRENT", "TARGET"}}, nil
} }
return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: []string{output.TextOutput}} return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: []string{output.JSONOutput, output.YAMLOutput, output.TextOutput}}
} }
// runPlan takes care of outputting available versions to upgrade to for the user // runPlan takes care of outputting available versions to upgrade to for the user
@ -247,8 +247,7 @@ func runPlan(flags *planFlags, args []string, printer output.Printer) error {
// Compute which upgrade possibilities there are // Compute which upgrade possibilities there are
klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities") klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities")
klog.V(1).Infoln("[upgrade/plan] Fetching available versions to upgrade to") availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, isExternalEtcd, client, constants.GetStaticPodDirectory(), printer)
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, isExternalEtcd, client, constants.GetStaticPodDirectory())
if err != nil { if err != nil {
return errors.Wrap(err, "[upgrade/versions] FATAL") return errors.Wrap(err, "[upgrade/versions] FATAL")
} }
@ -291,7 +290,7 @@ func runPlan(flags *planFlags, args []string, printer output.Printer) error {
// 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 []outputapi.ComponentUpgradePlan, up *upgrade.Upgrade, name string) []outputapi.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
@ -302,7 +301,7 @@ func appendDNSComponent(components []outputapi.ComponentUpgradePlan, up *upgrade
} }
// genUpgradePlan generates output-friendly upgrade plan out of upgrade.Upgrade structure // genUpgradePlan generates output-friendly upgrade plan out of upgrade.Upgrade structure
func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapi.UpgradePlan, string, error) { func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha2.UpgradePlan, string, error) {
newK8sVersion, err := version.ParseSemantic(up.After.KubeVersion) newK8sVersion, err := version.ParseSemantic(up.After.KubeVersion)
if err != nil { if err != nil {
return nil, "", errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", up.After.KubeVersion) return nil, "", errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", up.After.KubeVersion)
@ -317,7 +316,7 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapi.Upgrad
} }
} }
components := []outputapi.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
@ -339,10 +338,10 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapi.Upgrad
components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion)) components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion))
} }
return &outputapi.UpgradePlan{Components: components}, unstableVersionFlag, nil return &outputapiv1alpha2.UpgradePlan{Components: components}, unstableVersionFlag, nil
} }
func getComponentConfigVersionStates(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, cfgPath string) ([]outputapi.ComponentConfigVersionState, error) { func getComponentConfigVersionStates(cfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, cfgPath string) ([]outputapiv1alpha2.ComponentConfigVersionState, error) {
docmap := kubeadmapi.DocumentMap{} docmap := kubeadmapi.DocumentMap{}
if cfgPath != "" { if cfgPath != "" {
@ -361,16 +360,7 @@ func getComponentConfigVersionStates(cfg *kubeadmapi.ClusterConfiguration, clien
} }
// printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to // printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to
func printUpgradePlan(up *upgrade.Upgrade, plan *outputapi.UpgradePlan, unstableVersionFlag string, isExternalEtcd bool, w io.Writer, printer output.Printer) { func printUpgradePlan(up *upgrade.Upgrade, plan *outputapiv1alpha2.UpgradePlan, unstableVersionFlag string, isExternalEtcd bool, writer io.Writer, printer output.Printer) {
// The tab writer writes to the "real" writer w
tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0)
// endOfTable helper function flashes table writer
endOfTable := func() {
tabw.Flush()
printer.Fprintln(w, "")
}
printHeader := true printHeader := true
printManualUpgradeHeader := true printManualUpgradeHeader := true
for _, component := range plan.Components { for _, component := range plan.Components {
@ -379,41 +369,40 @@ func printUpgradePlan(up *upgrade.Upgrade, plan *outputapi.UpgradePlan, unstable
continue continue
} else if component.Name == constants.Kubelet { } else if component.Name == constants.Kubelet {
if printManualUpgradeHeader { if printManualUpgradeHeader {
printer.Fprintln(w, "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.Fprintln(tabw, "COMPONENT\tCURRENT\tTARGET") printer.PrintObj(newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion), writer)
printer.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion)
printManualUpgradeHeader = false printManualUpgradeHeader = false
} else { } else {
printer.Fprintf(tabw, "%s\t%s\t%s\n", "", component.CurrentVersion, component.NewVersion) printer.PrintObj(newComponentUpgradePlan("", component.CurrentVersion, component.NewVersion), writer)
} }
} else { } else {
if printHeader { if printHeader {
// End of manual upgrades table // End of manual upgrades table
endOfTable() printer.Flush(writer)
printer.Fprintf(w, "Upgrade to the latest %s:\n", up.Description) printer.Fprintln(writer, "")
printer.Fprintln(w, "") printer.Fprintf(writer, "Upgrade to the latest %s:\n", up.Description)
printer.Fprintln(tabw, "COMPONENT\tCURRENT\tTARGET") printer.Fprintln(writer, "")
printHeader = false printHeader = false
} }
printer.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion) printer.PrintObj(newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion), writer)
} }
} }
// End of control plane table printer.Flush(writer)
endOfTable() printer.Fprintln(writer, "")
//fmt.Fprintln(w, "") printer.Fprintln(writer, "You can now apply the upgrade by executing the following command:")
printer.Fprintln(w, "You can now apply the upgrade by executing the following command:") printer.Fprintln(writer, "")
printer.Fprintln(w, "") printer.Fprintf(writer, "\tkubeadm upgrade apply %s%s\n", up.After.KubeVersion, unstableVersionFlag)
printer.Fprintf(w, "\tkubeadm upgrade apply %s%s\n", up.After.KubeVersion, unstableVersionFlag) printer.Fprintln(writer, "")
printer.Fprintln(w, "")
if up.Before.KubeadmVersion != up.After.KubeadmVersion { if up.Before.KubeadmVersion != up.After.KubeadmVersion {
printer.Fprintf(w, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", up.After.KubeadmVersion) printer.Fprintf(writer, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", up.After.KubeadmVersion)
printer.Fprintln(w, "") printer.Fprintln(writer, "")
} }
printLineSeparator(w, printer) printLineSeparator(writer, printer)
printer.Close(writer)
} }
// sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically // sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically
@ -445,7 +434,7 @@ func printLineSeparator(w io.Writer, printer output.Printer) {
printer.Fprintln(w, "") printer.Fprintln(w, "")
} }
func printComponentConfigVersionStates(versionStates []outputapi.ComponentConfigVersionState, w io.Writer, printer output.Printer) { func printComponentConfigVersionStates(versionStates []outputapiv1alpha2.ComponentConfigVersionState, w io.Writer, printer output.Printer) {
if len(versionStates) == 0 { if len(versionStates) == 0 {
printer.Fprintln(w, "No information available on component configs.") printer.Fprintln(w, "No information available on component configs.")
return return

View File

@ -22,6 +22,7 @@ import (
"testing" "testing"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
) )
func TestSortedSliceFromStringIntMap(t *testing.T) { func TestSortedSliceFromStringIntMap(t *testing.T) {
@ -441,13 +442,19 @@ _____________________________________________________________________
for _, rt := range tests { for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) { t.Run(rt.name, func(t *testing.T) {
rt.buf = bytes.NewBufferString("") rt.buf = bytes.NewBufferString("")
outputFlags := newUpgradePlanPrintFlags(output.TextOutput)
printer, err := outputFlags.ToPrinter()
if err != nil {
t.Errorf("failed ToPrinter, err: %+v", err)
}
// Generate and print upgrade plans // Generate and print upgrade plans
for _, up := range rt.upgrades { for _, up := range rt.upgrades {
plan, unstableVersionFlag, err := genUpgradePlan(&up, rt.externalEtcd) plan, unstableVersionFlag, err := genUpgradePlan(&up, rt.externalEtcd)
if err != nil { if err != nil {
t.Errorf("failed genUpgradePlan, err: %+v", err) t.Errorf("failed genUpgradePlan, err: %+v", err)
} }
printUpgradePlan(&up, plan, unstableVersionFlag, rt.externalEtcd, rt.buf) printUpgradePlan(&up, plan, unstableVersionFlag, rt.externalEtcd, rt.buf, printer)
} }
actualBytes := rt.buf.Bytes() actualBytes := rt.buf.Bytes()
if !bytes.Equal(actualBytes, rt.expectedBytes) { if !bytes.Equal(actualBytes, rt.expectedBytes) {
@ -460,3 +467,164 @@ _____________________________________________________________________
}) })
} }
} }
func TestPrintAvailableUpgradesStructured(t *testing.T) {
upgrades := []upgrade.Upgrade{
{
Description: "version in the v1.8 series",
Before: upgrade.ClusterState{
KubeVersion: "v1.8.1",
KubeletVersions: map[string]uint16{
"v1.8.1": 1,
},
KubeadmVersion: "v1.8.2",
DNSVersion: "1.14.5",
EtcdVersion: "3.0.17",
},
After: upgrade.ClusterState{
KubeVersion: "v1.8.3",
KubeadmVersion: "v1.8.3",
DNSVersion: "1.14.5",
EtcdVersion: "3.0.17",
},
},
}
var tests = []struct {
name string
outputFormat string
buf *bytes.Buffer
expected string
externalEtcd bool
}{
{
name: "JSON output",
outputFormat: "json",
expected: `{
"kind": "UpgradePlan",
"apiVersion": "output.kubeadm.k8s.io/v1alpha2",
"components": [
{
"name": "kubelet",
"currentVersion": "1 x v1.8.1",
"newVersion": "v1.8.3"
},
{
"name": "kube-apiserver",
"currentVersion": "v1.8.1",
"newVersion": "v1.8.3"
},
{
"name": "kube-controller-manager",
"currentVersion": "v1.8.1",
"newVersion": "v1.8.3"
},
{
"name": "kube-scheduler",
"currentVersion": "v1.8.1",
"newVersion": "v1.8.3"
},
{
"name": "kube-proxy",
"currentVersion": "v1.8.1",
"newVersion": "v1.8.3"
},
{
"name": "CoreDNS",
"currentVersion": "1.14.5",
"newVersion": "1.14.5"
},
{
"name": "etcd",
"currentVersion": "3.0.17",
"newVersion": "3.0.17"
}
],
"configVersions": null
}
`,
},
{
name: "YAML output",
outputFormat: "yaml",
expected: `apiVersion: output.kubeadm.k8s.io/v1alpha2
components:
- currentVersion: 1 x v1.8.1
name: kubelet
newVersion: v1.8.3
- currentVersion: v1.8.1
name: kube-apiserver
newVersion: v1.8.3
- currentVersion: v1.8.1
name: kube-controller-manager
newVersion: v1.8.3
- currentVersion: v1.8.1
name: kube-scheduler
newVersion: v1.8.3
- currentVersion: v1.8.1
name: kube-proxy
newVersion: v1.8.3
- currentVersion: 1.14.5
name: CoreDNS
newVersion: 1.14.5
- currentVersion: 3.0.17
name: etcd
newVersion: 3.0.17
configVersions: null
kind: UpgradePlan
`,
},
{
name: "Text output",
outputFormat: "Text",
expected: `Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT CURRENT TARGET
kubelet 1 x v1.8.1 v1.8.3
Upgrade to the latest version in the v1.8 series:
COMPONENT CURRENT TARGET
kube-apiserver v1.8.1 v1.8.3
kube-controller-manager v1.8.1 v1.8.3
kube-scheduler v1.8.1 v1.8.3
kube-proxy v1.8.1 v1.8.3
CoreDNS 1.14.5 1.14.5
etcd 3.0.17 3.0.17
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.8.3
Note: Before you can perform this upgrade, you have to update kubeadm to v1.8.3.
_____________________________________________________________________
`,
},
}
for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) {
rt.buf = bytes.NewBufferString("")
outputFlags := newUpgradePlanPrintFlags(rt.outputFormat)
printer, err := outputFlags.ToPrinter()
if err != nil {
t.Errorf("failed ToPrinter, err: %+v", err)
}
// Generate and print upgrade plans
for _, up := range upgrades {
plan, unstableVersionFlag, err := genUpgradePlan(&up, rt.externalEtcd)
if err != nil {
t.Errorf("failed genUpgradePlan, err: %+v", err)
}
printUpgradePlan(&up, plan, unstableVersionFlag, rt.externalEtcd, rt.buf, printer)
}
actual := rt.buf.String()
if actual != rt.expected {
t.Errorf("failed PrintAvailableUpgrades:\n\nexpected:\n%s\n\nactual:\n%s", rt.expected, actual)
}
})
}
}

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"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
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"
@ -289,7 +289,7 @@ func FetchFromClusterWithLocalOverwrites(clusterCfg *kubeadmapi.ClusterConfigura
// GetVersionStates returns a slice of ComponentConfigVersionState structs // GetVersionStates returns a slice of ComponentConfigVersionState structs
// describing all supported component config groups that were identified on the cluster // describing all supported component config groups that were identified on the cluster
func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, docmap kubeadmapi.DocumentMap) ([]output.ComponentConfigVersionState, error) { func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface, docmap kubeadmapi.DocumentMap) ([]outputapiv1alpha2.ComponentConfigVersionState, error) {
// We don't want to modify clusterCfg so we make a working deep copy of it. // We don't want to modify clusterCfg so we make a working deep copy of it.
// Also, we don't want the defaulted component configs so we get rid of them. // Also, we don't want the defaulted component configs so we get rid of them.
scratchClusterCfg := clusterCfg.DeepCopy() scratchClusterCfg := clusterCfg.DeepCopy()
@ -308,13 +308,13 @@ func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client client
} }
} }
results := []output.ComponentConfigVersionState{} results := []outputapiv1alpha2.ComponentConfigVersionState{}
for _, handler := range known { for _, handler := range known {
group := handler.GroupVersion.Group group := handler.GroupVersion.Group
if vererr, ok := multipleVerErrs[group]; ok { if vererr, ok := multipleVerErrs[group]; ok {
// If there is an UnsupportedConfigVersionError then we are dealing with a case where the config was user // If there is an UnsupportedConfigVersionError then we are dealing with a case where the config was user
// supplied and requires manual upgrade // supplied and requires manual upgrade
results = append(results, output.ComponentConfigVersionState{ results = append(results, outputapiv1alpha2.ComponentConfigVersionState{
Group: group, Group: group,
CurrentVersion: vererr.OldVersion.Version, CurrentVersion: vererr.OldVersion.Version,
PreferredVersion: vererr.CurrentVersion.Version, PreferredVersion: vererr.CurrentVersion.Version,
@ -322,7 +322,7 @@ func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client client
}) })
} else if _, ok := scratchClusterCfg.ComponentConfigs[group]; ok { } else if _, ok := scratchClusterCfg.ComponentConfigs[group]; ok {
// Normally loaded component config. No manual upgrade required on behalf of users. // Normally loaded component config. No manual upgrade required on behalf of users.
results = append(results, output.ComponentConfigVersionState{ results = append(results, outputapiv1alpha2.ComponentConfigVersionState{
Group: group, Group: group,
CurrentVersion: handler.GroupVersion.Version, // Currently kubeadm supports only one version per API CurrentVersion: handler.GroupVersion.Version, // Currently kubeadm supports only one version per API
PreferredVersion: handler.GroupVersion.Version, // group so we can get away with these being the same PreferredVersion: handler.GroupVersion.Version, // group so we can get away with these being the same
@ -331,7 +331,7 @@ func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client client
// This config was either not present (user did not install an addon) or the config was unsupported kubeadm // This config was either not present (user did not install an addon) or the config was unsupported kubeadm
// generated one and is therefore skipped so we can automatically re-generate it (no action required on // generated one and is therefore skipped so we can automatically re-generate it (no action required on
// behalf of the user). // behalf of the user).
results = append(results, output.ComponentConfigVersionState{ results = append(results, outputapiv1alpha2.ComponentConfigVersionState{
Group: group, Group: group,
PreferredVersion: handler.GroupVersion.Version, PreferredVersion: handler.GroupVersion.Version,
}) })

View File

@ -35,7 +35,7 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
outputapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/output" outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
) )
@ -615,12 +615,12 @@ func TestFetchFromClusterWithLocalOverwrites(t *testing.T) {
func TestGetVersionStates(t *testing.T) { func TestGetVersionStates(t *testing.T) {
fakeKnownContext(func() { fakeKnownContext(func() {
versionStateCurrent := outputapi.ComponentConfigVersionState{ versionStateCurrent := outputapiv1alpha2.ComponentConfigVersionState{
Group: kubeadmapiv1.GroupName, Group: kubeadmapiv1.GroupName,
CurrentVersion: currentClusterConfigVersion, CurrentVersion: currentClusterConfigVersion,
PreferredVersion: currentClusterConfigVersion, PreferredVersion: currentClusterConfigVersion,
} }
versionStateOld := outputapi.ComponentConfigVersionState{ versionStateOld := outputapiv1alpha2.ComponentConfigVersionState{
Group: kubeadmapiv1.GroupName, Group: kubeadmapiv1.GroupName,
CurrentVersion: oldClusterConfigVersion, CurrentVersion: oldClusterConfigVersion,
PreferredVersion: currentClusterConfigVersion, PreferredVersion: currentClusterConfigVersion,
@ -631,7 +631,7 @@ func TestGetVersionStates(t *testing.T) {
desc string desc string
obj runtime.Object obj runtime.Object
config string config string
expected outputapi.ComponentConfigVersionState expected outputapiv1alpha2.ComponentConfigVersionState
}{ }{
{ {
desc: "appropriate cluster object without overwrite", desc: "appropriate cluster object without overwrite",
@ -687,7 +687,7 @@ func TestGetVersionStates(t *testing.T) {
{ {
desc: "old signed config without an overwrite", desc: "old signed config without an overwrite",
obj: testClusterConfigMap(oldFooClusterConfig, true), obj: testClusterConfigMap(oldFooClusterConfig, true),
expected: outputapi.ComponentConfigVersionState{ expected: outputapiv1alpha2.ComponentConfigVersionState{
Group: kubeadmapiv1.GroupName, Group: kubeadmapiv1.GroupName,
CurrentVersion: "", // The config is treated as if it's missing CurrentVersion: "", // The config is treated as if it's missing
PreferredVersion: currentClusterConfigVersion, PreferredVersion: currentClusterConfigVersion,

View File

@ -26,6 +26,7 @@ import (
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
) )
// Upgrade defines an upgrade possibility to upgrade from a current version to a new one // Upgrade defines an upgrade possibility to upgrade from a current version to a new one
@ -72,7 +73,9 @@ type ClusterState struct {
// GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which // GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which
// kinds of upgrades can be performed // kinds of upgrades can be performed
func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed, externalEtcd bool, client clientset.Interface, manifestsDir string) ([]Upgrade, error) { func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed, externalEtcd bool, client clientset.Interface, manifestsDir string, printer output.Printer) ([]Upgrade, error) {
printer.Printf("[upgrade] Fetching available versions to upgrade to\n")
// Collect the upgrades kubeadm can do in this list // Collect the upgrades kubeadm can do in this list
upgrades := []Upgrade{} upgrades := []Upgrade{}
@ -81,14 +84,14 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
if err != nil { if err != nil {
return upgrades, err return upgrades, err
} }
fmt.Printf("[upgrade/versions] Cluster version: %s\n", clusterVersionStr) printer.Printf("[upgrade/versions] Cluster version: %s\n", clusterVersionStr)
// Get current kubeadm CLI version // Get current kubeadm CLI version
kubeadmVersionStr, kubeadmVersion, err := versionGetterImpl.KubeadmVersion() kubeadmVersionStr, kubeadmVersion, err := versionGetterImpl.KubeadmVersion()
if err != nil { if err != nil {
return upgrades, err return upgrades, err
} }
fmt.Printf("[upgrade/versions] kubeadm version: %s\n", kubeadmVersionStr) printer.Printf("[upgrade/versions] kubeadm version: %s\n", kubeadmVersionStr)
// Get and output the current latest stable version // Get and output the current latest stable version
stableVersionStr, stableVersion, err := versionGetterImpl.VersionFromCILabel("stable", "stable version") stableVersionStr, stableVersion, err := versionGetterImpl.VersionFromCILabel("stable", "stable version")
@ -97,7 +100,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
klog.Warningf("[upgrade/versions] WARNING: Falling back to current kubeadm version as latest stable version") klog.Warningf("[upgrade/versions] WARNING: Falling back to current kubeadm version as latest stable version")
stableVersionStr, stableVersion = kubeadmVersionStr, kubeadmVersion stableVersionStr, stableVersion = kubeadmVersionStr, kubeadmVersion
} else { } else {
fmt.Printf("[upgrade/versions] Target version: %s\n", stableVersionStr) printer.Printf("[upgrade/versions] Target version: %s\n", stableVersionStr)
} }
// Get the kubelet versions in the cluster // Get the kubelet versions in the cluster
@ -146,7 +149,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA
if err != nil { if err != nil {
klog.Warningf("[upgrade/versions] WARNING: %v\n", err) klog.Warningf("[upgrade/versions] WARNING: %v\n", err)
} else { } else {
fmt.Printf("[upgrade/versions] Latest %s: %s\n", description, patchVersionStr) printer.Printf("[upgrade/versions] Latest %s: %s\n", description, patchVersionStr)
// Check if a minor version upgrade is possible when a patch release exists // Check if a minor version upgrade is possible when a patch release exists
// It's only possible if the latest patch version is higher than the current patch version // It's only possible if the latest patch version is higher than the current patch version
@ -264,6 +267,9 @@ 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")
return upgrades, nil return upgrades, nil
} }

View File

@ -31,6 +31,7 @@ import (
clientsetfake "k8s.io/client-go/kubernetes/fake" clientsetfake "k8s.io/client-go/kubernetes/fake"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
) )
type fakeVersionGetter struct { type fakeVersionGetter struct {
@ -652,7 +653,7 @@ func TestGetAvailableUpgrades(t *testing.T) {
t.Fatalf("Unable to create test static pod manifest: %v", err) t.Fatalf("Unable to create test static pod manifest: %v", err)
} }
actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, rt.externalEtcd, client, manifestsDir) actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, rt.externalEtcd, client, manifestsDir, &output.TextPrinter{})
if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) { if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) {
t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades) t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades)
} }