Merge pull request #123068 from calvin0327/upgrade-configuration

kubeadm: implementation of UpgradeConfiguration API types
This commit is contained in:
Kubernetes Prow Robot
2024-02-28 23:40:12 -08:00
committed by GitHub
35 changed files with 1668 additions and 274 deletions

View File

@@ -41,6 +41,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
fuzzJoinConfiguration,
fuzzJoinControlPlane,
fuzzResetConfiguration,
fuzzUpgradeConfiguration,
}
}
@@ -152,3 +153,13 @@ func fuzzResetConfiguration(obj *kubeadm.ResetConfiguration, c fuzz.Continue) {
obj.CertificatesDir = "/tmp"
kubeadm.SetDefaultTimeouts(&obj.Timeouts)
}
func fuzzUpgradeConfiguration(obj *kubeadm.UpgradeConfiguration, c fuzz.Continue) {
c.FuzzNoCustom(obj)
// Pinning values for fields that get defaults if fuzz value is empty string or nil (thus making the round trip test fail)
obj.Node.EtcdUpgrade = ptr.To(true)
obj.Apply.EtcdUpgrade = ptr.To(true)
obj.Apply.CertificateRenewal = ptr.To(false)
obj.Node.CertificateRenewal = ptr.To(false)
}

View File

@@ -40,6 +40,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ClusterConfiguration{},
&JoinConfiguration{},
&ResetConfiguration{},
&UpgradeConfiguration{},
)
return nil
}

View File

@@ -39,7 +39,7 @@ type InitConfiguration struct {
// the `json:"-"` tag in the external variant of these API types.
ClusterConfiguration `json:"-"`
// BootstrapTokens is respected at `kubeadm init` time and describes a set of Bootstrap Tokens to create.
// BootstrapTokens is respected at "kubeadm init" time and describes a set of Bootstrap Tokens to create.
BootstrapTokens []bootstraptokenv1.BootstrapToken
// DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done.
@@ -221,7 +221,7 @@ type APIEndpoint struct {
// NodeRegistrationOptions holds fields that relate to registering a new control-plane or node to the cluster, either via "kubeadm init" or "kubeadm join"
type NodeRegistrationOptions struct {
// Name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation.
// Name is the `.Metadata.Name` field of the Node API object that will be created in this "kubeadm init" or "kubeadm join" operation.
// This field is also used in the CommonName field of the kubelet's client certificate to the API server.
// Defaults to the hostname of the node if not provided.
Name string
@@ -541,6 +541,125 @@ type ResetConfiguration struct {
Timeouts *Timeouts
}
// UpgradeApplyConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade apply" command.
type UpgradeApplyConfiguration struct {
// KubernetesVersion is the target version of the control plane.
KubernetesVersion string
// AllowExperimentalUpgrades instructs kubeadm to show unstable versions of Kubernetes as an upgrade
// alternative and allows upgrading to an alpha/beta/release candidate version of Kubernetes.
// Default: false
AllowExperimentalUpgrades *bool
// Enable AllowRCUpgrades will show release candidate versions of Kubernetes as an upgrade alternative and
// allows upgrading to a release candidate version of Kubernetes.
AllowRCUpgrades *bool
// CertificateRenewal instructs kubeadm to execute certificate renewal during upgrades.
CertificateRenewal *bool
// DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done.
DryRun *bool
// EtcdUpgrade instructs kubeadm to execute etcd upgrade during upgrades.
EtcdUpgrade *bool
// ForceUpgrade flag instructs kubeadm to upgrade the cluster without prompting for confirmation.
ForceUpgrade *bool
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored during the upgrade process, e.g. 'IsPrivilegedUser,Swap'.
// Value 'all' ignores errors from all checks.
IgnorePreflightErrors []string
// Patches contains options related to applying patches to components deployed by kubeadm during "kubeadm upgrade".
Patches *Patches
// PrintConfig specifies whether the configuration file that will be used in the upgrade should be printed or not.
PrintConfig *bool
// SkipPhases is a list of phases to skip during command execution.
// NOTE: This field is currently ignored for "kubeadm upgrade apply", but in the future it will be supported.
SkipPhases []string
}
// UpgradeDiffConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade diff" command.
type UpgradeDiffConfiguration struct {
// KubernetesVersion is the target version of the control plane.
KubernetesVersion string
// DiffContextLines is the number of lines of context in the diff.
DiffContextLines int
}
// UpgradeNodeConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade node" command.
type UpgradeNodeConfiguration struct {
// CertificateRenewal instructs kubeadm to execute certificate renewal during upgrades.
CertificateRenewal *bool
// DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done.
DryRun *bool
// EtcdUpgrade instructs kubeadm to execute etcd upgrade during upgrades.
EtcdUpgrade *bool
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored during the upgrade process, e.g. 'IsPrivilegedUser,Swap'.
// Value 'all' ignores errors from all checks.
IgnorePreflightErrors []string
// SkipPhases is a list of phases to skip during command execution.
// The list of phases can be obtained with the "kubeadm upgrade node phase --help" command.
SkipPhases []string
// Patches contains options related to applying patches to components deployed by kubeadm during "kubeadm upgrade".
Patches *Patches
}
// UpgradePlanConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade plan" command.
type UpgradePlanConfiguration struct {
// KubernetesVersion is the target version of the control plane.
// +optional
KubernetesVersion string
// AllowExperimentalUpgrades instructs kubeadm to show unstable versions of Kubernetes as an upgrade
// alternative and allows upgrading to an alpha/beta/release candidate version of Kubernetes.
// Default: false
// +optional
AllowExperimentalUpgrades *bool
// Enable AllowRCUpgrades will show release candidate versions of Kubernetes as an upgrade alternative and
// allows upgrading to a release candidate version of Kubernetes.
AllowRCUpgrades *bool
// DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done.
DryRun *bool
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored during the upgrade process, e.g. 'IsPrivilegedUser,Swap'.
// Value 'all' ignores errors from all checks.
IgnorePreflightErrors []string
// PrintConfig specifies whether the configuration file that will be used in the upgrade should be printed or not.
PrintConfig *bool
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// UpgradeConfiguration contains a list of options that are specific to "kubeadm upgrade" subcommands.
type UpgradeConfiguration struct {
metav1.TypeMeta
// Apply holds a list of options that are specific to the "kubeadm upgrade apply" command.
Apply UpgradeApplyConfiguration
// Diff holds a list of options that are specific to the "kubeadm upgrade diff" command.
Diff UpgradeDiffConfiguration
// Node holds a list of options that are specific to the "kubeadm upgrade node" command.
Node UpgradeNodeConfiguration
// Plan holds a list of options that are specific to the "kubeadm upgrade plan" command.
Plan UpgradePlanConfiguration
}
const (
// UnmountFlagMNTForce represents the flag "MNT_FORCE"
UnmountFlagMNTForce = "MNT_FORCE"

View File

@@ -256,3 +256,22 @@ func SetDefaults_Timeouts(obj *Timeouts) {
}
}
}
// SetDefaults_UpgradeConfiguration assigns default values for the UpgradeConfiguration
func SetDefaults_UpgradeConfiguration(obj *UpgradeConfiguration) {
if obj.Node.EtcdUpgrade == nil {
obj.Node.EtcdUpgrade = ptr.To(true)
}
if obj.Node.CertificateRenewal == nil {
obj.Node.CertificateRenewal = ptr.To(true)
}
if obj.Apply.EtcdUpgrade == nil {
obj.Apply.EtcdUpgrade = ptr.To(true)
}
if obj.Apply.CertificateRenewal == nil {
obj.Apply.CertificateRenewal = ptr.To(true)
}
}

View File

@@ -41,6 +41,9 @@ limitations under the License.
// that can be used to configure various timeouts.
// - Add the `NodeRegistration.ImagePullSerial` field in 'InitConfiguration` and `JoinConfiguration`, which
// can be used to control if kubeadm pulls images serially or in parallel.
// - The UpgradeConfiguration kubeadm API is now supported in v1beta4 when passing --config to "kubeadm upgrade" subcommands.
// Usage of component configuration for kubelet and kube-proxy, InitConfiguration and ClusterConfiguration is deprecated
// and will be ignored when passing --config to upgrade subcommands.
//
// Migration from old kubeadm config versions
//

View File

@@ -49,6 +49,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ClusterConfiguration{},
&JoinConfiguration{},
&ResetConfiguration{},
&UpgradeConfiguration{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@@ -597,3 +597,151 @@ type Timeouts struct {
// +optional
Discovery *metav1.Duration `json:"discovery,omitempty"`
}
// UpgradeApplyConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade apply" command.
type UpgradeApplyConfiguration struct {
// KubernetesVersion is the target version of the control plane.
// +optional
KubernetesVersion string `json:"kubernetesVersion,omitempty"`
// AllowExperimentalUpgrades instructs kubeadm to show unstable versions of Kubernetes as an upgrade
// alternative and allows upgrading to an alpha/beta/release candidate version of Kubernetes.
// Default: false
// +optional
AllowExperimentalUpgrades *bool `json:"allowExperimentalUpgrades,omitempty"`
// Enable AllowRCUpgrades will show release candidate versions of Kubernetes as an upgrade alternative and
// allows upgrading to a release candidate version of Kubernetes.
// +optional
AllowRCUpgrades *bool `json:"allowRCUpgrades,omitempty"`
// CertificateRenewal instructs kubeadm to execute certificate renewal during upgrades.
// Defaults to true.
// +optional
CertificateRenewal *bool `json:"certificateRenewal,omitempty"`
// DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done.
// +optional
DryRun *bool `json:"dryRun,omitempty"`
// EtcdUpgrade instructs kubeadm to execute etcd upgrade during upgrades.
// Defaults to true.
// +optional
EtcdUpgrade *bool `json:"etcdUpgrade,omitempty"`
// ForceUpgrade flag instructs kubeadm to upgrade the cluster without prompting for confirmation.
// +optional
ForceUpgrade *bool `json:"forceUpgrade,omitempty"`
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored during the upgrade process, e.g. 'IsPrivilegedUser,Swap'.
// Value 'all' ignores errors from all checks.
// +optional
IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"`
// Patches contains options related to applying patches to components deployed by kubeadm during "kubeadm upgrade".
// +optional
Patches *Patches `json:"patches,omitempty"`
// PrintConfig specifies whether the configuration file that will be used in the upgrade should be printed or not.
// +optional
PrintConfig *bool `json:"printConfig,omitempty"`
// SkipPhases is a list of phases to skip during command execution.
// NOTE: This field is currently ignored for "kubeadm upgrade apply", but in the future it will be supported.
SkipPhases []string
}
// UpgradeDiffConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade diff" command.
type UpgradeDiffConfiguration struct {
// KubernetesVersion is the target version of the control plane.
// +optional
KubernetesVersion string `json:"kubernetesVersion,omitempty"`
// DiffContextLines is the number of lines of context in the diff.
// +optional
DiffContextLines int `json:"contextLines,omitempty"`
}
// UpgradeNodeConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade node" command.
type UpgradeNodeConfiguration struct {
// CertificateRenewal instructs kubeadm to execute certificate renewal during upgrades.
// Defaults to true.
// +optional
CertificateRenewal *bool `json:"certificateRenewal,omitempty"`
// DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done.
// +optional
DryRun *bool `json:"dryRun,omitempty"`
// EtcdUpgrade instructs kubeadm to execute etcd upgrade during upgrades.
// Defaults to true.
// +optional
EtcdUpgrade *bool `json:"etcdUpgrade,omitempty"`
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored during the upgrade process, e.g. 'IsPrivilegedUser,Swap'.
// Value 'all' ignores errors from all checks.
// +optional
IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"`
// SkipPhases is a list of phases to skip during command execution.
// The list of phases can be obtained with the "kubeadm upgrade node phase --help" command.
// +optional
SkipPhases []string `json:"skipPhases,omitempty"`
// Patches contains options related to applying patches to components deployed by kubeadm during "kubeadm upgrade".
// +optional
Patches *Patches `json:"patches,omitempty"`
}
// UpgradePlanConfiguration contains a list of configurable options which are specific to the "kubeadm upgrade plan" command.
type UpgradePlanConfiguration struct {
// KubernetesVersion is the target version of the control plane.
KubernetesVersion string `json:"kubernetesVersion,omitempty"`
// AllowExperimentalUpgrades instructs kubeadm to show unstable versions of Kubernetes as an upgrade
// alternative and allows upgrading to an alpha/beta/release candidate version of Kubernetes.
// Default: false
// +optional
AllowExperimentalUpgrades *bool `json:"allowExperimentalUpgrades,omitempty"`
// Enable AllowRCUpgrades will show release candidate versions of Kubernetes as an upgrade alternative and
// allows upgrading to a release candidate version of Kubernetes.
// +optional
AllowRCUpgrades *bool `json:"allowRCUpgrades,omitempty"`
// DryRun tells if the dry run mode is enabled, don't apply any change if it is and just output what would be done.
// +optional
DryRun *bool `json:"dryRun,omitempty"`
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored during the upgrade process, e.g. 'IsPrivilegedUser,Swap'.
// Value 'all' ignores errors from all checks.
// +optional
IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"`
// PrintConfig specifies whether the configuration file that will be used in the upgrade should be printed or not.
// +optional
PrintConfig *bool `json:"printConfig,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// UpgradeConfiguration contains a list of options that are specific to "kubeadm upgrade" subcommands.
type UpgradeConfiguration struct {
metav1.TypeMeta `json:",inline"`
// Apply holds a list of options that are specific to the "kubeadm upgrade apply" command.
// +optional
Apply UpgradeApplyConfiguration `json:"apply,omitempty"`
// Diff holds a list of options that are specific to the "kubeadm upgrade diff" command.
// +optional
Diff UpgradeDiffConfiguration `json:"diff,omitempty"`
// Node holds a list of options that are specific to the "kubeadm upgrade node" command.
// +optional
Node UpgradeNodeConfiguration `json:"node,omitempty"`
// Plan holds a list of options that are specific to the "kubeadm upgrade plan" command.
// +optional
Plan UpgradePlanConfiguration `json:"plan,omitempty"`
}

View File

@@ -244,6 +244,56 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*UpgradeApplyConfiguration)(nil), (*kubeadm.UpgradeApplyConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta4_UpgradeApplyConfiguration_To_kubeadm_UpgradeApplyConfiguration(a.(*UpgradeApplyConfiguration), b.(*kubeadm.UpgradeApplyConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*kubeadm.UpgradeApplyConfiguration)(nil), (*UpgradeApplyConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_kubeadm_UpgradeApplyConfiguration_To_v1beta4_UpgradeApplyConfiguration(a.(*kubeadm.UpgradeApplyConfiguration), b.(*UpgradeApplyConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*UpgradeConfiguration)(nil), (*kubeadm.UpgradeConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta4_UpgradeConfiguration_To_kubeadm_UpgradeConfiguration(a.(*UpgradeConfiguration), b.(*kubeadm.UpgradeConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*kubeadm.UpgradeConfiguration)(nil), (*UpgradeConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_kubeadm_UpgradeConfiguration_To_v1beta4_UpgradeConfiguration(a.(*kubeadm.UpgradeConfiguration), b.(*UpgradeConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*UpgradeDiffConfiguration)(nil), (*kubeadm.UpgradeDiffConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta4_UpgradeDiffConfiguration_To_kubeadm_UpgradeDiffConfiguration(a.(*UpgradeDiffConfiguration), b.(*kubeadm.UpgradeDiffConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*kubeadm.UpgradeDiffConfiguration)(nil), (*UpgradeDiffConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_kubeadm_UpgradeDiffConfiguration_To_v1beta4_UpgradeDiffConfiguration(a.(*kubeadm.UpgradeDiffConfiguration), b.(*UpgradeDiffConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*UpgradeNodeConfiguration)(nil), (*kubeadm.UpgradeNodeConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta4_UpgradeNodeConfiguration_To_kubeadm_UpgradeNodeConfiguration(a.(*UpgradeNodeConfiguration), b.(*kubeadm.UpgradeNodeConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*kubeadm.UpgradeNodeConfiguration)(nil), (*UpgradeNodeConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_kubeadm_UpgradeNodeConfiguration_To_v1beta4_UpgradeNodeConfiguration(a.(*kubeadm.UpgradeNodeConfiguration), b.(*UpgradeNodeConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*UpgradePlanConfiguration)(nil), (*kubeadm.UpgradePlanConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta4_UpgradePlanConfiguration_To_kubeadm_UpgradePlanConfiguration(a.(*UpgradePlanConfiguration), b.(*kubeadm.UpgradePlanConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*kubeadm.UpgradePlanConfiguration)(nil), (*UpgradePlanConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_kubeadm_UpgradePlanConfiguration_To_v1beta4_UpgradePlanConfiguration(a.(*kubeadm.UpgradePlanConfiguration), b.(*UpgradePlanConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*kubeadm.APIServer)(nil), (*APIServer)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_kubeadm_APIServer_To_v1beta4_APIServer(a.(*kubeadm.APIServer), b.(*APIServer), scope)
}); err != nil {
@@ -940,3 +990,167 @@ func autoConvert_kubeadm_Timeouts_To_v1beta4_Timeouts(in *kubeadm.Timeouts, out
func Convert_kubeadm_Timeouts_To_v1beta4_Timeouts(in *kubeadm.Timeouts, out *Timeouts, s conversion.Scope) error {
return autoConvert_kubeadm_Timeouts_To_v1beta4_Timeouts(in, out, s)
}
func autoConvert_v1beta4_UpgradeApplyConfiguration_To_kubeadm_UpgradeApplyConfiguration(in *UpgradeApplyConfiguration, out *kubeadm.UpgradeApplyConfiguration, s conversion.Scope) error {
out.KubernetesVersion = in.KubernetesVersion
out.AllowExperimentalUpgrades = (*bool)(unsafe.Pointer(in.AllowExperimentalUpgrades))
out.AllowRCUpgrades = (*bool)(unsafe.Pointer(in.AllowRCUpgrades))
out.CertificateRenewal = (*bool)(unsafe.Pointer(in.CertificateRenewal))
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
out.EtcdUpgrade = (*bool)(unsafe.Pointer(in.EtcdUpgrade))
out.ForceUpgrade = (*bool)(unsafe.Pointer(in.ForceUpgrade))
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
out.Patches = (*kubeadm.Patches)(unsafe.Pointer(in.Patches))
out.PrintConfig = (*bool)(unsafe.Pointer(in.PrintConfig))
out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases))
return nil
}
// Convert_v1beta4_UpgradeApplyConfiguration_To_kubeadm_UpgradeApplyConfiguration is an autogenerated conversion function.
func Convert_v1beta4_UpgradeApplyConfiguration_To_kubeadm_UpgradeApplyConfiguration(in *UpgradeApplyConfiguration, out *kubeadm.UpgradeApplyConfiguration, s conversion.Scope) error {
return autoConvert_v1beta4_UpgradeApplyConfiguration_To_kubeadm_UpgradeApplyConfiguration(in, out, s)
}
func autoConvert_kubeadm_UpgradeApplyConfiguration_To_v1beta4_UpgradeApplyConfiguration(in *kubeadm.UpgradeApplyConfiguration, out *UpgradeApplyConfiguration, s conversion.Scope) error {
out.KubernetesVersion = in.KubernetesVersion
out.AllowExperimentalUpgrades = (*bool)(unsafe.Pointer(in.AllowExperimentalUpgrades))
out.AllowRCUpgrades = (*bool)(unsafe.Pointer(in.AllowRCUpgrades))
out.CertificateRenewal = (*bool)(unsafe.Pointer(in.CertificateRenewal))
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
out.EtcdUpgrade = (*bool)(unsafe.Pointer(in.EtcdUpgrade))
out.ForceUpgrade = (*bool)(unsafe.Pointer(in.ForceUpgrade))
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
out.Patches = (*Patches)(unsafe.Pointer(in.Patches))
out.PrintConfig = (*bool)(unsafe.Pointer(in.PrintConfig))
out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases))
return nil
}
// Convert_kubeadm_UpgradeApplyConfiguration_To_v1beta4_UpgradeApplyConfiguration is an autogenerated conversion function.
func Convert_kubeadm_UpgradeApplyConfiguration_To_v1beta4_UpgradeApplyConfiguration(in *kubeadm.UpgradeApplyConfiguration, out *UpgradeApplyConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_UpgradeApplyConfiguration_To_v1beta4_UpgradeApplyConfiguration(in, out, s)
}
func autoConvert_v1beta4_UpgradeConfiguration_To_kubeadm_UpgradeConfiguration(in *UpgradeConfiguration, out *kubeadm.UpgradeConfiguration, s conversion.Scope) error {
if err := Convert_v1beta4_UpgradeApplyConfiguration_To_kubeadm_UpgradeApplyConfiguration(&in.Apply, &out.Apply, s); err != nil {
return err
}
if err := Convert_v1beta4_UpgradeDiffConfiguration_To_kubeadm_UpgradeDiffConfiguration(&in.Diff, &out.Diff, s); err != nil {
return err
}
if err := Convert_v1beta4_UpgradeNodeConfiguration_To_kubeadm_UpgradeNodeConfiguration(&in.Node, &out.Node, s); err != nil {
return err
}
if err := Convert_v1beta4_UpgradePlanConfiguration_To_kubeadm_UpgradePlanConfiguration(&in.Plan, &out.Plan, s); err != nil {
return err
}
return nil
}
// Convert_v1beta4_UpgradeConfiguration_To_kubeadm_UpgradeConfiguration is an autogenerated conversion function.
func Convert_v1beta4_UpgradeConfiguration_To_kubeadm_UpgradeConfiguration(in *UpgradeConfiguration, out *kubeadm.UpgradeConfiguration, s conversion.Scope) error {
return autoConvert_v1beta4_UpgradeConfiguration_To_kubeadm_UpgradeConfiguration(in, out, s)
}
func autoConvert_kubeadm_UpgradeConfiguration_To_v1beta4_UpgradeConfiguration(in *kubeadm.UpgradeConfiguration, out *UpgradeConfiguration, s conversion.Scope) error {
if err := Convert_kubeadm_UpgradeApplyConfiguration_To_v1beta4_UpgradeApplyConfiguration(&in.Apply, &out.Apply, s); err != nil {
return err
}
if err := Convert_kubeadm_UpgradeDiffConfiguration_To_v1beta4_UpgradeDiffConfiguration(&in.Diff, &out.Diff, s); err != nil {
return err
}
if err := Convert_kubeadm_UpgradeNodeConfiguration_To_v1beta4_UpgradeNodeConfiguration(&in.Node, &out.Node, s); err != nil {
return err
}
if err := Convert_kubeadm_UpgradePlanConfiguration_To_v1beta4_UpgradePlanConfiguration(&in.Plan, &out.Plan, s); err != nil {
return err
}
return nil
}
// Convert_kubeadm_UpgradeConfiguration_To_v1beta4_UpgradeConfiguration is an autogenerated conversion function.
func Convert_kubeadm_UpgradeConfiguration_To_v1beta4_UpgradeConfiguration(in *kubeadm.UpgradeConfiguration, out *UpgradeConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_UpgradeConfiguration_To_v1beta4_UpgradeConfiguration(in, out, s)
}
func autoConvert_v1beta4_UpgradeDiffConfiguration_To_kubeadm_UpgradeDiffConfiguration(in *UpgradeDiffConfiguration, out *kubeadm.UpgradeDiffConfiguration, s conversion.Scope) error {
out.KubernetesVersion = in.KubernetesVersion
out.DiffContextLines = in.DiffContextLines
return nil
}
// Convert_v1beta4_UpgradeDiffConfiguration_To_kubeadm_UpgradeDiffConfiguration is an autogenerated conversion function.
func Convert_v1beta4_UpgradeDiffConfiguration_To_kubeadm_UpgradeDiffConfiguration(in *UpgradeDiffConfiguration, out *kubeadm.UpgradeDiffConfiguration, s conversion.Scope) error {
return autoConvert_v1beta4_UpgradeDiffConfiguration_To_kubeadm_UpgradeDiffConfiguration(in, out, s)
}
func autoConvert_kubeadm_UpgradeDiffConfiguration_To_v1beta4_UpgradeDiffConfiguration(in *kubeadm.UpgradeDiffConfiguration, out *UpgradeDiffConfiguration, s conversion.Scope) error {
out.KubernetesVersion = in.KubernetesVersion
out.DiffContextLines = in.DiffContextLines
return nil
}
// Convert_kubeadm_UpgradeDiffConfiguration_To_v1beta4_UpgradeDiffConfiguration is an autogenerated conversion function.
func Convert_kubeadm_UpgradeDiffConfiguration_To_v1beta4_UpgradeDiffConfiguration(in *kubeadm.UpgradeDiffConfiguration, out *UpgradeDiffConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_UpgradeDiffConfiguration_To_v1beta4_UpgradeDiffConfiguration(in, out, s)
}
func autoConvert_v1beta4_UpgradeNodeConfiguration_To_kubeadm_UpgradeNodeConfiguration(in *UpgradeNodeConfiguration, out *kubeadm.UpgradeNodeConfiguration, s conversion.Scope) error {
out.CertificateRenewal = (*bool)(unsafe.Pointer(in.CertificateRenewal))
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
out.EtcdUpgrade = (*bool)(unsafe.Pointer(in.EtcdUpgrade))
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases))
out.Patches = (*kubeadm.Patches)(unsafe.Pointer(in.Patches))
return nil
}
// Convert_v1beta4_UpgradeNodeConfiguration_To_kubeadm_UpgradeNodeConfiguration is an autogenerated conversion function.
func Convert_v1beta4_UpgradeNodeConfiguration_To_kubeadm_UpgradeNodeConfiguration(in *UpgradeNodeConfiguration, out *kubeadm.UpgradeNodeConfiguration, s conversion.Scope) error {
return autoConvert_v1beta4_UpgradeNodeConfiguration_To_kubeadm_UpgradeNodeConfiguration(in, out, s)
}
func autoConvert_kubeadm_UpgradeNodeConfiguration_To_v1beta4_UpgradeNodeConfiguration(in *kubeadm.UpgradeNodeConfiguration, out *UpgradeNodeConfiguration, s conversion.Scope) error {
out.CertificateRenewal = (*bool)(unsafe.Pointer(in.CertificateRenewal))
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
out.EtcdUpgrade = (*bool)(unsafe.Pointer(in.EtcdUpgrade))
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
out.SkipPhases = *(*[]string)(unsafe.Pointer(&in.SkipPhases))
out.Patches = (*Patches)(unsafe.Pointer(in.Patches))
return nil
}
// Convert_kubeadm_UpgradeNodeConfiguration_To_v1beta4_UpgradeNodeConfiguration is an autogenerated conversion function.
func Convert_kubeadm_UpgradeNodeConfiguration_To_v1beta4_UpgradeNodeConfiguration(in *kubeadm.UpgradeNodeConfiguration, out *UpgradeNodeConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_UpgradeNodeConfiguration_To_v1beta4_UpgradeNodeConfiguration(in, out, s)
}
func autoConvert_v1beta4_UpgradePlanConfiguration_To_kubeadm_UpgradePlanConfiguration(in *UpgradePlanConfiguration, out *kubeadm.UpgradePlanConfiguration, s conversion.Scope) error {
out.KubernetesVersion = in.KubernetesVersion
out.AllowExperimentalUpgrades = (*bool)(unsafe.Pointer(in.AllowExperimentalUpgrades))
out.AllowRCUpgrades = (*bool)(unsafe.Pointer(in.AllowRCUpgrades))
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
out.PrintConfig = (*bool)(unsafe.Pointer(in.PrintConfig))
return nil
}
// Convert_v1beta4_UpgradePlanConfiguration_To_kubeadm_UpgradePlanConfiguration is an autogenerated conversion function.
func Convert_v1beta4_UpgradePlanConfiguration_To_kubeadm_UpgradePlanConfiguration(in *UpgradePlanConfiguration, out *kubeadm.UpgradePlanConfiguration, s conversion.Scope) error {
return autoConvert_v1beta4_UpgradePlanConfiguration_To_kubeadm_UpgradePlanConfiguration(in, out, s)
}
func autoConvert_kubeadm_UpgradePlanConfiguration_To_v1beta4_UpgradePlanConfiguration(in *kubeadm.UpgradePlanConfiguration, out *UpgradePlanConfiguration, s conversion.Scope) error {
out.KubernetesVersion = in.KubernetesVersion
out.AllowExperimentalUpgrades = (*bool)(unsafe.Pointer(in.AllowExperimentalUpgrades))
out.AllowRCUpgrades = (*bool)(unsafe.Pointer(in.AllowRCUpgrades))
out.DryRun = (*bool)(unsafe.Pointer(in.DryRun))
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
out.PrintConfig = (*bool)(unsafe.Pointer(in.PrintConfig))
return nil
}
// Convert_kubeadm_UpgradePlanConfiguration_To_v1beta4_UpgradePlanConfiguration is an autogenerated conversion function.
func Convert_kubeadm_UpgradePlanConfiguration_To_v1beta4_UpgradePlanConfiguration(in *kubeadm.UpgradePlanConfiguration, out *UpgradePlanConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_UpgradePlanConfiguration_To_v1beta4_UpgradePlanConfiguration(in, out, s)
}

View File

@@ -658,3 +658,201 @@ func (in *Timeouts) DeepCopy() *Timeouts {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradeApplyConfiguration) DeepCopyInto(out *UpgradeApplyConfiguration) {
*out = *in
if in.AllowExperimentalUpgrades != nil {
in, out := &in.AllowExperimentalUpgrades, &out.AllowExperimentalUpgrades
*out = new(bool)
**out = **in
}
if in.AllowRCUpgrades != nil {
in, out := &in.AllowRCUpgrades, &out.AllowRCUpgrades
*out = new(bool)
**out = **in
}
if in.CertificateRenewal != nil {
in, out := &in.CertificateRenewal, &out.CertificateRenewal
*out = new(bool)
**out = **in
}
if in.DryRun != nil {
in, out := &in.DryRun, &out.DryRun
*out = new(bool)
**out = **in
}
if in.EtcdUpgrade != nil {
in, out := &in.EtcdUpgrade, &out.EtcdUpgrade
*out = new(bool)
**out = **in
}
if in.ForceUpgrade != nil {
in, out := &in.ForceUpgrade, &out.ForceUpgrade
*out = new(bool)
**out = **in
}
if in.IgnorePreflightErrors != nil {
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Patches != nil {
in, out := &in.Patches, &out.Patches
*out = new(Patches)
**out = **in
}
if in.PrintConfig != nil {
in, out := &in.PrintConfig, &out.PrintConfig
*out = new(bool)
**out = **in
}
if in.SkipPhases != nil {
in, out := &in.SkipPhases, &out.SkipPhases
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeApplyConfiguration.
func (in *UpgradeApplyConfiguration) DeepCopy() *UpgradeApplyConfiguration {
if in == nil {
return nil
}
out := new(UpgradeApplyConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradeConfiguration) DeepCopyInto(out *UpgradeConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
in.Apply.DeepCopyInto(&out.Apply)
out.Diff = in.Diff
in.Node.DeepCopyInto(&out.Node)
in.Plan.DeepCopyInto(&out.Plan)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeConfiguration.
func (in *UpgradeConfiguration) DeepCopy() *UpgradeConfiguration {
if in == nil {
return nil
}
out := new(UpgradeConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *UpgradeConfiguration) 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 *UpgradeDiffConfiguration) DeepCopyInto(out *UpgradeDiffConfiguration) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeDiffConfiguration.
func (in *UpgradeDiffConfiguration) DeepCopy() *UpgradeDiffConfiguration {
if in == nil {
return nil
}
out := new(UpgradeDiffConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradeNodeConfiguration) DeepCopyInto(out *UpgradeNodeConfiguration) {
*out = *in
if in.CertificateRenewal != nil {
in, out := &in.CertificateRenewal, &out.CertificateRenewal
*out = new(bool)
**out = **in
}
if in.DryRun != nil {
in, out := &in.DryRun, &out.DryRun
*out = new(bool)
**out = **in
}
if in.EtcdUpgrade != nil {
in, out := &in.EtcdUpgrade, &out.EtcdUpgrade
*out = new(bool)
**out = **in
}
if in.IgnorePreflightErrors != nil {
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.SkipPhases != nil {
in, out := &in.SkipPhases, &out.SkipPhases
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Patches != nil {
in, out := &in.Patches, &out.Patches
*out = new(Patches)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeNodeConfiguration.
func (in *UpgradeNodeConfiguration) DeepCopy() *UpgradeNodeConfiguration {
if in == nil {
return nil
}
out := new(UpgradeNodeConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradePlanConfiguration) DeepCopyInto(out *UpgradePlanConfiguration) {
*out = *in
if in.AllowExperimentalUpgrades != nil {
in, out := &in.AllowExperimentalUpgrades, &out.AllowExperimentalUpgrades
*out = new(bool)
**out = **in
}
if in.AllowRCUpgrades != nil {
in, out := &in.AllowRCUpgrades, &out.AllowRCUpgrades
*out = new(bool)
**out = **in
}
if in.DryRun != nil {
in, out := &in.DryRun, &out.DryRun
*out = new(bool)
**out = **in
}
if in.IgnorePreflightErrors != nil {
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.PrintConfig != nil {
in, out := &in.PrintConfig, &out.PrintConfig
*out = new(bool)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradePlanConfiguration.
func (in *UpgradePlanConfiguration) DeepCopy() *UpgradePlanConfiguration {
if in == nil {
return nil
}
out := new(UpgradePlanConfiguration)
in.DeepCopyInto(out)
return out
}

View File

@@ -33,6 +33,7 @@ func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&InitConfiguration{}, func(obj interface{}) { SetObjectDefaults_InitConfiguration(obj.(*InitConfiguration)) })
scheme.AddTypeDefaultingFunc(&JoinConfiguration{}, func(obj interface{}) { SetObjectDefaults_JoinConfiguration(obj.(*JoinConfiguration)) })
scheme.AddTypeDefaultingFunc(&ResetConfiguration{}, func(obj interface{}) { SetObjectDefaults_ResetConfiguration(obj.(*ResetConfiguration)) })
scheme.AddTypeDefaultingFunc(&UpgradeConfiguration{}, func(obj interface{}) { SetObjectDefaults_UpgradeConfiguration(obj.(*UpgradeConfiguration)) })
return nil
}
@@ -87,3 +88,7 @@ func SetObjectDefaults_ResetConfiguration(in *ResetConfiguration) {
SetDefaults_Timeouts(in.Timeouts)
}
}
func SetObjectDefaults_UpgradeConfiguration(in *UpgradeConfiguration) {
SetDefaults_UpgradeConfiguration(in)
}

View File

@@ -758,3 +758,12 @@ func ValidateImagePullPolicy(policy corev1.PullPolicy, fldPath *field.Path) fiel
return allErrs
}
}
// ValidateUpgradeConfiguration validates a UpgradeConfiguration object and collects all encountered errors
func ValidateUpgradeConfiguration(c *kubeadm.UpgradeConfiguration) field.ErrorList {
allErrs := field.ErrorList{}
if c.Apply.Patches != nil {
allErrs = append(allErrs, ValidateAbsolutePath(c.Apply.Patches.Directory, field.NewPath("patches").Child("directory"))...)
}
return allErrs
}

View File

@@ -744,11 +744,14 @@ func TestValidateMixedArguments(t *testing.T) {
{[]string{"--foo=bar"}, true},
{[]string{"--config=hello"}, true},
{[]string{"--config=hello", "--ignore-preflight-errors=all"}, true},
// Expected to succeed, --config is mixed with skip-* flags only or no other flags
{[]string{"--config=hello", "--skip-token-print=true"}, true},
{[]string{"--config=hello", "--ignore-preflight-errors=baz", "--skip-token-print"}, true},
// Expected to fail, --config is mixed with the --foo flag
{[]string{"--config=hello", "--ignore-preflight-errors=baz", "--foo=bar"}, false},
{[]string{"--config=hello", "--foo=bar"}, false},
// Expected to fail, --config is mixed with the upgrade related flag
{[]string{"--config=hello", "--allow-experimental-upgrades"}, false},
}
var cfgPath string
@@ -760,6 +763,7 @@ func TestValidateMixedArguments(t *testing.T) {
}
f.String("foo", "", "flag bound to config object")
f.StringSliceVar(&ignorePreflightErrors, "ignore-preflight-errors", ignorePreflightErrors, "flag not bound to config object")
f.Bool("allow-experimental-upgrades", true, "upgrade flags for plan and apply command")
f.Bool("skip-token-print", false, "flag not bound to config object")
f.StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
if err := f.Parse(rt.args); err != nil {

View File

@@ -698,3 +698,201 @@ func (in *Timeouts) DeepCopy() *Timeouts {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradeApplyConfiguration) DeepCopyInto(out *UpgradeApplyConfiguration) {
*out = *in
if in.AllowExperimentalUpgrades != nil {
in, out := &in.AllowExperimentalUpgrades, &out.AllowExperimentalUpgrades
*out = new(bool)
**out = **in
}
if in.AllowRCUpgrades != nil {
in, out := &in.AllowRCUpgrades, &out.AllowRCUpgrades
*out = new(bool)
**out = **in
}
if in.CertificateRenewal != nil {
in, out := &in.CertificateRenewal, &out.CertificateRenewal
*out = new(bool)
**out = **in
}
if in.DryRun != nil {
in, out := &in.DryRun, &out.DryRun
*out = new(bool)
**out = **in
}
if in.EtcdUpgrade != nil {
in, out := &in.EtcdUpgrade, &out.EtcdUpgrade
*out = new(bool)
**out = **in
}
if in.ForceUpgrade != nil {
in, out := &in.ForceUpgrade, &out.ForceUpgrade
*out = new(bool)
**out = **in
}
if in.IgnorePreflightErrors != nil {
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Patches != nil {
in, out := &in.Patches, &out.Patches
*out = new(Patches)
**out = **in
}
if in.PrintConfig != nil {
in, out := &in.PrintConfig, &out.PrintConfig
*out = new(bool)
**out = **in
}
if in.SkipPhases != nil {
in, out := &in.SkipPhases, &out.SkipPhases
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeApplyConfiguration.
func (in *UpgradeApplyConfiguration) DeepCopy() *UpgradeApplyConfiguration {
if in == nil {
return nil
}
out := new(UpgradeApplyConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradeConfiguration) DeepCopyInto(out *UpgradeConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
in.Apply.DeepCopyInto(&out.Apply)
out.Diff = in.Diff
in.Node.DeepCopyInto(&out.Node)
in.Plan.DeepCopyInto(&out.Plan)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeConfiguration.
func (in *UpgradeConfiguration) DeepCopy() *UpgradeConfiguration {
if in == nil {
return nil
}
out := new(UpgradeConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *UpgradeConfiguration) 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 *UpgradeDiffConfiguration) DeepCopyInto(out *UpgradeDiffConfiguration) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeDiffConfiguration.
func (in *UpgradeDiffConfiguration) DeepCopy() *UpgradeDiffConfiguration {
if in == nil {
return nil
}
out := new(UpgradeDiffConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradeNodeConfiguration) DeepCopyInto(out *UpgradeNodeConfiguration) {
*out = *in
if in.CertificateRenewal != nil {
in, out := &in.CertificateRenewal, &out.CertificateRenewal
*out = new(bool)
**out = **in
}
if in.DryRun != nil {
in, out := &in.DryRun, &out.DryRun
*out = new(bool)
**out = **in
}
if in.EtcdUpgrade != nil {
in, out := &in.EtcdUpgrade, &out.EtcdUpgrade
*out = new(bool)
**out = **in
}
if in.IgnorePreflightErrors != nil {
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.SkipPhases != nil {
in, out := &in.SkipPhases, &out.SkipPhases
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Patches != nil {
in, out := &in.Patches, &out.Patches
*out = new(Patches)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradeNodeConfiguration.
func (in *UpgradeNodeConfiguration) DeepCopy() *UpgradeNodeConfiguration {
if in == nil {
return nil
}
out := new(UpgradeNodeConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UpgradePlanConfiguration) DeepCopyInto(out *UpgradePlanConfiguration) {
*out = *in
if in.AllowExperimentalUpgrades != nil {
in, out := &in.AllowExperimentalUpgrades, &out.AllowExperimentalUpgrades
*out = new(bool)
**out = **in
}
if in.AllowRCUpgrades != nil {
in, out := &in.AllowRCUpgrades, &out.AllowRCUpgrades
*out = new(bool)
**out = **in
}
if in.DryRun != nil {
in, out := &in.DryRun, &out.DryRun
*out = new(bool)
**out = **in
}
if in.IgnorePreflightErrors != nil {
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.PrintConfig != nil {
in, out := &in.PrintConfig, &out.PrintConfig
*out = new(bool)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpgradePlanConfiguration.
func (in *UpgradePlanConfiguration) DeepCopy() *UpgradePlanConfiguration {
if in == nil {
return nil
}
out := new(UpgradePlanConfiguration)
in.DeepCopyInto(out)
return out
}

View File

@@ -145,4 +145,13 @@ const (
// AllowExperimentalAPI flag can be used to allow experimental / work in progress APIs
AllowExperimentalAPI = "allow-experimental-api"
// AllowRCUpgrades enable this flag will allow upgrading to a release candidate version of Kubernetes.
AllowRCUpgrades = "allow-release-candidate-upgrades"
// AllowExperimentalUpgrades enable this flag will allow upgrading to an alpha/beta/release candidate version of Kubernetes.
AllowExperimentalUpgrades = "allow-experimental-upgrades"
// PrintConfig specifies whether the cluster configuration that will be used in the upgrade should be printed or not.
PrintConfig = "print-config"
)

View File

@@ -60,7 +60,7 @@ func runControlPlane() func(c workflow.RunData) error {
}
// otherwise, retrieve all the info required for control plane upgrade
cfg := data.Cfg()
cfg := data.InitCfg()
client := data.Client()
dryRun := data.DryRun()
etcdUpgrade := data.EtcdUpgrade()

View File

@@ -31,7 +31,8 @@ type Data interface {
EtcdUpgrade() bool
RenewCerts() bool
DryRun() bool
Cfg() *kubeadmapi.InitConfiguration
Cfg() *kubeadmapi.UpgradeConfiguration
InitCfg() *kubeadmapi.InitConfiguration
IsControlPlaneNode() bool
Client() clientset.Interface
IgnorePreflightErrors() sets.Set[string]

View File

@@ -34,7 +34,8 @@ var _ Data = &testData{}
func (t *testData) EtcdUpgrade() bool { return false }
func (t *testData) RenewCerts() bool { return false }
func (t *testData) DryRun() bool { return false }
func (t *testData) Cfg() *kubeadmapi.InitConfiguration { return nil }
func (t *testData) Cfg() *kubeadmapi.UpgradeConfiguration { return nil }
func (t *testData) InitCfg() *kubeadmapi.InitConfiguration { return nil }
func (t *testData) IsControlPlaneNode() bool { return false }
func (t *testData) Client() clientset.Interface { return nil }
func (t *testData) IgnorePreflightErrors() sets.Set[string] { return nil }

View File

@@ -57,7 +57,7 @@ func runKubeletConfigPhase() func(c workflow.RunData) error {
}
// otherwise, retrieve all the info required for kubelet config upgrade
cfg := data.Cfg()
cfg := data.InitCfg()
dryRun := data.DryRun()
// Write the configuration for the kubelet down to disk and print the generated manifests instead if dry-running.

View File

@@ -60,7 +60,7 @@ func runPreflight(c workflow.RunData) error {
fmt.Println("[preflight] Pulling images required for setting up a Kubernetes cluster")
fmt.Println("[preflight] This might take a minute or two, depending on the speed of your internet connection")
fmt.Println("[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'")
if err := preflight.RunPullImagesCheck(utilsexec.New(), data.Cfg(), data.IgnorePreflightErrors()); err != nil {
if err := preflight.RunPullImagesCheck(utilsexec.New(), data.InitCfg(), data.IgnorePreflightErrors()); err != nil {
return err
}
} else {

View File

@@ -22,6 +22,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/version"
@@ -30,6 +31,7 @@ import (
utilsexec "k8s.io/utils/exec"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
@@ -71,7 +73,10 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command {
DisableFlagsInUseLine: true,
Short: "Upgrade your Kubernetes cluster to the specified version",
RunE: func(cmd *cobra.Command, args []string) error {
return runApply(flags, args)
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
return err
}
return runApply(cmd.Flags(), flags, args)
},
}
@@ -81,7 +86,7 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command {
cmd.Flags().BoolVarP(&flags.nonInteractiveMode, "yes", "y", flags.nonInteractiveMode, "Perform the upgrade and do not prompt for confirmation (non-interactive mode).")
cmd.Flags().BoolVarP(&flags.force, "force", "f", flags.force, "Force upgrading although some requirements might not be met. This also implies non-interactive mode.")
cmd.Flags().BoolVar(&flags.dryRun, options.DryRun, flags.dryRun, "Do not change any state, just output what actions would be performed.")
cmd.Flags().BoolVar(&flags.etcdUpgrade, "etcd-upgrade", flags.etcdUpgrade, "Perform the upgrade of etcd.")
cmd.Flags().BoolVar(&flags.etcdUpgrade, options.EtcdUpgrade, flags.etcdUpgrade, "Perform the upgrade of etcd.")
cmd.Flags().BoolVar(&flags.renewCerts, options.CertificateRenewal, flags.renewCerts, "Perform the renewal of certificates used by component changed during upgrades.")
options.AddPatchesFlag(cmd.Flags(), &flags.patchesDir)
@@ -100,39 +105,67 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command {
// - Uploads the newly used configuration to the cluster ConfigMap
// - Creating the RBAC rules for the bootstrap tokens and the cluster-info ConfigMap
// - Applying new CoreDNS and kube-proxy manifests
func runApply(flags *applyFlags, args []string) error {
func runApply(flagSet *pflag.FlagSet, flags *applyFlags, args []string) error {
// Start with the basics, verify that the cluster is healthy and get the configuration from the cluster (using the ConfigMap)
klog.V(1).Infoln("[upgrade/apply] verifying health of cluster")
klog.V(1).Infoln("[upgrade/apply] retrieving configuration from cluster")
client, versionGetter, cfg, err := enforceRequirements(flags.applyPlanFlags, args, flags.dryRun, true, &output.TextPrinter{}, loadConfig)
client, versionGetter, initCfg, upgradeCfg, err := enforceRequirements(flagSet, flags.applyPlanFlags, args, flags.dryRun, true, &output.TextPrinter{})
if err != nil {
return err
}
// Validate requested and validate actual version
klog.V(1).Infoln("[upgrade/apply] validating requested and actual version")
if err := configutil.NormalizeKubernetesVersion(&cfg.ClusterConfiguration); err != nil {
if err := configutil.NormalizeKubernetesVersion(&initCfg.ClusterConfiguration); err != nil {
return err
}
// Use normalized version string in all following code.
newK8sVersion, err := version.ParseSemantic(cfg.KubernetesVersion)
newK8sVersion, err := version.ParseSemantic(initCfg.KubernetesVersion)
if err != nil {
return errors.Errorf("unable to parse normalized version %q as a semantic version", cfg.KubernetesVersion)
return errors.Errorf("unable to parse normalized version %q as a semantic version", initCfg.KubernetesVersion)
}
if err := features.ValidateVersion(features.InitFeatureGates, cfg.FeatureGates, cfg.KubernetesVersion); err != nil {
if err := features.ValidateVersion(features.InitFeatureGates, initCfg.FeatureGates, initCfg.KubernetesVersion); err != nil {
return err
}
// Enforce the version skew policies
klog.V(1).Infoln("[upgrade/version] enforcing version skew policies")
if err := EnforceVersionPolicies(cfg.KubernetesVersion, newK8sVersion, flags, versionGetter); err != nil {
allowRCUpgrades, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.AllowRCUpgrades, upgradeCfg.Apply.AllowRCUpgrades, &flags.allowRCUpgrades).(*bool)
if ok {
flags.allowRCUpgrades = *allowRCUpgrades
} else {
return cmdutil.TypeMismatchErr("allowRCUpgrades", "bool")
}
force, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, "force", upgradeCfg.Apply.ForceUpgrade, &flags.force).(*bool)
if ok {
flags.force = *force
} else {
return cmdutil.TypeMismatchErr("force", "bool")
}
allowExperimentalUpgrades, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.AllowExperimentalUpgrades, upgradeCfg.Apply.AllowExperimentalUpgrades, &flags.allowExperimentalUpgrades).(*bool)
if ok {
flags.allowExperimentalUpgrades = *allowExperimentalUpgrades
} else {
return cmdutil.TypeMismatchErr("allowExperimentalUpgrades", "bool")
}
if err := EnforceVersionPolicies(initCfg.KubernetesVersion, newK8sVersion, flags, versionGetter); err != nil {
return errors.Wrap(err, "[upgrade/version] FATAL")
}
// If the current session is interactive, ask the user whether they really want to upgrade.
dryRun, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.DryRun, upgradeCfg.Apply.DryRun, &flags.dryRun).(*bool)
if ok {
flags.dryRun = *dryRun
} else {
return cmdutil.TypeMismatchErr("dryRun", "bool")
}
if flags.sessionIsInteractive() {
if err := cmdutil.InteractivelyConfirmAction("upgrade", "Are you sure you want to proceed?", os.Stdin); err != nil {
return err
@@ -143,7 +176,7 @@ func runApply(flags *applyFlags, args []string) error {
fmt.Println("[upgrade/prepull] Pulling images required for setting up a Kubernetes cluster")
fmt.Println("[upgrade/prepull] This might take a minute or two, depending on the speed of your internet connection")
fmt.Println("[upgrade/prepull] You can also perform this action in beforehand using 'kubeadm config images pull'")
if err := preflight.RunPullImagesCheck(utilsexec.New(), cfg, sets.New(cfg.NodeRegistration.IgnorePreflightErrors...)); err != nil {
if err := preflight.RunPullImagesCheck(utilsexec.New(), initCfg, sets.New(upgradeCfg.Apply.IgnorePreflightErrors...)); err != nil {
return err
}
} else {
@@ -152,14 +185,35 @@ func runApply(flags *applyFlags, args []string) error {
waiter := getWaiter(flags.dryRun, client, upgrade.UpgradeManifestTimeout)
// If the config is set by flag, just overwrite it!
etcdUpgrade, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.EtcdUpgrade, upgradeCfg.Apply.EtcdUpgrade, &flags.etcdUpgrade).(*bool)
if ok {
upgradeCfg.Apply.EtcdUpgrade = etcdUpgrade
} else {
return cmdutil.TypeMismatchErr("etcdUpgrade", "bool")
}
renewCerts, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.CertificateRenewal, upgradeCfg.Apply.CertificateRenewal, &flags.renewCerts).(*bool)
if ok {
upgradeCfg.Apply.CertificateRenewal = renewCerts
} else {
return cmdutil.TypeMismatchErr("renewCerts", "bool")
}
if len(flags.patchesDir) > 0 {
upgradeCfg.Apply.Patches = &kubeadmapi.Patches{Directory: flags.patchesDir}
} else if upgradeCfg.Apply.Patches == nil {
upgradeCfg.Apply.Patches = &kubeadmapi.Patches{}
}
// Now; perform the upgrade procedure
if err := PerformControlPlaneUpgrade(flags, client, waiter, cfg); err != nil {
if err := PerformControlPlaneUpgrade(flags, client, waiter, initCfg, upgradeCfg); err != nil {
return errors.Wrap(err, "[upgrade/apply] FATAL")
}
// Upgrade RBAC rules and addons.
klog.V(1).Infoln("[upgrade/postupgrade] upgrading RBAC rules and addons")
if err := upgrade.PerformPostUpgradeTasks(client, cfg, flags.patchesDir, flags.dryRun, flags.applyPlanFlags.out); err != nil {
if err := upgrade.PerformPostUpgradeTasks(client, initCfg, upgradeCfg.Apply.Patches.Directory, flags.dryRun, flags.applyPlanFlags.out); err != nil {
return errors.Wrap(err, "[upgrade/postupgrade] FATAL post-upgrade error")
}
@@ -169,7 +223,7 @@ func runApply(flags *applyFlags, args []string) error {
}
fmt.Println("")
fmt.Printf("[upgrade/successful] SUCCESS! Your cluster was upgraded to %q. Enjoy!\n", cfg.KubernetesVersion)
fmt.Printf("[upgrade/successful] SUCCESS! Your cluster was upgraded to %q. Enjoy!\n", initCfg.KubernetesVersion)
fmt.Println("")
fmt.Println("[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.")
@@ -203,15 +257,14 @@ func EnforceVersionPolicies(newK8sVersionStr string, newK8sVersion *version.Vers
}
// PerformControlPlaneUpgrade actually performs the upgrade procedure for the cluster of your type (self-hosted or static-pod-hosted)
func PerformControlPlaneUpgrade(flags *applyFlags, client clientset.Interface, waiter apiclient.Waiter, internalcfg *kubeadmapi.InitConfiguration) error {
func PerformControlPlaneUpgrade(flags *applyFlags, client clientset.Interface, waiter apiclient.Waiter, initCfg *kubeadmapi.InitConfiguration, upgradeCfg *kubeadmapi.UpgradeConfiguration) error {
// OK, the cluster is hosted using static pods. Upgrade a static-pod hosted cluster
fmt.Printf("[upgrade/apply] Upgrading your Static Pod-hosted control plane to version %q (timeout: %v)...\n",
internalcfg.KubernetesVersion, upgrade.UpgradeManifestTimeout)
initCfg.KubernetesVersion, upgrade.UpgradeManifestTimeout)
if flags.dryRun {
return upgrade.DryRunStaticPodUpgrade(flags.patchesDir, internalcfg)
return upgrade.DryRunStaticPodUpgrade(upgradeCfg.Apply.Patches.Directory, initCfg)
}
return upgrade.PerformStaticPodUpgrade(client, waiter, internalcfg, flags.etcdUpgrade, flags.renewCerts, flags.patchesDir)
return upgrade.PerformStaticPodUpgrade(client, waiter, initCfg, *upgradeCfg.Apply.EtcdUpgrade, *upgradeCfg.Apply.CertificateRenewal, upgradeCfg.Apply.Patches.Directory)
}

View File

@@ -24,21 +24,25 @@ import (
"time"
"github.com/pkg/errors"
"github.com/spf13/pflag"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
fakediscovery "k8s.io/client-go/discovery/fake"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
"k8s.io/utils/ptr"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
@@ -46,162 +50,121 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
)
// isKubeadmConfigPresent checks if a kubeadm config type is found in the provided document map
func isKubeadmConfigPresent(docmap kubeadmapi.DocumentMap) bool {
for gvk := range docmap {
if gvk.Group == kubeadmapi.GroupName {
return true
}
}
return false
}
// loadConfig loads configuration from a file and/or the cluster. InitConfiguration, ClusterConfiguration and (optionally) component configs
// are loaded. This function allows the component configs to be loaded from a file that contains only them. If the file contains any kubeadm types
// in it (API group "kubeadm.k8s.io" present), then the supplied file is treaded as a legacy reconfiguration style "--config" use and the
// returned bool value is set to true (the only case to be done so).
func loadConfig(cfgPath string, client clientset.Interface, skipComponentConfigs bool, printer output.Printer) (*kubeadmapi.InitConfiguration, bool, error) {
// Used for info logs here
const logPrefix = "upgrade/config"
// The usual case here is to not have a config file, but rather load the config from the cluster.
// This is probably 90% of the time. So we handle it first.
if cfgPath == "" {
cfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, logPrefix, false, skipComponentConfigs)
return cfg, false, err
}
// Otherwise, we have a config file. Let's load it.
configBytes, err := os.ReadFile(cfgPath)
if err != nil {
return nil, false, errors.Wrapf(err, "unable to load config from file %q", cfgPath)
}
// Split the YAML documents in the file into a DocumentMap
docmap, err := kubeadmutil.SplitYAMLDocuments(configBytes)
if err != nil {
return nil, false, err
}
// If there are kubeadm types (API group kubeadm.k8s.io) present, we need to keep the existing behavior
// here. Basically, we have to load all of the configs from the file and none from the cluster. Configs that are
// missing from the file will be automatically regenerated by kubeadm even if they are present in the cluster.
// The resulting configs overwrite the existing cluster ones at the end of a successful upgrade apply operation.
if isKubeadmConfigPresent(docmap) {
klog.Warning("WARNING: Usage of the --config flag with kubeadm config types for reconfiguring the cluster during upgrade is not recommended!")
cfg, err := configutil.BytesToInitConfiguration(configBytes, false)
return cfg, true, err
}
// If no kubeadm config types are present, we assume that there are manually upgraded component configs in the file.
// Hence, we load the kubeadm types from the cluster.
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, logPrefix, false, true)
if err != nil {
return nil, false, err
}
// Stop here if the caller does not want us to load the component configs
if !skipComponentConfigs {
// Load the component configs with upgrades
if err := componentconfigs.FetchFromClusterWithLocalOverwrites(&initCfg.ClusterConfiguration, client, docmap); err != nil {
return nil, false, err
}
// Now default and validate the configs
componentconfigs.Default(&initCfg.ClusterConfiguration, &initCfg.LocalAPIEndpoint, &initCfg.NodeRegistration)
if errs := componentconfigs.Validate(&initCfg.ClusterConfiguration); len(errs) != 0 {
return nil, false, errs.ToAggregate()
}
}
return initCfg, false, nil
}
// LoadConfigFunc is a function type that loads configuration from a file and/or the cluster.
type LoadConfigFunc func(cfgPath string, client clientset.Interface, skipComponentConfigs bool, printer output.Printer) (*kubeadmapi.InitConfiguration, bool, error)
// enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure
func enforceRequirements(flags *applyPlanFlags, args []string, dryRun bool, upgradeApply bool, printer output.Printer, loadConfig LoadConfigFunc) (clientset.Interface, upgrade.VersionGetter, *kubeadmapi.InitConfiguration, error) {
client, err := getClient(flags.kubeConfigPath, dryRun)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
}
func enforceRequirements(flagSet *pflag.FlagSet, flags *applyPlanFlags, args []string, dryRun bool, upgradeApply bool, printer output.Printer) (clientset.Interface, upgrade.VersionGetter, *kubeadmapi.InitConfiguration, *kubeadmapi.UpgradeConfiguration, error) {
// Fetch the configuration from a file or ConfigMap and validate it
_, _ = printer.Println("[upgrade/config] Loading the kubeadm configuration")
_, _ = printer.Printf("[upgrade/config] Making sure the configuration is correct:\n")
var newK8sVersion string
cfg, legacyReconfigure, err := loadConfig(flags.cfgPath, client, !upgradeApply, printer)
upgradeCfg, err := configutil.LoadUpgradeConfig(flags.cfgPath)
if err != nil {
return nil, nil, nil, errors.Wrap(err, "could not load the kubeadm configuration")
} else if legacyReconfigure {
// Set the newK8sVersion to the value in the ClusterConfiguration. This is done, so that users who use the --config option
// to supply a new ClusterConfiguration don't have to specify the Kubernetes version twice,
// if they don't want to upgrade but just change a setting.
newK8sVersion = cfg.KubernetesVersion
return nil, nil, nil, nil, errors.Wrap(err, "[upgrade/upgrade config] FATAL")
}
// The version arg is mandatory, during upgrade apply, unless it's specified in the config file
if upgradeApply && newK8sVersion == "" {
if err := cmdutil.ValidateExactArgNumber(args, []string{"version"}); err != nil {
return nil, nil, nil, err
// `dryRun` should be always `false` for `kubeadm plan`.
isDryRun := ptr.To(false)
printConfigCfg := upgradeCfg.Plan.PrintConfig
ignoreErrCfg := upgradeCfg.Plan.IgnorePreflightErrors
ok := false
if upgradeApply {
printConfigCfg = upgradeCfg.Apply.PrintConfig
ignoreErrCfg = upgradeCfg.Apply.IgnorePreflightErrors
isDryRun, ok = cmdutil.ValueFromFlagsOrConfig(flagSet, options.DryRun, upgradeCfg.Apply.DryRun, &dryRun).(*bool)
if !ok {
return nil, nil, nil, nil, cmdutil.TypeMismatchErr("dryRun", "bool")
}
}
client, err := getClient(flags.kubeConfigPath, *isDryRun)
if err != nil {
return nil, nil, nil, nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
}
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(flags.ignorePreflightErrors, ignoreErrCfg)
if err != nil {
return nil, nil, nil, nil, err
}
// Also set the union of pre-flight errors to UpgradeConfiguration, to provide a consistent view of the runtime configuration.
// .Plan.IgnorePreflightErrors is not set as it's not used.
if upgradeApply {
upgradeCfg.Apply.IgnorePreflightErrors = sets.List(ignorePreflightErrorsSet)
}
// Ensure the user is root
klog.V(1).Info("running preflight checks")
if err := runPreflightChecks(client, ignorePreflightErrorsSet, printer); err != nil {
return nil, nil, nil, nil, err
}
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, printer, "upgrade/config", false, true)
if err != nil {
if apierrors.IsNotFound(err) {
_, _ = printer.Printf("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %s namespace must exist.\n", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
_, _ = printer.Printf("[upgrade/config] Next steps:\n")
_, _ = printer.Printf("\t- OPTION 1: Run 'kubeadm config upload from-flags' and specify the same CLI arguments you passed to 'kubeadm init' when you created your control-plane.\n")
_, _ = printer.Printf("\t- OPTION 2: Run 'kubeadm config upload from-file' and specify the same config file you passed to 'kubeadm init' when you created your control-plane.\n")
_, _ = printer.Println()
err = errors.Errorf("the ConfigMap %q in the %s namespace used for getting configuration information was not found", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
}
return nil, nil, nil, nil, errors.Wrap(err, "[upgrade/init config] FATAL")
}
newK8sVersion := upgradeCfg.Plan.KubernetesVersion
if upgradeApply {
newK8sVersion = upgradeCfg.Apply.KubernetesVersion
// The version arg is mandatory, during upgrade apply, unless it's specified in the config file
if newK8sVersion == "" {
if err := cmdutil.ValidateExactArgNumber(args, []string{"version"}); err != nil {
return nil, nil, nil, nil, err
}
}
}
// If option was specified in both args and config file, args will overwrite the config file.
if len(args) == 1 {
newK8sVersion = args[0]
if upgradeApply {
// The `upgrade apply` version always overwrites the KubernetesVersion in the returned cfg with the target
// version. While this is not the same for `upgrade plan` where the KubernetesVersion should be the old
// one (because the call to getComponentConfigVersionStates requires the currently installed version).
// This also makes the KubernetesVersion value returned for `upgrade plan` consistent as that command
// allows to not specify a target version in which case KubernetesVersion will always hold the currently
// installed one.
cfg.KubernetesVersion = newK8sVersion
}
}
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(flags.ignorePreflightErrors, cfg.NodeRegistration.IgnorePreflightErrors)
if err != nil {
return nil, nil, nil, err
}
// Also set the union of pre-flight errors to InitConfiguration, to provide a consistent view of the runtime configuration:
cfg.NodeRegistration.IgnorePreflightErrors = sets.List(ignorePreflightErrorsSet)
// Ensure the user is root
klog.V(1).Info("running preflight checks")
if err := runPreflightChecks(client, ignorePreflightErrorsSet, printer); err != nil {
return nil, nil, nil, err
if upgradeApply {
// The `upgrade apply` version always overwrites the KubernetesVersion in the returned cfg with the target
// version. While this is not the same for `upgrade plan` where the KubernetesVersion should be the old
// one (because the call to getComponentConfigVersionStates requires the currently installed version).
// This also makes the KubernetesVersion value returned for `upgrade plan` consistent as that command
// allows to not specify a target version in which case KubernetesVersion will always hold the currently
// installed one.
initCfg.KubernetesVersion = newK8sVersion
}
// Run healthchecks against the cluster
if err := upgrade.CheckClusterHealth(client, &cfg.ClusterConfiguration, ignorePreflightErrorsSet, printer); err != nil {
return nil, nil, nil, errors.Wrap(err, "[upgrade/health] FATAL")
if err := upgrade.CheckClusterHealth(client, &initCfg.ClusterConfiguration, ignorePreflightErrorsSet, printer); err != nil {
return nil, nil, nil, nil, errors.Wrap(err, "[upgrade/health] FATAL")
}
// If features gates are passed to the command line, use it (otherwise use featureGates from configuration)
if flags.featureGatesString != "" {
cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, flags.featureGatesString)
initCfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, flags.featureGatesString)
if err != nil {
return nil, nil, nil, errors.Wrap(err, "[upgrade/config] FATAL")
return nil, nil, nil, nil, errors.Wrap(err, "[upgrade/config] FATAL")
}
}
// 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, initCfg.FeatureGates); len(msg) > 0 {
for _, m := range msg {
printer.Printf("[upgrade/config] %s\n", m)
}
}
// If the user told us to print this information out; do it!
if flags.printConfig {
printConfiguration(&cfg.ClusterConfiguration, os.Stdout, printer)
printConfig, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.PrintConfig, printConfigCfg, &flags.printConfig).(*bool)
if ok && *printConfig {
printConfiguration(&initCfg.ClusterConfiguration, os.Stdout, printer)
} else if !ok {
return nil, nil, nil, nil, cmdutil.TypeMismatchErr("printConfig", "bool")
}
// Use a real version getter interface that queries the API server, the kubeadm client and the Kubernetes CI system for latest versions
return client, upgrade.NewOfflineVersionGetter(upgrade.NewKubeVersionGetter(client), newK8sVersion), cfg, nil
return client, upgrade.NewOfflineVersionGetter(upgrade.NewKubeVersionGetter(client), newK8sVersion), initCfg, upgradeCfg, nil
}
// printConfiguration prints the external version of the API to yaml

View File

@@ -24,7 +24,8 @@ import (
"strings"
"testing"
clientset "k8s.io/client-go/kubernetes"
"github.com/spf13/pflag"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
@@ -53,25 +54,18 @@ users:
client-certificate-data:
`
func fakeLoadConfig(cfgPath string, client clientset.Interface, skipComponentConfigs bool, printer output.Printer) (*kubeadmapi.InitConfiguration, bool, error) {
return &kubeadmapi.InitConfiguration{}, false, nil
}
func TestEnforceRequirements(t *testing.T) {
tmpDir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpDir)
fullPath := filepath.Join(tmpDir, "test-config-file")
f, err := os.Create(fullPath)
if err != nil {
t.Errorf("Unable to create test file %q: %v", fullPath, err)
}
defer f.Close()
if _, err = f.WriteString(testConfigToken); err != nil {
t.Errorf("Unable to write test file %q: %v", fullPath, err)
}
tcases := []struct {
name string
newK8sVersion string
@@ -106,8 +100,7 @@ func TestEnforceRequirements(t *testing.T) {
}
for _, tt := range tcases {
t.Run(tt.name, func(t *testing.T) {
_, _, _, err := enforceRequirements(&tt.flags, nil, tt.dryRun, false, &output.TextPrinter{}, fakeLoadConfig)
_, _, _, _, err := enforceRequirements(&pflag.FlagSet{}, &tt.flags, nil, tt.dryRun, false, &output.TextPrinter{})
if err == nil && len(tt.expectedErr) != 0 {
t.Error("Expected error, but got success")
}
@@ -123,7 +116,6 @@ func TestEnforceRequirements(t *testing.T) {
if err != nil && !strings.Contains(err.Error(), expErr) {
t.Fatalf("enforceRequirements returned unexpected error, expected: %s, got %v", expErr, err)
}
})
}
}
@@ -212,52 +204,3 @@ func TestPrintConfiguration(t *testing.T) {
})
}
}
func TestIsKubeadmConfigPresent(t *testing.T) {
var tcases = []struct {
name string
gvkmap kubeadmapi.DocumentMap
expected bool
}{
{
name: " Wrong Group value",
gvkmap: kubeadmapi.DocumentMap{
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"}: []byte(`kind: Foo`),
},
expected: false,
},
{
name: "Empty Group value",
gvkmap: kubeadmapi.DocumentMap{
{Group: "", Version: "v1", Kind: "Empty"}: []byte(`kind: Empty`),
},
expected: false,
},
{
name: "Nil value",
gvkmap: nil,
expected: false,
},
{
name: "Correct Group value 1",
gvkmap: kubeadmapi.DocumentMap{
{Group: "kubeadm.k8s.io", Version: "v1", Kind: "Empty"}: []byte(`kind: Empty`),
},
expected: true,
},
{
name: "Correct Group value 2",
gvkmap: kubeadmapi.DocumentMap{
{Group: kubeadmapi.GroupName, Version: "v1", Kind: "Empty"}: []byte(`kind: Empty`),
},
expected: true,
},
}
for _, tt := range tcases {
t.Run(tt.name, func(t *testing.T) {
if isKubeadmConfigPresent(tt.gvkmap) != tt.expected {
t.Error("unexpected result")
}
})
}
}

View File

@@ -23,13 +23,15 @@ import (
"github.com/pkg/errors"
"github.com/pmezard/go-difflib/difflib"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/version"
client "k8s.io/client-go/kubernetes"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
@@ -37,6 +39,7 @@ import (
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
)
type diffFlags struct {
@@ -74,7 +77,11 @@ func newCmdDiff(out io.Writer) *cobra.Command {
flags.schedulerManifestPath); err != nil {
return err
}
return runDiff(flags, args)
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
return err
}
return runDiff(cmd.Flags(), flags, args, configutil.FetchInitConfigurationFromCluster)
},
}
@@ -107,50 +114,49 @@ func validateManifestsPath(manifests ...string) (err error) {
return nil
}
func runDiff(flags *diffFlags, args []string) error {
var err error
var cfg *kubeadmapi.InitConfiguration
if flags.cfgPath != "" {
cfg, err = configutil.LoadInitConfigurationFromFile(flags.cfgPath, configutil.LoadOrDefaultConfigurationOptions{
SkipCRIDetect: true,
})
} else {
var client client.Interface
client, err = kubeconfigutil.ClientSetFromFile(flags.kubeConfigPath)
if err != nil {
return errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
}
cfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade/diff", false, false)
// FetchInitConfigurationFunc defines the signature of the function which will fetch InitConfiguration from cluster.
type FetchInitConfigurationFunc func(client clientset.Interface, printer output.Printer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error)
func runDiff(fs *pflag.FlagSet, flags *diffFlags, args []string, fetchInitConfigurationFromCluster FetchInitConfigurationFunc) error {
upgradeCfg, err := configutil.LoadUpgradeConfig(flags.cfgPath)
if err != nil {
return err
}
client, err := kubeconfigutil.ClientSetFromFile(flags.kubeConfigPath)
if err != nil {
return errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
}
initCfg, err := fetchInitConfigurationFromCluster(client, &output.TextPrinter{}, "upgrade/diff", false, true)
if err != nil {
return err
}
// If the version is specified in config file, pick up that value.
if cfg.KubernetesVersion != "" {
flags.newK8sVersionStr = cfg.KubernetesVersion
// Pick up the version from the ClusterConfiguration.
if initCfg.KubernetesVersion != "" {
flags.newK8sVersionStr = initCfg.KubernetesVersion
}
if upgradeCfg.Diff.KubernetesVersion != "" {
flags.newK8sVersionStr = upgradeCfg.Diff.KubernetesVersion
}
// If the new version is already specified in config file, version arg is optional.
// Version must be specified via version arg if it's not set in ClusterConfiguration.
if flags.newK8sVersionStr == "" {
if err := cmdutil.ValidateExactArgNumber(args, []string{"version"}); err != nil {
return err
}
}
// If option was specified in both args and config file, args will overwrite the config file.
if len(args) == 1 {
flags.newK8sVersionStr = args[0]
}
_, err = version.ParseSemantic(flags.newK8sVersionStr)
if err != nil {
return err
}
cfg.ClusterConfiguration.KubernetesVersion = flags.newK8sVersionStr
initCfg.ClusterConfiguration.KubernetesVersion = flags.newK8sVersionStr
specs := controlplane.GetStaticPodSpecs(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, nil)
specs := controlplane.GetStaticPodSpecs(&initCfg.ClusterConfiguration, &initCfg.LocalAPIEndpoint, nil)
for spec, pod := range specs {
var path string
switch spec {
@@ -164,7 +170,6 @@ func runDiff(flags *diffFlags, args []string) error {
klog.Errorf("[diff] unknown spec %v", spec)
continue
}
newManifest, err := kubeadmutil.MarshalToYaml(&pod, corev1.SchemeGroupVersion)
if err != nil {
return err
@@ -183,7 +188,7 @@ func runDiff(flags *diffFlags, args []string) error {
B: difflib.SplitLines(string(newManifest)),
FromFile: path,
ToFile: "new manifest",
Context: flags.contextLines,
Context: cmdutil.ValueFromFlagsOrConfig(fs, "context-lines", upgradeCfg.Diff.DiffContextLines, flags.contextLines).(int),
}
difflib.WriteUnifiedDiff(flags.out, diff)

View File

@@ -24,8 +24,10 @@ import (
"github.com/pkg/errors"
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
)
func createTestRunDiffFile(contents []byte) (string, error) {
@@ -42,23 +44,25 @@ func createTestRunDiffFile(contents []byte) (string, error) {
return file.Name(), nil
}
func TestRunDiff(t *testing.T) {
currentVersion := "v" + constants.CurrentKubernetesVersion.String()
func fakeFetchInitConfig(client clientset.Interface, printer output.Printer, logPrefix string, newControlPlane, skipComponentConfigs bool) (*kubeadmapi.InitConfiguration, error) {
return &kubeadmapi.InitConfiguration{
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
KubernetesVersion: "v1.0.1",
},
}, nil
}
func TestRunDiff(t *testing.T) {
// create a temporary file with valid ClusterConfiguration
testUpgradeDiffConfigContents := []byte(fmt.Sprintf(`
apiVersion: %s
kind: InitConfiguration
---
apiVersion: %[1]s
kind: ClusterConfiguration
kubernetesVersion: %s`, kubeadmapiv1.SchemeGroupVersion.String(), currentVersion))
kind: UpgradeConfiguration
contextLines: 4`, kubeadmapiv1.SchemeGroupVersion.String()))
testUpgradeDiffConfig, err := createTestRunDiffFile(testUpgradeDiffConfigContents)
if err != nil {
t.Fatal(err)
}
defer os.Remove(testUpgradeDiffConfig)
// create a temporary manifest file with dummy contents
testUpgradeDiffManifestContents := []byte("some-contents")
testUpgradeDiffManifest, err := createTestRunDiffFile(testUpgradeDiffManifestContents)
@@ -67,6 +71,13 @@ kubernetesVersion: %s`, kubeadmapiv1.SchemeGroupVersion.String(), currentVersion
}
defer os.Remove(testUpgradeDiffManifest)
kubeConfigPath, err := createTestRunDiffFile([]byte(testConfigToken))
if err != nil {
t.Fatal(err)
}
//nolint:errcheck
defer os.Remove(kubeConfigPath)
flags := &diffFlags{
cfgPath: "",
out: io.Discard,
@@ -118,12 +129,14 @@ kubernetesVersion: %s`, kubeadmapiv1.SchemeGroupVersion.String(), currentVersion
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
flags.cfgPath = tc.cfgPath
flags.kubeConfigPath = kubeConfigPath
cmd := newCmdDiff(os.Stdout)
if tc.setManifestPath {
flags.apiServerManifestPath = tc.manifestPath
flags.controllerManagerManifestPath = tc.manifestPath
flags.schedulerManifestPath = tc.manifestPath
}
if err := runDiff(flags, tc.args); (err != nil) != tc.expectedError {
if err := runDiff(cmd.Flags(), flags, tc.args, fakeFetchInitConfig); (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, saw: %v, error: %v", tc.expectedError, (err != nil), err)
}
})

View File

@@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
phases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade/node"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
)
@@ -40,6 +41,7 @@ import (
// Please note that this structure includes the public kubeadm config API, but only a subset of the options
// supported by this api will be exposed as a flag.
type nodeOptions struct {
cfgPath string
kubeConfigPath string
etcdUpgrade bool
renewCerts bool
@@ -57,7 +59,8 @@ type nodeData struct {
etcdUpgrade bool
renewCerts bool
dryRun bool
cfg *kubeadmapi.InitConfiguration
cfg *kubeadmapi.UpgradeConfiguration
initCfg *kubeadmapi.InitConfiguration
isControlPlaneNode bool
client clientset.Interface
patchesDir string
@@ -75,6 +78,10 @@ func newCmdNode(out io.Writer) *cobra.Command {
Use: "node",
Short: "Upgrade commands for a node in the cluster",
RunE: func(cmd *cobra.Command, args []string) error {
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
return err
}
return nodeRunner.Run(args)
},
Args: cobra.NoArgs,
@@ -83,6 +90,7 @@ func newCmdNode(out io.Writer) *cobra.Command {
// adds flags to the node command
// flags could be eventually inherited by the sub-commands automatically generated for phases
addUpgradeNodeFlags(cmd.Flags(), nodeOptions)
options.AddConfigFlag(cmd.Flags(), &nodeOptions.cfgPath)
options.AddPatchesFlag(cmd.Flags(), &nodeOptions.patchesDir)
// initialize the workflow runner with the list of phases
@@ -93,7 +101,15 @@ func newCmdNode(out io.Writer) *cobra.Command {
// sets the data builder function, that will be used by the runner
// both when running the entire workflow or single phases
nodeRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) {
return newNodeData(cmd, args, nodeOptions, out)
data, err := newNodeData(cmd, args, nodeOptions, out)
if err != nil {
return nil, err
}
// If the flag for skipping phases was empty, use the values from config
if len(nodeRunner.Options.SkipPhases) == 0 {
nodeRunner.Options.SkipPhases = data.cfg.Node.SkipPhases
}
return data, nil
})
// binds the Runner to kubeadm upgrade node command by altering
@@ -124,50 +140,69 @@ func addUpgradeNodeFlags(flagSet *flag.FlagSet, nodeOptions *nodeOptions) {
// newNodeData returns a new nodeData struct to be used for the execution of the kubeadm upgrade node workflow.
// This func takes care of validating nodeOptions passed to the command, and then it converts
// options into the internal InitConfiguration type that is used as input all the phases in the kubeadm upgrade node workflow
func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions, out io.Writer) (*nodeData, error) {
func newNodeData(cmd *cobra.Command, args []string, nodeOptions *nodeOptions, out io.Writer) (*nodeData, error) {
// Checks if a node is a control-plane node by looking up the kube-apiserver manifest file
isControlPlaneNode := true
filepath := constants.GetStaticPodFilepath(constants.KubeAPIServer, constants.GetStaticPodDirectory())
if _, err := os.Stat(filepath); os.IsNotExist(err) {
isControlPlaneNode = false
}
if len(options.kubeConfigPath) == 0 {
if len(nodeOptions.kubeConfigPath) == 0 {
// Update the kubeconfig path depending on whether this is a control plane node or not.
options.kubeConfigPath = constants.GetKubeletKubeConfigPath()
nodeOptions.kubeConfigPath = constants.GetKubeletKubeConfigPath()
if isControlPlaneNode {
options.kubeConfigPath = constants.GetAdminKubeConfigPath()
nodeOptions.kubeConfigPath = constants.GetAdminKubeConfigPath()
}
}
client, err := getClient(options.kubeConfigPath, options.dryRun)
upgradeCfg, err := configutil.LoadUpgradeConfig(nodeOptions.cfgPath)
if err != nil {
return nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", options.kubeConfigPath)
return nil, err
}
dryRun, ok := cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.DryRun, upgradeCfg.Node.DryRun, &nodeOptions.dryRun).(*bool)
if !ok {
return nil, cmdutil.TypeMismatchErr("dryRun", "bool")
}
client, err := getClient(nodeOptions.kubeConfigPath, *dryRun)
if err != nil {
return nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", nodeOptions.kubeConfigPath)
}
// Fetches the cluster configuration
// NB in case of control-plane node, we are reading all the info for the node; in case of NOT control-plane node
// (worker node), we are not reading local API address and the CRI socket from the node object
cfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", !isControlPlaneNode, false)
initCfg, err := configutil.FetchInitConfigurationFromCluster(client, nil, "upgrade", !isControlPlaneNode, false)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
}
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors, cfg.NodeRegistration.IgnorePreflightErrors)
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(nodeOptions.ignorePreflightErrors, initCfg.NodeRegistration.IgnorePreflightErrors)
if err != nil {
return nil, err
}
// Also set the union of pre-flight errors to JoinConfiguration, to provide a consistent view of the runtime configuration:
cfg.NodeRegistration.IgnorePreflightErrors = sets.List(ignorePreflightErrorsSet)
initCfg.NodeRegistration.IgnorePreflightErrors = sets.List(ignorePreflightErrorsSet)
var patchesDir string
if upgradeCfg.Node.Patches != nil {
patchesDir = cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.Patches, upgradeCfg.Node.Patches.Directory, nodeOptions.patchesDir).(string)
} else {
patchesDir = nodeOptions.patchesDir
}
return &nodeData{
etcdUpgrade: options.etcdUpgrade,
renewCerts: options.renewCerts,
dryRun: options.dryRun,
cfg: cfg,
cfg: upgradeCfg,
dryRun: *dryRun,
initCfg: initCfg,
client: client,
isControlPlaneNode: isControlPlaneNode,
patchesDir: options.patchesDir,
ignorePreflightErrors: ignorePreflightErrorsSet,
kubeConfigPath: options.kubeConfigPath,
kubeConfigPath: nodeOptions.kubeConfigPath,
outputWriter: out,
patchesDir: patchesDir,
etcdUpgrade: *cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.EtcdUpgrade, upgradeCfg.Node.EtcdUpgrade, &nodeOptions.etcdUpgrade).(*bool),
renewCerts: *cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.CertificateRenewal, upgradeCfg.Node.CertificateRenewal, &nodeOptions.renewCerts).(*bool),
}, nil
}
@@ -186,11 +221,16 @@ func (d *nodeData) RenewCerts() bool {
return d.renewCerts
}
// Cfg returns initConfiguration.
func (d *nodeData) Cfg() *kubeadmapi.InitConfiguration {
// Cfg returns upgradeConfiguration.
func (d *nodeData) Cfg() *kubeadmapi.UpgradeConfiguration {
return d.cfg
}
// InitCfg returns the InitConfiguration.
func (d *nodeData) InitCfg() *kubeadmapi.InitConfiguration {
return d.initCfg
}
// IsControlPlaneNode returns the isControlPlaneNode flag.
func (d *nodeData) IsControlPlaneNode() bool {
return d.isControlPlaneNode

View File

@@ -27,14 +27,17 @@ import (
"github.com/lithammer/dedent"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/klog/v2"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
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/cmd/options"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
@@ -64,13 +67,16 @@ func newCmdPlan(apf *applyPlanFlags) *cobra.Command {
Use: "plan [version] [flags]",
Short: "Check which versions are available to upgrade to and validate whether your current cluster is upgradeable.",
Long: upgradePlanLongDesc,
RunE: func(_ *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, args []string) error {
printer, err := outputFlags.ToPrinter()
if err != nil {
return errors.Wrap(err, "could not construct output printer")
}
return runPlan(flags, args, printer)
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
return err
}
return runPlan(cmd.Flags(), flags, args, printer)
},
}
@@ -91,29 +97,41 @@ func newComponentUpgradePlan(name, currentVersion, newVersion string) outputapiv
}
// runPlan takes care of outputting available versions to upgrade to for the user
func runPlan(flags *planFlags, args []string, printer output.Printer) error {
func runPlan(flagSet *pflag.FlagSet, 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.
klog.V(1).Infoln("[upgrade/plan] verifying health of cluster")
klog.V(1).Infoln("[upgrade/plan] retrieving configuration from cluster")
client, versionGetter, cfg, err := enforceRequirements(flags.applyPlanFlags, args, false, false, printer, loadConfig)
client, versionGetter, initCfg, upgradeCfg, err := enforceRequirements(flagSet, flags.applyPlanFlags, args, false, false, printer)
if err != nil {
return err
}
// Currently this is the only method we have for distinguishing
// external etcd vs static pod etcd
isExternalEtcd := cfg.Etcd.External != nil
isExternalEtcd := initCfg.Etcd.External != nil
// Compute which upgrade possibilities there are
klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities")
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, isExternalEtcd, client, constants.GetStaticPodDirectory(), printer)
// flags are respected while keeping the configuration file not changed.
allowRCUpgrades, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.AllowRCUpgrades, upgradeCfg.Plan.AllowRCUpgrades, &flags.allowRCUpgrades).(*bool)
if !ok {
return cmdutil.TypeMismatchErr("allowRCUpgrades", "bool")
}
allowExperimentalUpgrades, ok := cmdutil.ValueFromFlagsOrConfig(flagSet, options.AllowExperimentalUpgrades, upgradeCfg.Plan.AllowExperimentalUpgrades, &flags.allowExperimentalUpgrades).(*bool)
if !ok {
return cmdutil.TypeMismatchErr("allowExperimentalUpgrades", "bool")
}
availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, *allowExperimentalUpgrades, *allowRCUpgrades, isExternalEtcd, client, constants.GetStaticPodDirectory(), printer)
if err != nil {
return errors.Wrap(err, "[upgrade/versions] FATAL")
}
// Fetch the current state of the component configs
klog.V(1).Infoln("[upgrade/plan] analysing component config version states")
configVersionStates, err := componentconfigs.GetVersionStates(&cfg.ClusterConfiguration, client)
configVersionStates, err := componentconfigs.GetVersionStates(&initCfg.ClusterConfiguration, client)
if err != nil {
return errors.WithMessage(err, "[upgrade/versions] FATAL")
}

View File

@@ -30,6 +30,7 @@ import (
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"
"k8s.io/utils/ptr"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
@@ -135,6 +136,18 @@ func ValueFromFlagsOrConfig(flagSet *pflag.FlagSet, name string, cfgValue interf
if flagSet.Changed(name) {
return flagValue
}
// covert the nil to false if this is a bool, this will help to get rid of nil dereference error.
cfg, ok := cfgValue.(*bool)
if ok && cfg == nil {
return ptr.To(false)
}
// assume config has all the defaults set correctly.
return cfgValue
}
// TypeMismatchErr return an error which indicates how the type is mismatched.
func TypeMismatchErr(opt, rType string) error {
return errors.Errorf("type mismatch, %s is expected to be a pointer to %s", opt, rType)
}

View File

@@ -128,6 +128,12 @@ func TestValueFromFlagsOrConfig(t *testing.T) {
flagValue: false,
expected: false,
},
{
name: "nil bool is converted to false",
cfg: (*bool)(nil),
flagValue: false,
expected: false,
},
}
for _, tt := range tests {
type options struct {
@@ -140,11 +146,15 @@ func TestValueFromFlagsOrConfig(t *testing.T) {
fs.BoolVar(&fakeOptions.bar, "bar", false, "")
t.Run(tt.name, func(t *testing.T) {
err := fs.Set(tt.flag, fmt.Sprintf("%v", tt.flagValue))
if err != nil {
t.Fatalf("failed to set the value of the flag %v", tt.flagValue)
if tt.flag != "" {
if err := fs.Set(tt.flag, fmt.Sprintf("%v", tt.flagValue)); err != nil {
t.Fatalf("failed to set the value of the flag %v", tt.flagValue)
}
}
actualResult := ValueFromFlagsOrConfig(&fs, tt.flag, tt.cfg, tt.flagValue)
if result, ok := actualResult.(*bool); ok {
actualResult = *result
}
if actualResult != tt.expected {
t.Errorf(
"failed ValueFromFlagsOrConfig:\n\texpected: %s\n\t actual: %s",

View File

@@ -452,6 +452,8 @@ const (
EtcdUserName string = "kubeadm-etcd"
// ServiceAccountKeyReadersGroupName is the group of users that are allowed to read the service account private key.
ServiceAccountKeyReadersGroupName string = "kubeadm-sa-key-readers"
// UpgradeConfigurationKind is the string kind value for the UpgradeConfiguration struct
UpgradeConfigurationKind = "UpgradeConfiguration"
)
var (

View File

@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package config contains utilities for managing the kubeadm configuration API.
package config
import (
@@ -490,3 +491,13 @@ func defaultEmptyMigrateMutators() migrateMutators {
return *mutators
}
// isKubeadmConfigPresent checks if a kubeadm config type is found in the provided document map
func isKubeadmConfigPresent(docmap kubeadmapi.DocumentMap) bool {
for gvk := range docmap {
if gvk.Group == kubeadmapi.GroupName {
return true
}
}
return false
}

View File

@@ -858,3 +858,52 @@ func TestDefaultMigrateMutators(t *testing.T) {
})
}
}
func TestIsKubeadmConfigPresent(t *testing.T) {
var tcases = []struct {
name string
gvkmap kubeadmapi.DocumentMap
expected bool
}{
{
name: " Wrong Group value",
gvkmap: kubeadmapi.DocumentMap{
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"}: []byte(`kind: Foo`),
},
expected: false,
},
{
name: "Empty Group value",
gvkmap: kubeadmapi.DocumentMap{
{Group: "", Version: "v1", Kind: "Empty"}: []byte(`kind: Empty`),
},
expected: false,
},
{
name: "Nil value",
gvkmap: nil,
expected: false,
},
{
name: "Correct Group value 1",
gvkmap: kubeadmapi.DocumentMap{
{Group: "kubeadm.k8s.io", Version: "v1", Kind: "Empty"}: []byte(`kind: Empty`),
},
expected: true,
},
{
name: "Correct Group value 2",
gvkmap: kubeadmapi.DocumentMap{
{Group: kubeadmapi.GroupName, Version: "v1", Kind: "Empty"}: []byte(`kind: Empty`),
},
expected: true,
},
}
for _, tt := range tcases {
t.Run(tt.name, func(t *testing.T) {
if isKubeadmConfigPresent(tt.gvkmap) != tt.expected {
t.Error("unexpected result")
}
})
}
}

View File

@@ -0,0 +1,158 @@
/*
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 config
import (
"os"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
kubeproxyconfig "k8s.io/kube-proxy/config/v1alpha1"
kubeletconfig "k8s.io/kubelet/config/v1beta1"
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/v1beta4"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
)
var componentCfgGV = sets.New(kubeproxyconfig.GroupName, kubeletconfig.GroupName)
// DefaultUpgradeConfiguration return a default UpgradeConfiguration
func DefaultUpgradeConfiguration() (*kubeadmapi.UpgradeConfiguration, error) {
versionedCfg := &kubeadmapiv1.UpgradeConfiguration{}
kubeadmscheme.Scheme.Default(versionedCfg)
cfg := &kubeadmapi.UpgradeConfiguration{}
if err := kubeadmscheme.Scheme.Convert(versionedCfg, cfg, nil); err != nil {
return nil, errors.Wrap(err, "could not prepare a defaulted UpgradeConfiguration")
}
return cfg, nil
}
// documentMapToUpgradeConfiguration takes a map between GVKs and YAML documents (as returned by SplitYAMLDocuments),
// finds a UpgradeConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated bool) (*kubeadmapi.UpgradeConfiguration, error) {
var internalcfg *kubeadmapi.UpgradeConfiguration
for gvk, bytes := range gvkmap {
// check if this version is supported and possibly not deprecated
if err := validateSupportedVersion(gvk.GroupVersion(), allowDeprecated, true); err != nil {
return nil, err
}
// verify the validity of the YAML
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
klog.Warning(err.Error())
}
if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvk) || kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvk) {
klog.Warningf("[config] WARNING: YAML document with GroupVersionKind %v is deprecated for upgrade, please use config file with kind of UpgradeConfiguration instead \n", gvk)
continue
}
if kubeadmutil.GroupVersionKindsHasUpgradeConfiguration(gvk) {
// Set internalcfg to an empty struct value the deserializer will populate
internalcfg = &kubeadmapi.UpgradeConfiguration{}
// Decode the bytes into the internal struct. Under the hood, the bytes will be unmarshalled into the
// right external version, defaulted, and converted into the internal version.
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), bytes, internalcfg); err != nil {
return nil, err
}
continue
}
// If the group is neither a kubeadm core type or of a supported component config group, we dump a warning about it being ignored
if !componentconfigs.Scheme.IsGroupRegistered(gvk.Group) {
klog.Warningf("[config] WARNING: Ignored YAML document with GroupVersionKind %v\n", gvk)
}
}
// If UpgradeConfiguration wasn't given, default it by creating an external struct instance, default it and convert into the internal type
if internalcfg == nil {
extinitcfg := &kubeadmapiv1.UpgradeConfiguration{}
kubeadmscheme.Scheme.Default(extinitcfg)
// Set upgradeCfg to an empty struct value the deserializer will populate
internalcfg = &kubeadmapi.UpgradeConfiguration{}
if err := kubeadmscheme.Scheme.Convert(extinitcfg, internalcfg, nil); err != nil {
return nil, err
}
}
// Validates cfg
if err := validation.ValidateUpgradeConfiguration(internalcfg).ToAggregate(); err != nil {
return nil, err
}
return internalcfg, nil
}
// DocMapToUpgradeConfiguration converts documentMap to an internal, defaulted and validated UpgradeConfiguration object.
// The map may contain many different YAML documents. These YAML documents are parsed one-by-one
// and well-known ComponentConfig GroupVersionKinds are stored inside of the internal UpgradeConfiguration struct.
// The resulting UpgradeConfiguration is then dynamically defaulted and validated prior to return.
func DocMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap) (*kubeadmapi.UpgradeConfiguration, error) {
return documentMapToUpgradeConfiguration(gvkmap, false)
}
// LoadUpgradeConfig loads UpgradeConfiguration from a file.
func LoadUpgradeConfig(cfgPath string) (*kubeadmapi.UpgradeConfiguration, error) {
var err error
var upgradeCfg *kubeadmapi.UpgradeConfiguration
if cfgPath == "" {
if upgradeCfg, err = DefaultUpgradeConfiguration(); err != nil {
return nil, err
}
return upgradeCfg, nil
}
// Otherwise, we have a config file. Let's load it.
configBytes, err := os.ReadFile(cfgPath)
if err != nil {
return nil, errors.Wrapf(err, "unable to load config from file %q", cfgPath)
}
// Split the YAML documents in the file into a DocumentMap
docmap, err := kubeadmutil.SplitYAMLDocuments(configBytes)
if err != nil {
return nil, err
}
// Convert documentMap to internal UpgradeConfiguration, InitConfiguration and ClusterConfiguration from config file will be ignored.
// Upgrade should respect the cluster configuration from the existing cluster, re-configure the cluster with a InitConfiguration and
// ClusterConfiguration from the config file is not allowed for upgrade.
if isKubeadmConfigPresent(docmap) {
if upgradeCfg, err = DocMapToUpgradeConfiguration(docmap); err != nil {
return nil, err
}
}
// Check is there any component configs defined in the config file.
for gvk := range docmap {
if componentCfgGV.Has(gvk.Group) {
klog.Warningf("[config] WARNING: YAML document with Component Configs %v is deprecated for upgrade and will be ignored \n", gvk.Group)
continue
}
}
return upgradeCfg, nil
}

View File

@@ -0,0 +1,104 @@
/*
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 config
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/yaml"
"github.com/google/go-cmp/cmp"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
func TestDocMapToUpgradeConfiguration(t *testing.T) {
tests := []struct {
name string
cfg kubeadmapiv1.UpgradeConfiguration
expectedCfg kubeadmapi.UpgradeConfiguration
}{
{
name: "default config is set correctly",
cfg: kubeadmapiv1.UpgradeConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
Kind: constants.UpgradeConfigurationKind,
},
},
expectedCfg: kubeadmapi.UpgradeConfiguration{
Apply: kubeadmapi.UpgradeApplyConfiguration{
CertificateRenewal: ptr.To(true),
EtcdUpgrade: ptr.To(true),
},
Node: kubeadmapi.UpgradeNodeConfiguration{
CertificateRenewal: ptr.To(true),
EtcdUpgrade: ptr.To(true),
},
},
},
{
name: "cfg has part of fields configured",
cfg: kubeadmapiv1.UpgradeConfiguration{
Apply: kubeadmapiv1.UpgradeApplyConfiguration{
CertificateRenewal: ptr.To(false),
},
Node: kubeadmapiv1.UpgradeNodeConfiguration{
EtcdUpgrade: ptr.To(false),
},
TypeMeta: metav1.TypeMeta{
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
Kind: constants.UpgradeConfigurationKind,
},
},
expectedCfg: kubeadmapi.UpgradeConfiguration{
Apply: kubeadmapi.UpgradeApplyConfiguration{
CertificateRenewal: ptr.To(false),
EtcdUpgrade: ptr.To(true),
},
Node: kubeadmapi.UpgradeNodeConfiguration{
CertificateRenewal: ptr.To(true),
EtcdUpgrade: ptr.To(false),
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
b, err := yaml.Marshal(tc.cfg)
if err != nil {
t.Fatalf("unexpected error while marshalling to YAML: %v", err)
}
docmap, err := kubeadmutil.SplitYAMLDocuments(b)
if err != nil {
t.Fatalf("Unexpect error of SplitYAMLDocuments: %v", err)
}
cfg, err := DocMapToUpgradeConfiguration(docmap)
if err != nil {
t.Fatalf("unexpected error of DocMapToUpgradeConfiguration: %v\nconfig: %s", err, string(b))
}
if diff := cmp.Diff(*cfg, tc.expectedCfg); diff != "" {
t.Fatalf("DocMapToUpgradeConfiguration returned unexpected diff (-want,+got):\n%s", diff)
}
})
}
}

View File

@@ -151,3 +151,8 @@ func GroupVersionKindsHasJoinConfiguration(gvks ...schema.GroupVersionKind) bool
func GroupVersionKindsHasResetConfiguration(gvks ...schema.GroupVersionKind) bool {
return GroupVersionKindsHasKind(gvks, constants.ResetConfigurationKind)
}
// GroupVersionKindsHasUpgradeConfiguration returns whether the following gvk slice contains a UpgradeConfiguration object
func GroupVersionKindsHasUpgradeConfiguration(gvks ...schema.GroupVersionKind) bool {
return GroupVersionKindsHasKind(gvks, constants.UpgradeConfigurationKind)
}

View File

@@ -431,3 +431,69 @@ func TestGroupVersionKindsHasResetConfiguration(t *testing.T) {
})
}
}
func TestGroupVersionKindsHasClusterConfiguration(t *testing.T) {
tests := []struct {
name string
gvks []schema.GroupVersionKind
expected bool
}{
{
name: "does not have ClusterConfiguraiton",
gvks: []schema.GroupVersionKind{
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
},
expected: false,
},
{
name: "has ClusterConfiguraiton",
gvks: []schema.GroupVersionKind{
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
{Group: "foo.k8s.io", Version: "v1", Kind: "ClusterConfiguration"},
},
expected: true,
},
}
for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) {
actual := GroupVersionKindsHasClusterConfiguration(rt.gvks...)
if rt.expected != actual {
t.Errorf("expected gvks to have a ClusterConfiguration: %t\n\tactual: %t\n", rt.expected, actual)
}
})
}
}
func TestGroupVersionKindsHasUpgradeConfiguration(t *testing.T) {
var tests = []struct {
name string
gvks []schema.GroupVersionKind
kind string
expected bool
}{
{
name: "no UpgradeConfiguration found",
gvks: []schema.GroupVersionKind{
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
},
expected: false,
},
{
name: "UpgradeConfiguration is found",
gvks: []schema.GroupVersionKind{
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
{Group: "bar.k8s.io", Version: "v2", Kind: "UpgradeConfiguration"},
},
expected: true,
},
}
for _, rt := range tests {
t.Run(rt.name, func(t2 *testing.T) {
actual := GroupVersionKindsHasUpgradeConfiguration(rt.gvks...)
if rt.expected != actual {
t2.Errorf("expected gvks has UpgradeConfiguration: %t\n\tactual: %t\n", rt.expected, actual)
}
})
}
}