in the new output API output.kubeadm.k8s.io/v1alpha3 modify the UpgradePlan structure to include a list of multiple available upgrades.
This commit is contained in:
		| @@ -33,6 +33,7 @@ import ( | ||||
| func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { | ||||
| 	return []interface{}{ | ||||
| 		fuzzBootstrapToken, | ||||
| 		fuzzUpgradePlan, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -45,3 +46,10 @@ func fuzzBootstrapToken(obj *output.BootstrapToken, c fuzz.Continue) { | ||||
| 	obj.Usages = []string{"authentication", "signing"} | ||||
| 	obj.Groups = []string{constants.NodeBootstrapTokenAuthGroup} | ||||
| } | ||||
|  | ||||
| // TODO: Remove this func when v1alpha2 is removed | ||||
| func fuzzUpgradePlan(obj *output.UpgradePlan, c fuzz.Continue) { | ||||
| 	// Pin the value to avoid round tripping the AvailableUpgrades field | ||||
| 	// which is not present in the v1alpha2 version. | ||||
| 	obj.AvailableUpgrades = nil | ||||
| } | ||||
|   | ||||
| @@ -73,14 +73,28 @@ type ComponentConfigVersionState struct { | ||||
|  | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // AvailableUpgrade represents information for a single available upgrade. | ||||
| type AvailableUpgrade struct { | ||||
| 	metav1.TypeMeta | ||||
|  | ||||
| 	Description string | ||||
|  | ||||
| 	Components []ComponentUpgradePlan | ||||
| } | ||||
|  | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // UpgradePlan represents information about upgrade plan for the output | ||||
| // produced by 'kubeadm upgrade plan' | ||||
| type UpgradePlan struct { | ||||
| 	metav1.TypeMeta | ||||
|  | ||||
| 	Components []ComponentUpgradePlan | ||||
| 	AvailableUpgrades []AvailableUpgrade | ||||
|  | ||||
| 	ConfigVersions []ComponentConfigVersionState | ||||
|  | ||||
| 	// TODO: Remove this field when v1alpha2 is removed | ||||
| 	Components []ComponentUpgradePlan | ||||
| } | ||||
|  | ||||
| // Certificate represents information for a certificate or a certificate authority when using the check-expiration command. | ||||
|   | ||||
							
								
								
									
										28
									
								
								cmd/kubeadm/app/apis/output/v1alpha2/conversion.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								cmd/kubeadm/app/apis/output/v1alpha2/conversion.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
| Copyright 2024 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package v1alpha2 | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/apimachinery/pkg/conversion" | ||||
|  | ||||
| 	"k8s.io/kubernetes/cmd/kubeadm/app/apis/output" | ||||
| ) | ||||
|  | ||||
| // Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan converts a private UpgradePlan to public UpgradePlan. | ||||
| func Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { | ||||
| 	return autoConvert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in, out, s) | ||||
| } | ||||
| @@ -81,7 +81,7 @@ func RegisterConversions(s *runtime.Scheme) error { | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := s.AddGeneratedConversionFunc((*output.UpgradePlan)(nil), (*UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { | ||||
| 	if err := s.AddConversionFunc((*output.UpgradePlan)(nil), (*UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { | ||||
| 		return Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan(a.(*output.UpgradePlan), b.(*UpgradePlan), scope) | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| @@ -191,12 +191,8 @@ 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)) | ||||
| 	// WARNING: in.AvailableUpgrades requires manual conversion: does not exist in peer-type | ||||
| 	out.ConfigVersions = *(*[]ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions)) | ||||
| 	out.Components = *(*[]ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan is an autogenerated conversion function. | ||||
| func Convert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { | ||||
| 	return autoConvert_output_UpgradePlan_To_v1alpha2_UpgradePlan(in, out, s) | ||||
| } | ||||
|   | ||||
							
								
								
									
										28
									
								
								cmd/kubeadm/app/apis/output/v1alpha3/conversion.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								cmd/kubeadm/app/apis/output/v1alpha3/conversion.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
| Copyright 2024 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package v1alpha3 | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/apimachinery/pkg/conversion" | ||||
|  | ||||
| 	"k8s.io/kubernetes/cmd/kubeadm/app/apis/output" | ||||
| ) | ||||
|  | ||||
| // Convert_output_UpgradePlan_To_v1alpha3_UpgradePlan converts a private UpgradePlan to public UpgradePlan. | ||||
| func Convert_output_UpgradePlan_To_v1alpha3_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { | ||||
| 	return autoConvert_output_UpgradePlan_To_v1alpha3_UpgradePlan(in, out, s) | ||||
| } | ||||
| @@ -27,4 +27,6 @@ limitations under the License. | ||||
| // Changes since v1alpha2: | ||||
| //   - Added support for outputting certificate expiration information for "kubeadm certs check-expiration" | ||||
| //     with the CertificateExpirationInfo structure. | ||||
| //   - Introduce a (breaking) change to the UpgradePlan structure used by "kubeadm upgrade plan". | ||||
| //     UpgradePlan now contains a list of AvailableUpgrade structures. | ||||
| package v1alpha3 // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha3" | ||||
|   | ||||
| @@ -73,14 +73,25 @@ type ComponentConfigVersionState struct { | ||||
|  | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // AvailableUpgrade represents information for a single available upgrade. | ||||
| type AvailableUpgrade struct { | ||||
| 	metav1.TypeMeta | ||||
|  | ||||
| 	Description string `json:"description"` | ||||
|  | ||||
| 	Components []ComponentUpgradePlan `json:"components"` | ||||
| } | ||||
|  | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // UpgradePlan represents information about upgrade plan for the output | ||||
| // produced by 'kubeadm upgrade plan' | ||||
| type UpgradePlan struct { | ||||
| 	metav1.TypeMeta | ||||
|  | ||||
| 	Components []ComponentUpgradePlan `json:"components"` | ||||
| 	AvailableUpgrades []AvailableUpgrade `json:"availableUpgrades,omitempty"` | ||||
|  | ||||
| 	ConfigVersions []ComponentConfigVersionState `json:"configVersions"` | ||||
| 	ConfigVersions []ComponentConfigVersionState `json:"configVersions,omitempty"` | ||||
| } | ||||
|  | ||||
| // Certificate represents information for a certificate or a certificate authority when using the check-expiration command. | ||||
|   | ||||
| @@ -36,6 +36,16 @@ func init() { | ||||
| // RegisterConversions adds conversion functions to the given scheme. | ||||
| // Public to allow building arbitrary schemes. | ||||
| func RegisterConversions(s *runtime.Scheme) error { | ||||
| 	if err := s.AddGeneratedConversionFunc((*AvailableUpgrade)(nil), (*output.AvailableUpgrade)(nil), func(a, b interface{}, scope conversion.Scope) error { | ||||
| 		return Convert_v1alpha3_AvailableUpgrade_To_output_AvailableUpgrade(a.(*AvailableUpgrade), b.(*output.AvailableUpgrade), scope) | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := s.AddGeneratedConversionFunc((*output.AvailableUpgrade)(nil), (*AvailableUpgrade)(nil), func(a, b interface{}, scope conversion.Scope) error { | ||||
| 		return Convert_output_AvailableUpgrade_To_v1alpha3_AvailableUpgrade(a.(*output.AvailableUpgrade), b.(*AvailableUpgrade), scope) | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := s.AddGeneratedConversionFunc((*BootstrapToken)(nil), (*output.BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { | ||||
| 		return Convert_v1alpha3_BootstrapToken_To_output_BootstrapToken(a.(*BootstrapToken), b.(*output.BootstrapToken), scope) | ||||
| 	}); err != nil { | ||||
| @@ -101,7 +111,7 @@ func RegisterConversions(s *runtime.Scheme) error { | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := s.AddGeneratedConversionFunc((*output.UpgradePlan)(nil), (*UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { | ||||
| 	if err := s.AddConversionFunc((*output.UpgradePlan)(nil), (*UpgradePlan)(nil), func(a, b interface{}, scope conversion.Scope) error { | ||||
| 		return Convert_output_UpgradePlan_To_v1alpha3_UpgradePlan(a.(*output.UpgradePlan), b.(*UpgradePlan), scope) | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| @@ -109,6 +119,28 @@ func RegisterConversions(s *runtime.Scheme) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func autoConvert_v1alpha3_AvailableUpgrade_To_output_AvailableUpgrade(in *AvailableUpgrade, out *output.AvailableUpgrade, s conversion.Scope) error { | ||||
| 	out.Description = in.Description | ||||
| 	out.Components = *(*[]output.ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Convert_v1alpha3_AvailableUpgrade_To_output_AvailableUpgrade is an autogenerated conversion function. | ||||
| func Convert_v1alpha3_AvailableUpgrade_To_output_AvailableUpgrade(in *AvailableUpgrade, out *output.AvailableUpgrade, s conversion.Scope) error { | ||||
| 	return autoConvert_v1alpha3_AvailableUpgrade_To_output_AvailableUpgrade(in, out, s) | ||||
| } | ||||
|  | ||||
| func autoConvert_output_AvailableUpgrade_To_v1alpha3_AvailableUpgrade(in *output.AvailableUpgrade, out *AvailableUpgrade, s conversion.Scope) error { | ||||
| 	out.Description = in.Description | ||||
| 	out.Components = *(*[]ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Convert_output_AvailableUpgrade_To_v1alpha3_AvailableUpgrade is an autogenerated conversion function. | ||||
| func Convert_output_AvailableUpgrade_To_v1alpha3_AvailableUpgrade(in *output.AvailableUpgrade, out *AvailableUpgrade, s conversion.Scope) error { | ||||
| 	return autoConvert_output_AvailableUpgrade_To_v1alpha3_AvailableUpgrade(in, out, s) | ||||
| } | ||||
|  | ||||
| func autoConvert_v1alpha3_BootstrapToken_To_output_BootstrapToken(in *BootstrapToken, out *output.BootstrapToken, s conversion.Scope) error { | ||||
| 	out.BootstrapToken = in.BootstrapToken | ||||
| 	return nil | ||||
| @@ -252,7 +284,7 @@ func Convert_output_Images_To_v1alpha3_Images(in *output.Images, out *Images, s | ||||
| } | ||||
|  | ||||
| func autoConvert_v1alpha3_UpgradePlan_To_output_UpgradePlan(in *UpgradePlan, out *output.UpgradePlan, s conversion.Scope) error { | ||||
| 	out.Components = *(*[]output.ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) | ||||
| 	out.AvailableUpgrades = *(*[]output.AvailableUpgrade)(unsafe.Pointer(&in.AvailableUpgrades)) | ||||
| 	out.ConfigVersions = *(*[]output.ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions)) | ||||
| 	return nil | ||||
| } | ||||
| @@ -263,12 +295,8 @@ func Convert_v1alpha3_UpgradePlan_To_output_UpgradePlan(in *UpgradePlan, out *ou | ||||
| } | ||||
|  | ||||
| func autoConvert_output_UpgradePlan_To_v1alpha3_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { | ||||
| 	out.Components = *(*[]ComponentUpgradePlan)(unsafe.Pointer(&in.Components)) | ||||
| 	out.AvailableUpgrades = *(*[]AvailableUpgrade)(unsafe.Pointer(&in.AvailableUpgrades)) | ||||
| 	out.ConfigVersions = *(*[]ComponentConfigVersionState)(unsafe.Pointer(&in.ConfigVersions)) | ||||
| 	// WARNING: in.Components requires manual conversion: does not exist in peer-type | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Convert_output_UpgradePlan_To_v1alpha3_UpgradePlan is an autogenerated conversion function. | ||||
| func Convert_output_UpgradePlan_To_v1alpha3_UpgradePlan(in *output.UpgradePlan, out *UpgradePlan, s conversion.Scope) error { | ||||
| 	return autoConvert_output_UpgradePlan_To_v1alpha3_UpgradePlan(in, out, s) | ||||
| } | ||||
|   | ||||
| @@ -25,6 +25,36 @@ import ( | ||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. | ||||
| func (in *AvailableUpgrade) DeepCopyInto(out *AvailableUpgrade) { | ||||
| 	*out = *in | ||||
| 	out.TypeMeta = in.TypeMeta | ||||
| 	if in.Components != nil { | ||||
| 		in, out := &in.Components, &out.Components | ||||
| 		*out = make([]ComponentUpgradePlan, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AvailableUpgrade. | ||||
| func (in *AvailableUpgrade) DeepCopy() *AvailableUpgrade { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(AvailableUpgrade) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
|  | ||||
| // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. | ||||
| func (in *AvailableUpgrade) DeepCopyObject() runtime.Object { | ||||
| 	if c := in.DeepCopy(); c != nil { | ||||
| 		return c | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. | ||||
| func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) { | ||||
| 	*out = *in | ||||
| @@ -182,10 +212,12 @@ func (in *Images) DeepCopyObject() runtime.Object { | ||||
| func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) { | ||||
| 	*out = *in | ||||
| 	out.TypeMeta = in.TypeMeta | ||||
| 	if in.Components != nil { | ||||
| 		in, out := &in.Components, &out.Components | ||||
| 		*out = make([]ComponentUpgradePlan, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	if in.AvailableUpgrades != nil { | ||||
| 		in, out := &in.AvailableUpgrades, &out.AvailableUpgrades | ||||
| 		*out = make([]AvailableUpgrade, len(*in)) | ||||
| 		for i := range *in { | ||||
| 			(*in)[i].DeepCopyInto(&(*out)[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	if in.ConfigVersions != nil { | ||||
| 		in, out := &in.ConfigVersions, &out.ConfigVersions | ||||
|   | ||||
							
								
								
									
										45
									
								
								cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										45
									
								
								cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go
									
									
									
										generated
									
									
									
								
							| @@ -25,6 +25,36 @@ import ( | ||||
| 	runtime "k8s.io/apimachinery/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. | ||||
| func (in *AvailableUpgrade) DeepCopyInto(out *AvailableUpgrade) { | ||||
| 	*out = *in | ||||
| 	out.TypeMeta = in.TypeMeta | ||||
| 	if in.Components != nil { | ||||
| 		in, out := &in.Components, &out.Components | ||||
| 		*out = make([]ComponentUpgradePlan, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AvailableUpgrade. | ||||
| func (in *AvailableUpgrade) DeepCopy() *AvailableUpgrade { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(AvailableUpgrade) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
|  | ||||
| // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. | ||||
| func (in *AvailableUpgrade) DeepCopyObject() runtime.Object { | ||||
| 	if c := in.DeepCopy(); c != nil { | ||||
| 		return c | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. | ||||
| func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) { | ||||
| 	*out = *in | ||||
| @@ -182,16 +212,23 @@ func (in *Images) DeepCopyObject() runtime.Object { | ||||
| func (in *UpgradePlan) DeepCopyInto(out *UpgradePlan) { | ||||
| 	*out = *in | ||||
| 	out.TypeMeta = in.TypeMeta | ||||
| 	if in.Components != nil { | ||||
| 		in, out := &in.Components, &out.Components | ||||
| 		*out = make([]ComponentUpgradePlan, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	if in.AvailableUpgrades != nil { | ||||
| 		in, out := &in.AvailableUpgrades, &out.AvailableUpgrades | ||||
| 		*out = make([]AvailableUpgrade, len(*in)) | ||||
| 		for i := range *in { | ||||
| 			(*in)[i].DeepCopyInto(&(*out)[i]) | ||||
| 		} | ||||
| 	} | ||||
| 	if in.ConfigVersions != nil { | ||||
| 		in, out := &in.ConfigVersions, &out.ConfigVersions | ||||
| 		*out = make([]ComponentConfigVersionState, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| 	if in.Components != nil { | ||||
| 		in, out := &in.Components, &out.Components | ||||
| 		*out = make([]ComponentUpgradePlan, len(*in)) | ||||
| 		copy(*out, *in) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -31,11 +31,10 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/version" | ||||
| 	"k8s.io/cli-runtime/pkg/genericclioptions" | ||||
| 	"k8s.io/cli-runtime/pkg/printers" | ||||
| 	"k8s.io/klog/v2" | ||||
|  | ||||
| 	outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme" | ||||
| 	outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2" | ||||
| 	outputapiv1alpha3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha3" | ||||
| 	cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" | ||||
| 	"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs" | ||||
| 	"k8s.io/kubernetes/cmd/kubeadm/app/constants" | ||||
| @@ -59,7 +58,7 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command { | ||||
| 		applyPlanFlags: apf, | ||||
| 	} | ||||
|  | ||||
| 	outputFlags := newUpgradePlanPrintFlags(output.TextOutput) | ||||
| 	outputFlags := output.NewOutputFlags(&upgradePlanTextPrintFlags{}).WithTypeSetter(outputapischeme.Scheme).WithDefaultOutput(output.TextOutput) | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "plan [version] [flags]", | ||||
| @@ -82,174 +81,15 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command { | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| // newComponentUpgradePlan helper creates outputapiv1alpha2.ComponentUpgradePlan object | ||||
| func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha2.ComponentUpgradePlan { | ||||
| 	return outputapiv1alpha2.ComponentUpgradePlan{ | ||||
| // newComponentUpgradePlan helper creates outputapiv1alpha3.ComponentUpgradePlan object | ||||
| func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv1alpha3.ComponentUpgradePlan { | ||||
| 	return outputapiv1alpha3.ComponentUpgradePlan{ | ||||
| 		Name:           name, | ||||
| 		CurrentVersion: currentVersion, | ||||
| 		NewVersion:     newVersion, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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 *upgradePlanJSONYamlPrintFlags | ||||
| 	// 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 | ||||
| 	// OutputFormat contains currently set output format | ||||
| 	OutputFormat string | ||||
| } | ||||
|  | ||||
| func newUpgradePlanPrintFlags(outputFormat string) *upgradePlanPrintFlags { | ||||
| 	return &upgradePlanPrintFlags{ | ||||
| 		JSONYamlPrintFlags: &upgradePlanJSONYamlPrintFlags{}, | ||||
| 		TextPrintFlags:     &upgradePlanTextPrintFlags{}, | ||||
| 		TypeSetterPrinter:  printers.NewTypeSetter(outputapischeme.Scheme), | ||||
| 		OutputFormat:       strings.ToLower(outputFormat), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AllowedFormats returns a list of allowed output formats | ||||
| func (pf *upgradePlanPrintFlags) AllowedFormats() []string { | ||||
| 	ret := pf.TextPrintFlags.AllowedFormats() | ||||
| 	return append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...) | ||||
| } | ||||
|  | ||||
| // AddFlags receives a *cobra.Command reference and binds | ||||
| // flags related to Kubeadm printing to it | ||||
| func (pf *upgradePlanPrintFlags) AddFlags(cmd *cobra.Command) { | ||||
| 	pf.TextPrintFlags.AddFlags(cmd) | ||||
| 	pf.JSONYamlPrintFlags.AddFlags(cmd) | ||||
| 	// 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 | ||||
| // handling format printing. | ||||
| // Returns error if the specified outputFormat does not match supported formats. | ||||
| func (pf *upgradePlanPrintFlags) ToPrinter() (output.Printer, error) { | ||||
| 	switch pf.OutputFormat { | ||||
| 	case output.TextOutput: | ||||
| 		return pf.TextPrintFlags.ToPrinter(pf.OutputFormat) | ||||
| 	case output.JSONOutput: | ||||
| 		return newUpgradePlanJSONYAMLPrinter(pf.TypeSetterPrinter.WrapToPrinter(pf.JSONYamlPrintFlags.ToPrinter(output.JSONOutput))) | ||||
| 	case output.YAMLOutput: | ||||
| 		return newUpgradePlanJSONYAMLPrinter(pf.TypeSetterPrinter.WrapToPrinter(pf.JSONYamlPrintFlags.ToPrinter(output.YAMLOutput))) | ||||
| 	default: | ||||
| 		return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &pf.OutputFormat, AllowedFormats: pf.AllowedFormats()} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type upgradePlanJSONYamlPrintFlags struct { | ||||
| 	genericclioptions.JSONYamlPrintFlags | ||||
| } | ||||
|  | ||||
| // AllowedFormats returns a list of allowed output formats | ||||
| func (pf *upgradePlanJSONYamlPrintFlags) AllowedFormats() []string { | ||||
| 	return []string{output.JSONOutput, output.YAMLOutput} | ||||
| } | ||||
|  | ||||
| // upgradePlanJSONYAMLPrinter prints upgrade plan in a JSON or YAML format | ||||
| type upgradePlanJSONYAMLPrinter struct { | ||||
| 	output.ResourcePrinterWrapper | ||||
| 	Components     []outputapiv1alpha2.ComponentUpgradePlan | ||||
| 	ConfigVersions []outputapiv1alpha2.ComponentConfigVersionState | ||||
| } | ||||
|  | ||||
| // newUpgradePlanJSONYAMLPrinter creates a new upgradePlanJSONYAMLPrinter object | ||||
| func newUpgradePlanJSONYAMLPrinter(resourcePrinter printers.ResourcePrinter, err error) (output.Printer, error) { | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &upgradePlanJSONYAMLPrinter{ResourcePrinterWrapper: output.ResourcePrinterWrapper{Printer: resourcePrinter}}, nil | ||||
| } | ||||
|  | ||||
| // PrintObj is an implementation of ResourcePrinter.PrintObj that adds object to the buffer | ||||
| func (p *upgradePlanJSONYAMLPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { | ||||
| 	item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan) | ||||
| 	if !ok { | ||||
| 		return errors.Errorf("expected ComponentUpgradePlan, but got %+v", obj) | ||||
| 	} | ||||
| 	p.Components = append(p.Components, *item) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 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.Components) == 0 && len(p.ConfigVersions) == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	plan := &outputapiv1alpha2.UpgradePlan{Components: p.Components, ConfigVersions: p.ConfigVersions} | ||||
| 	if err := p.Printer.PrintObj(plan, writer); err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, "could not flush output buffer: %v\n", err) | ||||
| 	} | ||||
| 	p.Components = p.Components[:0] | ||||
| } | ||||
|  | ||||
| // Close does nothing. | ||||
| func (p *upgradePlanJSONYAMLPrinter) Close(writer io.Writer) {} | ||||
|  | ||||
| // upgradePlanTextPrinter prints upgrade plan in a text form | ||||
| type upgradePlanTextPrinter struct { | ||||
| 	output.TextPrinter | ||||
| 	columns   []string | ||||
| 	tabwriter *tabwriter.Writer | ||||
| } | ||||
|  | ||||
| // Flush writes any buffered data | ||||
| func (p *upgradePlanTextPrinter) Flush(writer io.Writer, last bool) { | ||||
| 	if p.tabwriter != nil { | ||||
| 		p.tabwriter.Flush() | ||||
| 		p.tabwriter = nil | ||||
| 		p.Fprintln(writer, "") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // PrintObj is an implementation of ResourcePrinter.PrintObj for upgrade plan plain text output | ||||
| func (p *upgradePlanTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { | ||||
| 	if p.tabwriter == nil { | ||||
| 		p.tabwriter = tabwriter.NewWriter(writer, 10, 4, 3, ' ', 0) | ||||
| 		// Print header | ||||
| 		fmt.Fprintln(p.tabwriter, strings.Join(p.columns, "\t")) | ||||
| 	} | ||||
|  | ||||
| 	item, ok := obj.(*outputapiv1alpha2.ComponentUpgradePlan) | ||||
| 	if !ok { | ||||
| 		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 | ||||
| type upgradePlanTextPrintFlags struct{} | ||||
|  | ||||
| func (pf *upgradePlanTextPrintFlags) AddFlags(cmd *cobra.Command) {} | ||||
|  | ||||
| // AllowedFormats returns a list of allowed output formats | ||||
| func (pf *upgradePlanTextPrintFlags) AllowedFormats() []string { | ||||
| 	return []string{output.TextOutput} | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| 	} | ||||
| 	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 | ||||
| func runPlan(flags *planFlags, args []string, printer output.Printer) error { | ||||
| 	// Start with the basics, verify that the cluster is healthy, build a client and a versionGetter. Never dry-run when planning. | ||||
| @@ -284,34 +124,24 @@ func runPlan(flags *planFlags, args []string, printer output.Printer) error { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// A workaround to set the configVersionStates in the printer | ||||
| 	if p, ok := printer.(*upgradePlanJSONYAMLPrinter); ok { | ||||
| 		p.ConfigVersions = configVersionStates | ||||
| 	} | ||||
| 	// Generate and print the upgrade plan | ||||
| 	plan := genUpgradePlan(availUpgrades, configVersionStates, isExternalEtcd) | ||||
| 	return printer.PrintObj(plan, os.Stdout) | ||||
| } | ||||
|  | ||||
| 	// Generate and print upgrade plans | ||||
| // genUpgradePlan generates upgrade plan from available upgrades and component config version states | ||||
| func genUpgradePlan(availUpgrades []upgrade.Upgrade, configVersions []outputapiv1alpha3.ComponentConfigVersionState, isExternalEtcd bool) *outputapiv1alpha3.UpgradePlan { | ||||
| 	plan := &outputapiv1alpha3.UpgradePlan{ConfigVersions: configVersions} | ||||
| 	for _, up := range availUpgrades { | ||||
| 		plan, unstableVersionFlag, err := genUpgradePlan(&up, isExternalEtcd) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Actually, this is needed for machine-readable output only. | ||||
| 		// printUpgradePlan won't output the configVersionStates as it will simply print the same table several times | ||||
| 		// in the human-readable output if it did so | ||||
| 		plan.ConfigVersions = configVersionStates | ||||
|  | ||||
| 		printUpgradePlan(&up, plan, unstableVersionFlag, isExternalEtcd, os.Stdout, printer) | ||||
| 		au := genAvailableUpgrade(&up, isExternalEtcd) | ||||
| 		plan.AvailableUpgrades = append(plan.AvailableUpgrades, au) | ||||
| 	} | ||||
|  | ||||
| 	// Finally, print the component config state table | ||||
| 	printComponentConfigVersionStates(configVersionStates, os.Stdout, printer) | ||||
| 	return nil | ||||
| 	return plan | ||||
| } | ||||
|  | ||||
| // 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 []outputapiv1alpha3.ComponentUpgradePlan, up *upgrade.Upgrade, name string) []outputapiv1alpha3.ComponentUpgradePlan { | ||||
| 	beforeVersion := up.Before.DNSVersion | ||||
| 	afterVersion := up.After.DNSVersion | ||||
|  | ||||
| @@ -321,23 +151,20 @@ func appendDNSComponent(components []outputapiv1alpha2.ComponentUpgradePlan, up | ||||
| 	return components | ||||
| } | ||||
|  | ||||
| // genUpgradePlan generates output-friendly upgrade plan out of upgrade.Upgrade structure | ||||
| func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha2.UpgradePlan, string, error) { | ||||
| 	newK8sVersion, err := version.ParseSemantic(up.After.KubeVersion) | ||||
| 	if err != nil { | ||||
| 		return nil, "", errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", up.After.KubeVersion) | ||||
| 	} | ||||
| // appendKubeadmComponent appends kubeadm component to the list of components | ||||
| func appendKubeadmComponent(components []outputapiv1alpha3.ComponentUpgradePlan, up *upgrade.Upgrade, name string) []outputapiv1alpha3.ComponentUpgradePlan { | ||||
| 	beforeVersion := up.Before.KubeadmVersion | ||||
| 	afterVersion := up.After.KubeadmVersion | ||||
|  | ||||
| 	unstableVersionFlag := "" | ||||
| 	if len(newK8sVersion.PreRelease()) != 0 { | ||||
| 		if strings.HasPrefix(newK8sVersion.PreRelease(), "rc") { | ||||
| 			unstableVersionFlag = " --allow-release-candidate-upgrades" | ||||
| 		} else { | ||||
| 			unstableVersionFlag = " --allow-experimental-upgrades" | ||||
| 		} | ||||
| 	if beforeVersion != "" || afterVersion != "" { | ||||
| 		components = append(components, newComponentUpgradePlan(name, beforeVersion, afterVersion)) | ||||
| 	} | ||||
| 	return components | ||||
| } | ||||
|  | ||||
| 	components := []outputapiv1alpha2.ComponentUpgradePlan{} | ||||
| // genAvailableUpgrade generates available upgrade from upgrade object and external etcd boolean | ||||
| func genAvailableUpgrade(up *upgrade.Upgrade, isExternalEtcd bool) outputapiv1alpha3.AvailableUpgrade { | ||||
| 	components := []outputapiv1alpha3.ComponentUpgradePlan{} | ||||
|  | ||||
| 	if up.CanUpgradeKubelets() { | ||||
| 		// The map is of the form <old-version>:<node-count>. Here all the keys are put into a slice and sorted | ||||
| @@ -354,59 +181,13 @@ func genUpgradePlan(up *upgrade.Upgrade, isExternalEtcd bool) (*outputapiv1alpha | ||||
| 	components = append(components, newComponentUpgradePlan(constants.KubeProxy, up.Before.KubeVersion, up.After.KubeVersion)) | ||||
|  | ||||
| 	components = appendDNSComponent(components, up, constants.CoreDNS) | ||||
| 	components = appendKubeadmComponent(components, up, constants.Kubeadm) | ||||
|  | ||||
| 	if !isExternalEtcd { | ||||
| 		components = append(components, newComponentUpgradePlan(constants.Etcd, up.Before.EtcdVersion, up.After.EtcdVersion)) | ||||
| 	} | ||||
|  | ||||
| 	return &outputapiv1alpha2.UpgradePlan{Components: components}, unstableVersionFlag, nil | ||||
| } | ||||
|  | ||||
| // printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to | ||||
| func printUpgradePlan(up *upgrade.Upgrade, plan *outputapiv1alpha2.UpgradePlan, unstableVersionFlag string, isExternalEtcd bool, writer io.Writer, printer output.Printer) { | ||||
| 	printHeader := true | ||||
| 	printManualUpgradeHeader := true | ||||
| 	for _, component := range plan.Components { | ||||
| 		if isExternalEtcd && component.Name == constants.Etcd { | ||||
| 			// Don't print etcd if it's external | ||||
| 			continue | ||||
| 		} 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':") | ||||
| 				plan := newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion) | ||||
| 				printer.PrintObj(&plan, writer) | ||||
| 				printManualUpgradeHeader = false | ||||
| 			} else { | ||||
| 				plan := newComponentUpgradePlan("", component.CurrentVersion, component.NewVersion) | ||||
| 				printer.PrintObj(&plan, writer) | ||||
| 			} | ||||
| 		} else { | ||||
| 			if printHeader { | ||||
| 				// End of manual upgrades table | ||||
| 				printer.Flush(writer, false) | ||||
| 				printer.Fprintf(writer, "Upgrade to the latest %s:\n", up.Description) | ||||
| 				printer.Fprintln(writer, "") | ||||
| 				printHeader = false | ||||
| 			} | ||||
| 			plan := newComponentUpgradePlan(component.Name, component.CurrentVersion, component.NewVersion) | ||||
| 			printer.PrintObj(&plan, writer) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	printer.Flush(writer, true) | ||||
|  | ||||
| 	printer.Fprintln(writer, "You can now apply the upgrade by executing the following command:") | ||||
| 	printer.Fprintln(writer, "") | ||||
| 	printer.Fprintf(writer, "\tkubeadm upgrade apply %s%s\n", up.After.KubeVersion, unstableVersionFlag) | ||||
| 	printer.Fprintln(writer, "") | ||||
|  | ||||
| 	if up.Before.KubeadmVersion != 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(writer, "") | ||||
| 	} | ||||
|  | ||||
| 	printLineSeparator(writer, printer) | ||||
| 	printer.Close(writer) | ||||
| 	return outputapiv1alpha3.AvailableUpgrade{Description: up.Description, Components: components} | ||||
| } | ||||
|  | ||||
| // sortedSliceFromStringIntMap returns a slice of the keys in the map sorted alphabetically | ||||
| @@ -419,6 +200,143 @@ func sortedSliceFromStringIntMap(strMap map[string]uint16) []string { | ||||
| 	return strSlice | ||||
| } | ||||
|  | ||||
| // upgradePlanTextPrintFlags provides flags necessary for printing upgrade plan in a text form | ||||
| type upgradePlanTextPrintFlags struct{} | ||||
|  | ||||
| // ToPrinter returns a kubeadm printer for the text output format | ||||
| func (tpf *upgradePlanTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) { | ||||
| 	if outputFormat == output.TextOutput { | ||||
| 		return &upgradePlanTextPrinter{}, nil | ||||
| 	} | ||||
| 	return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: []string{output.TextOutput}} | ||||
| } | ||||
|  | ||||
| // upgradePlanTextPrinter prints upgrade plan in a text form | ||||
| type upgradePlanTextPrinter struct { | ||||
| 	output.TextPrinter | ||||
| } | ||||
|  | ||||
| // PrintObj is an implementation of ResourcePrinter.PrintObj for upgrade plan plain text output | ||||
| func (printer *upgradePlanTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { | ||||
| 	plan, ok := obj.(*outputapiv1alpha3.UpgradePlan) | ||||
| 	if !ok { | ||||
| 		return errors.Errorf("expected UpgradePlan, but got %+v", obj) | ||||
| 	} | ||||
|  | ||||
| 	for _, au := range plan.AvailableUpgrades { | ||||
| 		if err := printer.printAvailableUpgrade(writer, &au); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	printer.printComponentConfigVersionStates(writer, plan.ConfigVersions) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // printUpgradePlan prints a UX-friendly overview of what versions are available to upgrade to | ||||
| func (printer *upgradePlanTextPrinter) printAvailableUpgrade(writer io.Writer, au *outputapiv1alpha3.AvailableUpgrade) error { | ||||
| 	var kubeVersion string | ||||
| 	var beforeKubeadmVersion, afterKubeadmVersion string | ||||
| 	for _, component := range au.Components { | ||||
| 		if component.Name == constants.KubeAPIServer { | ||||
| 			kubeVersion = component.NewVersion | ||||
| 		} | ||||
| 		if component.Name == constants.Kubeadm { | ||||
| 			beforeKubeadmVersion = component.CurrentVersion | ||||
| 			afterKubeadmVersion = component.NewVersion | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	newK8sVersion, err := version.ParseSemantic(kubeVersion) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrapf(err, "Unable to parse normalized version %q as a semantic version", kubeVersion) | ||||
| 	} | ||||
|  | ||||
| 	unstableVersionFlag := "" | ||||
| 	if len(newK8sVersion.PreRelease()) != 0 { | ||||
| 		if strings.HasPrefix(newK8sVersion.PreRelease(), "rc") { | ||||
| 			unstableVersionFlag = " --allow-release-candidate-upgrades" | ||||
| 		} else { | ||||
| 			unstableVersionFlag = " --allow-experimental-upgrades" | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	_, _ = printer.Fprintln(writer, "Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':") | ||||
| 	tabw := tabwriter.NewWriter(writer, 10, 4, 3, ' ', 0) | ||||
| 	_, _ = printer.Fprintln(tabw, strings.Join([]string{"COMPONENT", "CURRENT", "TARGET"}, "\t")) | ||||
| 	for i, component := range au.Components { | ||||
| 		if component.Name != constants.Kubelet { | ||||
| 			continue | ||||
| 		} | ||||
| 		if i == 0 { | ||||
| 			_, _ = printer.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion) | ||||
| 		} else { | ||||
| 			_, _ = printer.Fprintf(tabw, "%s\t%s\t%s\n", "", component.CurrentVersion, component.NewVersion) | ||||
| 		} | ||||
| 	} | ||||
| 	_ = tabw.Flush() | ||||
|  | ||||
| 	_, _ = printer.Fprintln(writer, "") | ||||
| 	_, _ = printer.Fprintf(writer, "Upgrade to the latest %s:\n", au.Description) | ||||
| 	_, _ = printer.Fprintln(writer, "") | ||||
| 	_, _ = printer.Fprintln(tabw, strings.Join([]string{"COMPONENT", "CURRENT", "TARGET"}, "\t")) | ||||
| 	for _, component := range au.Components { | ||||
| 		if component.Name == constants.Kubelet || component.Name == constants.Kubeadm { | ||||
| 			continue | ||||
| 		} | ||||
| 		_, _ = printer.Fprintf(tabw, "%s\t%s\t%s\n", component.Name, component.CurrentVersion, component.NewVersion) | ||||
| 	} | ||||
| 	_ = tabw.Flush() | ||||
|  | ||||
| 	_, _ = printer.Fprintln(writer, "") | ||||
| 	_, _ = printer.Fprintln(writer, "You can now apply the upgrade by executing the following command:") | ||||
| 	_, _ = printer.Fprintln(writer, "") | ||||
| 	_, _ = printer.Fprintf(writer, "\tkubeadm upgrade apply %s%s\n", kubeVersion, unstableVersionFlag) | ||||
| 	_, _ = printer.Fprintln(writer, "") | ||||
|  | ||||
| 	if beforeKubeadmVersion != afterKubeadmVersion { | ||||
| 		_, _ = printer.Fprintf(writer, "Note: Before you can perform this upgrade, you have to update kubeadm to %s.\n", afterKubeadmVersion) | ||||
| 		_, _ = printer.Fprintln(writer, "") | ||||
| 	} | ||||
|  | ||||
| 	printer.printLineSeparator(writer) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // printComponentConfigVersionStates prints a UX-friendly overview of the current state of component configs | ||||
| func (printer *upgradePlanTextPrinter) printComponentConfigVersionStates(w io.Writer, versionStates []outputapiv1alpha3.ComponentConfigVersionState) { | ||||
| 	if len(versionStates) == 0 { | ||||
| 		_, _ = printer.Fprintln(w, "No information available on component configs.") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	_, _ = printer.Fprintln(w, dedent.Dedent(` | ||||
| 		The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| 		Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| 		resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| 		upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
| 	`)) | ||||
|  | ||||
| 	tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0) | ||||
| 	_, _ = printer.Fprintln(tabw, strings.Join([]string{"API GROUP", "CURRENT VERSION", "PREFERRED VERSION", "MANUAL UPGRADE REQUIRED"}, "\t")) | ||||
|  | ||||
| 	for _, state := range versionStates { | ||||
| 		_, _ = printer.Fprintf(tabw, | ||||
| 			"%s\t%s\t%s\t%s\n", | ||||
| 			state.Group, | ||||
| 			strOrDash(state.CurrentVersion), | ||||
| 			strOrDash(state.PreferredVersion), | ||||
| 			yesOrNo(state.ManualUpgradeRequired), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	_ = tabw.Flush() | ||||
| 	printer.printLineSeparator(w) | ||||
| } | ||||
|  | ||||
| func (printer *upgradePlanTextPrinter) printLineSeparator(w io.Writer) { | ||||
| 	_, _ = printer.Fprintf(w, "_____________________________________________________________________\n\n") | ||||
| } | ||||
|  | ||||
| func strOrDash(s string) string { | ||||
| 	if s != "" { | ||||
| 		return s | ||||
| @@ -432,37 +350,3 @@ func yesOrNo(b bool) string { | ||||
| 	} | ||||
| 	return "no" | ||||
| } | ||||
|  | ||||
| func printLineSeparator(w io.Writer, printer output.Printer) { | ||||
| 	printer.Fprintf(w, "_____________________________________________________________________\n\n") | ||||
| } | ||||
|  | ||||
| func printComponentConfigVersionStates(versionStates []outputapiv1alpha2.ComponentConfigVersionState, w io.Writer, printer output.Printer) { | ||||
| 	if len(versionStates) == 0 { | ||||
| 		printer.Fprintln(w, "No information available on component configs.") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	printer.Fprintln(w, dedent.Dedent(` | ||||
| 		The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| 		Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| 		resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| 		upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
| 	`)) | ||||
|  | ||||
| 	tabw := tabwriter.NewWriter(w, 10, 4, 3, ' ', 0) | ||||
| 	printer.Fprintln(tabw, "API GROUP\tCURRENT VERSION\tPREFERRED VERSION\tMANUAL UPGRADE REQUIRED") | ||||
|  | ||||
| 	for _, state := range versionStates { | ||||
| 		printer.Fprintf(tabw, | ||||
| 			"%s\t%s\t%s\t%s\n", | ||||
| 			state.Group, | ||||
| 			strOrDash(state.CurrentVersion), | ||||
| 			strOrDash(state.PreferredVersion), | ||||
| 			yesOrNo(state.ManualUpgradeRequired), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	tabw.Flush() | ||||
| 	printLineSeparator(w, printer) | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,9 @@ import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/diff" | ||||
| 	outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme" | ||||
| 	outputapiv1alpha3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha3" | ||||
| 	"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" | ||||
| 	"k8s.io/kubernetes/cmd/kubeadm/app/util/output" | ||||
| ) | ||||
| @@ -67,10 +70,26 @@ func TestSortedSliceFromStringIntMap(t *testing.T) { | ||||
| } | ||||
|  | ||||
| // TODO Think about modifying this test to be less verbose checking b/c it can be brittle. | ||||
| func TestPrintAvailableUpgrades(t *testing.T) { | ||||
| func TestPrintUpgradePlan(t *testing.T) { | ||||
| 	versionStates := []outputapiv1alpha3.ComponentConfigVersionState{ | ||||
| 		{ | ||||
| 			Group:                 "kubeproxy.config.k8s.io", | ||||
| 			CurrentVersion:        "v1alpha1", | ||||
| 			PreferredVersion:      "v1alpha1", | ||||
| 			ManualUpgradeRequired: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Group:                 "kubelet.config.k8s.io", | ||||
| 			CurrentVersion:        "v1beta1", | ||||
| 			PreferredVersion:      "v1beta1", | ||||
| 			ManualUpgradeRequired: false, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	var tests = []struct { | ||||
| 		name          string | ||||
| 		upgrades      []upgrade.Upgrade | ||||
| 		versionStates []outputapiv1alpha3.ComponentConfigVersionState | ||||
| 		buf           *bytes.Buffer | ||||
| 		expectedBytes []byte | ||||
| 		externalEtcd  bool | ||||
| @@ -97,6 +116,7 @@ func TestPrintAvailableUpgrades(t *testing.T) { | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			versionStates: versionStates, | ||||
| 			expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': | ||||
| COMPONENT   CURRENT       TARGET | ||||
| kubelet     1 x v1.18.1   v1.18.4 | ||||
| @@ -119,6 +139,17 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.18.4 | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `), | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -143,6 +174,7 @@ _____________________________________________________________________ | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			versionStates: versionStates, | ||||
| 			expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': | ||||
| COMPONENT   CURRENT       TARGET | ||||
| kubelet     1 x v1.18.4   v1.19.0 | ||||
| @@ -165,6 +197,17 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.19.0 | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `), | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -207,6 +250,7 @@ _____________________________________________________________________ | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			versionStates: versionStates, | ||||
| 			expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': | ||||
| COMPONENT   CURRENT       TARGET | ||||
| kubelet     1 x v1.18.3   v1.18.5 | ||||
| @@ -249,6 +293,17 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.19.0 | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `), | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -273,6 +328,7 @@ _____________________________________________________________________ | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			versionStates: versionStates, | ||||
| 			expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': | ||||
| COMPONENT   CURRENT       TARGET | ||||
| kubelet     1 x v1.18.5   v1.19.0-beta.1 | ||||
| @@ -295,6 +351,17 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.19.0 | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `), | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -319,6 +386,7 @@ _____________________________________________________________________ | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			versionStates: versionStates, | ||||
| 			expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': | ||||
| COMPONENT   CURRENT       TARGET | ||||
| kubelet     1 x v1.18.5   v1.19.0-rc.1 | ||||
| @@ -341,6 +409,17 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.19.0 | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `), | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -366,6 +445,7 @@ _____________________________________________________________________ | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			versionStates: versionStates, | ||||
| 			expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': | ||||
| COMPONENT   CURRENT       TARGET | ||||
| kubelet     1 x v1.19.2   v1.19.3 | ||||
| @@ -389,6 +469,17 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.19.3 | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `), | ||||
| 		}, | ||||
|  | ||||
| @@ -414,7 +505,8 @@ _____________________________________________________________________ | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			externalEtcd: true, | ||||
| 			versionStates: versionStates, | ||||
| 			externalEtcd:  true, | ||||
| 			expectedBytes: []byte(`Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply': | ||||
| COMPONENT   CURRENT       TARGET | ||||
| kubelet     1 x v1.19.2   v1.19.3 | ||||
| @@ -436,30 +528,38 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.19.3 | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `), | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, rt := range tests { | ||||
| 		t.Run(rt.name, func(t *testing.T) { | ||||
| 			rt.buf = bytes.NewBufferString("") | ||||
| 			outputFlags := newUpgradePlanPrintFlags(output.TextOutput) | ||||
| 			outputFlags := output.NewOutputFlags(&upgradePlanTextPrintFlags{}).WithTypeSetter(outputapischeme.Scheme).WithDefaultOutput(output.TextOutput) | ||||
| 			printer, err := outputFlags.ToPrinter() | ||||
| 			if err != nil { | ||||
| 				t.Errorf("failed ToPrinter, err: %+v", err) | ||||
| 			} | ||||
|  | ||||
| 			// Generate and print upgrade plans | ||||
| 			for _, up := range rt.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) | ||||
| 			plan := genUpgradePlan(rt.upgrades, rt.versionStates, rt.externalEtcd) | ||||
| 			if err := printer.PrintObj(plan, rt.buf); err != nil { | ||||
| 				t.Errorf("unexpected error when print object: %v", err) | ||||
| 			} | ||||
|  | ||||
| 			actualBytes := rt.buf.Bytes() | ||||
| 			if !bytes.Equal(actualBytes, rt.expectedBytes) { | ||||
| 				t.Errorf( | ||||
| 					"failed PrintAvailableUpgrades:\n\texpected: %q\n\n\tactual  : %q", | ||||
| 					"failed PrintUpgradePlan:\n\texpected: %q\n\n\tactual  : %q", | ||||
| 					string(rt.expectedBytes), | ||||
| 					string(actualBytes), | ||||
| 				) | ||||
| @@ -468,7 +568,7 @@ _____________________________________________________________________ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPrintAvailableUpgradesStructured(t *testing.T) { | ||||
| func TestPrintUpgradePlanStructured(t *testing.T) { | ||||
| 	upgrades := []upgrade.Upgrade{ | ||||
| 		{ | ||||
| 			Description: "version in the v1.8 series", | ||||
| @@ -490,6 +590,21 @@ func TestPrintAvailableUpgradesStructured(t *testing.T) { | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	versionStates := []outputapiv1alpha3.ComponentConfigVersionState{ | ||||
| 		{ | ||||
| 			Group:                 "kubeproxy.config.k8s.io", | ||||
| 			CurrentVersion:        "v1alpha1", | ||||
| 			PreferredVersion:      "v1alpha1", | ||||
| 			ManualUpgradeRequired: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Group:                 "kubelet.config.k8s.io", | ||||
| 			CurrentVersion:        "v1beta1", | ||||
| 			PreferredVersion:      "v1beta1", | ||||
| 			ManualUpgradeRequired: false, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	var tests = []struct { | ||||
| 		name         string | ||||
| 		outputFormat string | ||||
| @@ -502,81 +617,117 @@ func TestPrintAvailableUpgradesStructured(t *testing.T) { | ||||
| 			outputFormat: "json", | ||||
| 			expected: `{ | ||||
|     "kind": "UpgradePlan", | ||||
|     "apiVersion": "output.kubeadm.k8s.io/v1alpha2", | ||||
|     "components": [ | ||||
|     "apiVersion": "output.kubeadm.k8s.io/v1alpha3", | ||||
|     "availableUpgrades": [ | ||||
|         { | ||||
|             "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" | ||||
|             "description": "version in the v1.8 series", | ||||
|             "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": "kubeadm", | ||||
|                     "currentVersion": "v1.8.2", | ||||
|                     "newVersion": "v1.8.3" | ||||
|                 }, | ||||
|                 { | ||||
|                     "name": "etcd", | ||||
|                     "currentVersion": "3.0.17", | ||||
|                     "newVersion": "3.0.17" | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ], | ||||
|     "configVersions": null | ||||
|     "configVersions": [ | ||||
|         { | ||||
|             "group": "kubeproxy.config.k8s.io", | ||||
|             "currentVersion": "v1alpha1", | ||||
|             "preferredVersion": "v1alpha1", | ||||
|             "manualUpgradeRequired": false | ||||
|         }, | ||||
|         { | ||||
|             "group": "kubelet.config.k8s.io", | ||||
|             "currentVersion": "v1beta1", | ||||
|             "preferredVersion": "v1beta1", | ||||
|             "manualUpgradeRequired": false | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| `, | ||||
| 		}, | ||||
| 		{ | ||||
| 			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 | ||||
| 			expected: `apiVersion: output.kubeadm.k8s.io/v1alpha3 | ||||
| availableUpgrades: | ||||
| - 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: v1.8.2 | ||||
|     name: kubeadm | ||||
|     newVersion: v1.8.3 | ||||
|   - currentVersion: 3.0.17 | ||||
|     name: etcd | ||||
|     newVersion: 3.0.17 | ||||
|   description: version in the v1.8 series | ||||
| configVersions: | ||||
| - currentVersion: v1alpha1 | ||||
|   group: kubeproxy.config.k8s.io | ||||
|   manualUpgradeRequired: false | ||||
|   preferredVersion: v1alpha1 | ||||
| - currentVersion: v1beta1 | ||||
|   group: kubelet.config.k8s.io | ||||
|   manualUpgradeRequired: false | ||||
|   preferredVersion: v1beta1 | ||||
| kind: UpgradePlan | ||||
| `, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:         "Text output", | ||||
| 			outputFormat: "Text", | ||||
| 			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 | ||||
| @@ -599,6 +750,17 @@ Note: Before you can perform this upgrade, you have to update kubeadm to v1.8.3. | ||||
|  | ||||
| _____________________________________________________________________ | ||||
|  | ||||
|  | ||||
| The table below shows the current state of component configs as understood by this version of kubeadm. | ||||
| Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or | ||||
| resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually | ||||
| upgrade to is denoted in the "PREFERRED VERSION" column. | ||||
|  | ||||
| API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED | ||||
| kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no | ||||
| kubelet.config.k8s.io     v1beta1           v1beta1             no | ||||
| _____________________________________________________________________ | ||||
|  | ||||
| `, | ||||
| 		}, | ||||
| 	} | ||||
| @@ -606,24 +768,21 @@ _____________________________________________________________________ | ||||
| 	for _, rt := range tests { | ||||
| 		t.Run(rt.name, func(t *testing.T) { | ||||
| 			rt.buf = bytes.NewBufferString("") | ||||
| 			outputFlags := newUpgradePlanPrintFlags(rt.outputFormat) | ||||
| 			outputFlags := output.NewOutputFlags(&upgradePlanTextPrintFlags{}).WithTypeSetter(outputapischeme.Scheme).WithDefaultOutput(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) | ||||
| 			plan := genUpgradePlan(upgrades, versionStates, false) | ||||
| 			if err := printer.PrintObj(plan, rt.buf); err != nil { | ||||
| 				t.Errorf("unexpected error when print object: %v", err) | ||||
| 			} | ||||
|  | ||||
| 			actual := rt.buf.String() | ||||
| 			if actual != rt.expected { | ||||
| 				t.Errorf("failed PrintAvailableUpgrades:\n\nexpected:\n%s\n\nactual:\n%s", rt.expected, actual) | ||||
|  | ||||
| 				t.Errorf("failed PrintUpgradePlan:\n\nexpected:\n%s\n\nactual:\n%s\n\ndiff:\n%s", rt.expected, actual, diff.StringDiff(actual, rt.expected)) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
|   | ||||
| @@ -28,7 +28,7 @@ import ( | ||||
| 	"k8s.io/klog/v2" | ||||
|  | ||||
| 	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" | ||||
| 	outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2" | ||||
| 	outputapiv1alpha3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha3" | ||||
| 	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" | ||||
| @@ -289,7 +289,7 @@ func FetchFromClusterWithLocalOverwrites(clusterCfg *kubeadmapi.ClusterConfigura | ||||
|  | ||||
| // GetVersionStates returns a slice of ComponentConfigVersionState structs | ||||
| // describing all supported component config groups that were identified on the cluster | ||||
| func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) ([]outputapiv1alpha2.ComponentConfigVersionState, error) { | ||||
| func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client clientset.Interface) ([]outputapiv1alpha3.ComponentConfigVersionState, error) { | ||||
| 	// 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. | ||||
| 	scratchClusterCfg := clusterCfg.DeepCopy() | ||||
| @@ -301,12 +301,12 @@ func GetVersionStates(clusterCfg *kubeadmapi.ClusterConfiguration, client client | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	results := []outputapiv1alpha2.ComponentConfigVersionState{} | ||||
| 	results := []outputapiv1alpha3.ComponentConfigVersionState{} | ||||
| 	for _, handler := range known { | ||||
| 		group := handler.GroupVersion.Group | ||||
| 		if _, ok := scratchClusterCfg.ComponentConfigs[group]; ok { | ||||
| 			// Normally loaded component config. No manual upgrade required on behalf of users. | ||||
| 			results = append(results, outputapiv1alpha2.ComponentConfigVersionState{ | ||||
| 			results = append(results, outputapiv1alpha3.ComponentConfigVersionState{ | ||||
| 				Group:            group, | ||||
| 				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 | ||||
| @@ -315,7 +315,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 | ||||
| 			// generated one and is therefore skipped so we can automatically re-generate it (no action required on | ||||
| 			// behalf of the user). | ||||
| 			results = append(results, outputapiv1alpha2.ComponentConfigVersionState{ | ||||
| 			results = append(results, outputapiv1alpha3.ComponentConfigVersionState{ | ||||
| 				Group:            group, | ||||
| 				PreferredVersion: handler.GroupVersion.Version, | ||||
| 			}) | ||||
|   | ||||
| @@ -35,7 +35,7 @@ import ( | ||||
| 	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" | ||||
| 	kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" | ||||
| 	kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" | ||||
| 	outputapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha2" | ||||
| 	outputapiv1alpha3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha3" | ||||
| 	"k8s.io/kubernetes/cmd/kubeadm/app/constants" | ||||
| 	kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" | ||||
| ) | ||||
| @@ -612,7 +612,7 @@ func TestFetchFromClusterWithLocalOverwrites(t *testing.T) { | ||||
|  | ||||
| func TestGetVersionStates(t *testing.T) { | ||||
| 	fakeKnownContext(func() { | ||||
| 		versionStateCurrent := outputapiv1alpha2.ComponentConfigVersionState{ | ||||
| 		versionStateCurrent := outputapiv1alpha3.ComponentConfigVersionState{ | ||||
| 			Group:            kubeadmapiv1.GroupName, | ||||
| 			CurrentVersion:   currentClusterConfigVersion, | ||||
| 			PreferredVersion: currentClusterConfigVersion, | ||||
| @@ -622,7 +622,7 @@ func TestGetVersionStates(t *testing.T) { | ||||
| 			desc        string | ||||
| 			obj         runtime.Object | ||||
| 			expectedErr bool | ||||
| 			expected    outputapiv1alpha2.ComponentConfigVersionState | ||||
| 			expected    outputapiv1alpha3.ComponentConfigVersionState | ||||
| 		}{ | ||||
| 			{ | ||||
| 				desc:     "appropriate cluster object", | ||||
| @@ -642,7 +642,7 @@ func TestGetVersionStates(t *testing.T) { | ||||
| 			{ | ||||
| 				desc: "old signed config", | ||||
| 				obj:  testClusterConfigMap(oldFooClusterConfig, true), | ||||
| 				expected: outputapiv1alpha2.ComponentConfigVersionState{ | ||||
| 				expected: outputapiv1alpha3.ComponentConfigVersionState{ | ||||
| 					Group:            kubeadmapiv1.GroupName, | ||||
| 					CurrentVersion:   "", // The config is treated as if it's missing | ||||
| 					PreferredVersion: currentClusterConfigVersion, | ||||
|   | ||||
| @@ -340,6 +340,8 @@ const ( | ||||
| 	CoreDNS = "CoreDNS" | ||||
| 	// Kubelet defines variable used internally when referring to the Kubelet | ||||
| 	Kubelet = "kubelet" | ||||
| 	// Kubeadm defines variable used internally when referring to the kubeadm component | ||||
| 	Kubeadm = "kubeadm" | ||||
|  | ||||
| 	// KubeCertificatesVolumeName specifies the name for the Volume that is used for injecting certificates to control plane components (can be both a hostPath volume or a projected, all-in-one volume) | ||||
| 	KubeCertificatesVolumeName = "k8s-certs" | ||||
|   | ||||
| @@ -144,9 +144,6 @@ type Printer interface { | ||||
| 	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, last bool) | ||||
| 	Close(writer io.Writer) | ||||
| } | ||||
|  | ||||
| // TextPrinter implements Printer interface for generic text output | ||||
| @@ -179,14 +176,6 @@ 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 | ||||
| @@ -200,14 +189,6 @@ func NewResourcePrinterWrapper(resourcePrinter printers.ResourcePrinter, err err | ||||
| 	return &ResourcePrinterWrapper{Printer: resourcePrinter}, nil | ||||
| } | ||||
|  | ||||
| // Flush writes any buffered data | ||||
| 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) { | ||||
| } | ||||
|  | ||||
| // PrintObj is an implementation of ResourcePrinter.PrintObj that calls underlying printer API | ||||
| func (rpw *ResourcePrinterWrapper) PrintObj(obj runtime.Object, writer io.Writer) error { | ||||
| 	return rpw.Printer.PrintObj(obj, writer) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 carlory
					carlory