From a2f8d31c65417da551ff4a6ec89baa3b8d54496b Mon Sep 17 00:00:00 2001 From: SataQiu Date: Thu, 15 Aug 2024 18:05:38 +0800 Subject: [PATCH] kubeadm: improve some grammar issues and add some unit test cases --- .../app/cmd/phases/upgrade/apply/addons.go | 58 +------- .../phases/upgrade/apply/bootstraptoken.go | 9 +- .../cmd/phases/upgrade/apply/controlplane.go | 2 +- .../cmd/phases/upgrade/apply/kubeconfig.go | 4 +- .../cmd/phases/upgrade/apply/kubeletconfig.go | 9 +- .../app/cmd/phases/upgrade/apply/preflight.go | 25 ++-- .../cmd/phases/upgrade/apply/uploadconfig.go | 12 +- cmd/kubeadm/app/cmd/upgrade/apply.go | 35 +++-- cmd/kubeadm/app/cmd/upgrade/apply_test.go | 133 ++++++++++++++++++ 9 files changed, 185 insertions(+), 102 deletions(-) diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/addons.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/addons.go index 3159b5cb287..e51f6700863 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/addons.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/addons.go @@ -18,32 +18,27 @@ limitations under the License. package apply import ( - "context" "fmt" "io" "github.com/pkg/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 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/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" - kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy" "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" ) -// NewAddonPhase returns the addon Cobra command +// NewAddonPhase returns a new addon phase. func NewAddonPhase() workflow.Phase { return workflow.Phase{ Name: "addon", - Short: "Install required addons for passing conformance tests", + Short: "Install the default kubeadm addons", Long: cmdutil.MacroCommandLongDescription, Phases: []workflow.Phase{ { @@ -54,13 +49,13 @@ func NewAddonPhase() workflow.Phase { }, { Name: "coredns", - Short: "Install the CoreDNS addon to a Kubernetes cluster", + Short: "Install the CoreDNS addon", InheritFlags: getAddonPhaseFlags("coredns"), Run: runCoreDNSAddon, }, { Name: "kube-proxy", - Short: "Install the kube-proxy addon to a Kubernetes cluster", + Short: "Install the kube-proxy addon", InheritFlags: getAddonPhaseFlags("kube-proxy"), Run: runKubeProxyAddon, }, @@ -74,10 +69,9 @@ func shouldUpgradeAddons(client clientset.Interface, cfg *kubeadmapi.InitConfigu return false, errors.Wrapf(err, "failed to determine whether all the control plane instances have been upgraded") } if len(unupgradedControlPlanes) > 0 { - fmt.Fprintf(out, "[upgrade/addons] skip upgrade addons because control plane instances %v have not been upgraded\n", unupgradedControlPlanes) + fmt.Fprintf(out, "[upgrade/addons] Skipping upgrade of addons because control plane instances %v have not been upgraded\n", unupgradedControlPlanes) return false, nil } - return true, nil } @@ -89,7 +83,7 @@ func getInitData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.I return data.InitCfg(), data.Client(), data.PatchesDir(), data.OutputWriter(), data.DryRun(), nil } -// runCoreDNSAddon installs CoreDNS addon to a Kubernetes cluster +// runCoreDNSAddon installs the CoreDNS addon. func runCoreDNSAddon(c workflow.RunData) error { cfg, client, patchesDir, out, dryRun, err := getInitData(c) if err != nil { @@ -104,25 +98,6 @@ func runCoreDNSAddon(c workflow.RunData) error { return nil } - // If the coredns ConfigMap is missing, show a warning and assume that the - // DNS addon was skipped during "kubeadm init", and that its redeployment on upgrade is not desired. - // - // TODO: remove this once "kubeadm upgrade apply" phases are supported: - // https://github.com/kubernetes/kubeadm/issues/1318 - if _, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get( - context.TODO(), - kubeadmconstants.CoreDNSConfigMap, - metav1.GetOptions{}, - ); err != nil && apierrors.IsNotFound(err) { - klog.Warningf("the ConfigMaps %q in the namespace %q were not found. "+ - "Assuming that a DNS server was not deployed for this cluster. "+ - "Note that once 'kubeadm upgrade apply' supports phases you "+ - "will have to skip the DNS upgrade manually", - kubeadmconstants.CoreDNSConfigMap, - metav1.NamespaceSystem) - return nil - } - // Upgrade CoreDNS if err := dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, dryRun); err != nil { return err @@ -131,7 +106,7 @@ func runCoreDNSAddon(c workflow.RunData) error { return nil } -// runKubeProxyAddon installs KubeProxy addon to a Kubernetes cluster +// runKubeProxyAddon installs the KubeProxy addon. func runKubeProxyAddon(c workflow.RunData) error { cfg, client, _, out, dryRun, err := getInitData(c) if err != nil { @@ -146,25 +121,6 @@ func runKubeProxyAddon(c workflow.RunData) error { return nil } - // If the kube-proxy ConfigMap is missing, show a warning and assume that kube-proxy - // was skipped during "kubeadm init", and that its redeployment on upgrade is not desired. - // - // TODO: remove this once "kubeadm upgrade apply" phases are supported: - // https://github.com/kubernetes/kubeadm/issues/1318 - if _, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get( - context.TODO(), - kubeadmconstants.KubeProxyConfigMap, - metav1.GetOptions{}, - ); err != nil && apierrors.IsNotFound(err) { - klog.Warningf("the ConfigMap %q in the namespace %q was not found. "+ - "Assuming that kube-proxy was not deployed for this cluster. "+ - "Note that once 'kubeadm upgrade apply' supports phases you "+ - "will have to skip the kube-proxy upgrade manually", - kubeadmconstants.KubeProxyConfigMap, - metav1.NamespaceSystem) - return nil - } - // Upgrade kube-proxy if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, dryRun); err != nil { return err diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/bootstraptoken.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/bootstraptoken.go index 950e0895dd2..953bfabf89c 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/bootstraptoken.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/bootstraptoken.go @@ -30,12 +30,11 @@ import ( nodebootstraptoken "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" ) -// NewBootstrapTokenPhase returns the phase to bootstrapToken +// NewBootstrapTokenPhase returns a new bootstrap-token phase. func NewBootstrapTokenPhase() workflow.Phase { return workflow.Phase{ - Name: "bootstrap-token", - Aliases: []string{"bootstraptoken"}, - Short: "Generates bootstrap tokens used to join a node to a cluster", + Name: "bootstrap-token", + Short: "Configures bootstrap token and cluster-info RBAC rules", InheritFlags: []string{ options.CfgPath, options.KubeconfigPath, @@ -56,7 +55,7 @@ func runBootstrapToken(c workflow.RunData) error { return nil } - fmt.Println("[bootstrap-token] Configuring cluster-info ConfigMap, RBAC Roles") + fmt.Println("[bootstrap-token] Configuring the cluster-info ConfigMap and RBAC roles") client := data.Client() diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/controlplane.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/controlplane.go index 137a35b08af..36f9721d356 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/controlplane.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/controlplane.go @@ -29,7 +29,7 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" ) -// NewControlPlanePhase creates a kubeadm workflow phase that implements handling of control-plane upgrade. +// NewControlPlanePhase returns a new control-plane phase. func NewControlPlanePhase() workflow.Phase { phase := workflow.Phase{ Name: "control-plane", diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeconfig.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeconfig.go index 4c9420fac88..6a931e61ccf 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeconfig.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeconfig.go @@ -28,7 +28,7 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" ) -// NewKubeconfigPhase creates a kubeadm workflow phase that implements handling of kubeconfig upgrade. +// NewKubeconfigPhase returns a new kubeconfig phase. func NewKubeconfigPhase() workflow.Phase { phase := workflow.Phase{ Name: "kubeconfig", @@ -59,7 +59,7 @@ func runKubeconfig() func(c workflow.RunData) error { } } - fmt.Println("[upgrade] The kubeconfig for this node was successfully updated!") + fmt.Println("[upgrade] The kubeconfig files for this node were successfully updated!") return nil } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeletconfig.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeletconfig.go index a97596b98ea..21f058d52d7 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeletconfig.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/kubeletconfig.go @@ -34,7 +34,7 @@ var ( `) ) -// NewKubeletConfigPhase creates a kubeadm workflow phase that implements handling of kubelet-config upgrade. +// NewKubeletConfigPhase returns a new kubelet-config phase. func NewKubeletConfigPhase() workflow.Phase { phase := workflow.Phase{ Name: "kubelet-config", @@ -59,15 +59,14 @@ func runKubeletConfigPhase(c workflow.RunData) error { initCfg, dryRun := data.InitCfg(), data.DryRun() - // Write the configuration for the kubelet down to disk and print the generated manifests instead if dry-running. + // Write the configuration for the kubelet down to disk and print the generated manifests instead of dry-running. // If not dry-running, the kubelet config file will be backed up to /etc/kubernetes/tmp/ dir, so that it could be - // recovered if there is anything goes wrong. + // recovered if anything goes wrong. err := upgrade.WriteKubeletConfigFiles(initCfg, data.PatchesDir(), dryRun, data.OutputWriter()) if err != nil { return err } - fmt.Println("[upgrade] The configuration for this node was successfully updated!") - fmt.Println("[upgrade] Now you should go ahead and upgrade the kubelet package using your package manager.") + fmt.Println("[upgrade] The kubelet configuration for this node was successfully updated!") return nil } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go index bec44f66bd3..22775635102 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go @@ -38,12 +38,12 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) -// NewPreflightPhase creates a kubeadm workflow phase that implements preflight checks for kubeadm upgrade apply. +// NewPreflightPhase returns a new prefight phase. func NewPreflightPhase() workflow.Phase { return workflow.Phase{ Name: "preflight", - Short: "Run upgrade apply pre-flight checks", - Long: "Run pre-flight checks for kubeadm upgrade apply.", + Short: "Run upgrade apply preflight checks", + Long: "Run preflight checks for kubeadm upgrade apply.", Run: runPreflight, InheritFlags: []string{ options.CfgPath, @@ -58,13 +58,12 @@ func NewPreflightPhase() workflow.Phase { } } -// runPreflight executes preflight checks logic. func runPreflight(c workflow.RunData) error { data, ok := c.(Data) if !ok { return errors.New("preflight phase invoked with an invalid data struct") } - fmt.Println("[preflight] Running pre-flight checks") + fmt.Println("[preflight] Running preflight checks") printer := &output.TextPrinter{} @@ -81,7 +80,7 @@ func runPreflight(c workflow.RunData) error { } // Run healthchecks against the cluster - klog.V(1).Infoln("[upgrade/apply] verifying health of cluster") + klog.V(1).Infoln("[upgrade/apply] Verifying the cluster health") if err := upgrade.CheckClusterHealth(client, &initCfg.ClusterConfiguration, ignorePreflightErrors, printer); err != nil { return err } @@ -94,7 +93,7 @@ func runPreflight(c workflow.RunData) error { } // Validate requested and validate actual version - klog.V(1).Infoln("[upgrade/apply] validating requested and actual version") + klog.V(1).Infoln("[upgrade/apply] Validating requested and actual version") if err := configutil.NormalizeKubernetesVersion(&initCfg.ClusterConfiguration); err != nil { return err } @@ -110,7 +109,7 @@ func runPreflight(c workflow.RunData) error { } versionGetter := upgrade.NewOfflineVersionGetter(upgrade.NewKubeVersionGetter(client), initCfg.KubernetesVersion) - if err := EnforceVersionPolicies(initCfg.KubernetesVersion, upgradeVersion, data.AllowExperimentalUpgrades(), data.AllowRCUpgrades(), data.ForceUpgrade(), versionGetter); err != nil { + if err := enforceVersionPolicies(initCfg.KubernetesVersion, upgradeVersion, data.AllowExperimentalUpgrades(), data.AllowRCUpgrades(), data.ForceUpgrade(), versionGetter); err != nil { return err } @@ -134,23 +133,23 @@ func runPreflight(c workflow.RunData) error { return nil } -// EnforceVersionPolicies makes sure that the version the user specified is valid to upgrade to +// enforceVersionPolicies makes sure that the version the user specified is valid to upgrade to // There are both fatal and skippable (with --force) errors -func EnforceVersionPolicies(newK8sVersionStr string, newK8sVersion *version.Version, allowExperimentalUpgrades, allowRCUpgrades, force bool, versionGetter upgrade.VersionGetter) error { - fmt.Printf("[upgrade/version] You have chosen to change the cluster version to %q\n", newK8sVersionStr) +func enforceVersionPolicies(newK8sVersionStr string, newK8sVersion *version.Version, allowExperimentalUpgrades, allowRCUpgrades, force bool, versionGetter upgrade.VersionGetter) error { + fmt.Printf("[upgrade/version] You have chosen to upgrade the cluster version to %q\n", newK8sVersionStr) versionSkewErrs := upgrade.EnforceVersionPolicies(versionGetter, newK8sVersionStr, newK8sVersion, allowExperimentalUpgrades, allowRCUpgrades) if versionSkewErrs != nil { if len(versionSkewErrs.Mandatory) > 0 { - return errors.Errorf("the --version argument is invalid due to these fatal errors:\n\n%v\nPlease fix the misalignments highlighted above and try upgrading again", + return errors.Errorf("the version argument is invalid due to these fatal errors:\n\n%v\nPlease fix the misalignments highlighted above and try upgrading again", kubeadmutil.FormatErrMsg(versionSkewErrs.Mandatory)) } if len(versionSkewErrs.Skippable) > 0 { // Return the error if the user hasn't specified the --force flag if !force { - return errors.Errorf("the --version argument is invalid due to these errors:\n\n%v\nCan be bypassed if you pass the --force flag", + return errors.Errorf("the version argument is invalid due to these errors:\n\n%v\nCan be bypassed if you pass the --force flag", kubeadmutil.FormatErrMsg(versionSkewErrs.Skippable)) } // Soft errors found, but --force was specified diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/uploadconfig.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/uploadconfig.go index 74cbdcbe15d..912b4731d0e 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/uploadconfig.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/uploadconfig.go @@ -39,12 +39,12 @@ func NewUploadConfigPhase() workflow.Phase { return workflow.Phase{ Name: "upload-config", Aliases: []string{"uploadconfig"}, - Short: "Upload the kubeadm and kubelet configuration to a ConfigMap", + Short: "Upload the kubeadm and kubelet configurations to a ConfigMaps", Long: cmdutil.MacroCommandLongDescription, Phases: []workflow.Phase{ { Name: "all", - Short: "Upload all configuration to a config map", + Short: "Upload all the configurations to ConfigMaps", RunAllSiblings: true, InheritFlags: getUploadConfigPhaseFlags(), }, @@ -56,7 +56,7 @@ func NewUploadConfigPhase() workflow.Phase { }, { Name: "kubelet", - Short: "Upload the kubelet component config to a ConfigMap", + Short: "Upload the kubelet configuration config to a ConfigMap", Run: runUploadKubeletConfig, InheritFlags: getUploadConfigPhaseFlags(), }, @@ -99,19 +99,19 @@ func runUploadKubeletConfig(c workflow.RunData) error { } if dryRun { - fmt.Println("[dryrun] Would upload the kubelet component config to a ConfigMap") + fmt.Println("[dryrun] Would upload the kubelet configuration to a ConfigMap") fmt.Println("[dryrun] Would write the CRISocket annotation for the control-plane node") return nil } - klog.V(1).Infoln("[upload-config] Uploading the kubelet component config to a ConfigMap") + klog.V(1).Infoln("[upload-config] Uploading the kubelet configuration to a ConfigMap") if err = kubeletphase.CreateConfigMap(&cfg.ClusterConfiguration, client); err != nil { return errors.Wrap(err, "error creating kubelet configuration ConfigMap") } klog.V(1).Infoln("[upload-config] Preserving the CRISocket information for the control-plane node") if err := patchnodephase.AnnotateCRISocket(client, cfg.NodeRegistration.Name, cfg.NodeRegistration.CRISocket); err != nil { - return errors.Wrap(err, "Error writing Crisocket information for the control-plane node") + return errors.Wrap(err, "error writing Crisocket information for the control-plane node") } return nil diff --git a/cmd/kubeadm/app/cmd/upgrade/apply.go b/cmd/kubeadm/app/cmd/upgrade/apply.go index 6e7a60ec034..4cfa5f86c9c 100644 --- a/cmd/kubeadm/app/cmd/upgrade/apply.go +++ b/cmd/kubeadm/app/cmd/upgrade/apply.go @@ -99,25 +99,21 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command { if err != nil { return err } - applyData, ok := data.(*applyData) if !ok { return errors.New("invalid data struct") } - if err := applyRunner.Run(args); err != nil { return err } - if flags.dryRun { fmt.Println("[upgrade/successful] Finished dryrunning successfully!") return nil } fmt.Println("") - fmt.Printf("[upgrade/successful] SUCCESS! Your cluster was upgraded to %q. Enjoy!\n", applyData.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.") + fmt.Printf("[upgrade/successful] SUCCESS! A control plane node of your cluster was upgraded to %q.\n\n", applyData.InitCfg().KubernetesVersion) + fmt.Println("[upgrade/kubelet] Now please proceed with upgrading the rest of the nodes by following the right order.") return nil }, @@ -133,7 +129,7 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command { 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) - // initialize the workflow runner with the list of phases + // Initialize the workflow runner with the list of phases applyRunner.AppendPhase(phases.NewPreflightPhase()) applyRunner.AppendPhase(phases.NewControlPlanePhase()) applyRunner.AppendPhase(phases.NewUploadConfigPhase()) @@ -142,7 +138,7 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command { applyRunner.AppendPhase(phases.NewBootstrapTokenPhase()) applyRunner.AppendPhase(phases.NewAddonPhase()) - // sets the data builder function, that will be used by the runner + // Sets the data builder function, that will be used by the runner // both when running the entire workflow or single phases applyRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) { data, err := newApplyData(cmd, args, flags) @@ -156,7 +152,7 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command { return data, nil }) - // binds the Runner to kubeadm upgrade apply command by altering + // Binds the Runner to kubeadm upgrade apply command by altering // command help, adding --skip-phases flag and by adding phases subcommands applyRunner.BindToCommand(cmd) @@ -173,18 +169,23 @@ func newApplyData(cmd *cobra.Command, args []string, applyFlags *applyFlags) (*a } upgradeVersion := upgradeCfg.Apply.KubernetesVersion - // The version arg is mandatory, during upgrade apply, unless it's specified in the config file + // The version arg is mandatory, unless it's specified in the config file if upgradeVersion == "" { if err := cmdutil.ValidateExactArgNumber(args, []string{"version"}); err != nil { return nil, err } } - // If option was specified in both args and config file, args will overwrite the config file. + // If the version was specified in both the arg and config file, the arg will overwrite the config file. if len(args) == 1 { upgradeVersion = args[0] } + ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(applyFlags.ignorePreflightErrors, upgradeCfg.Apply.IgnorePreflightErrors) + if err != nil { + return nil, err + } + force, ok := cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), "force", upgradeCfg.Apply.ForceUpgrade, &applyFlags.force).(*bool) if !ok { return nil, cmdutil.TypeMismatchErr("forceUpgrade", "bool") @@ -239,10 +240,6 @@ func newApplyData(cmd *cobra.Command, args []string, applyFlags *applyFlags) (*a return nil, errors.Wrap(err, "[upgrade/init config] FATAL") } - ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(applyFlags.ignorePreflightErrors, upgradeCfg.Apply.IgnorePreflightErrors) - if err != nil { - return nil, err - } // Also set the union of pre-flight errors to InitConfiguration, to provide a consistent view of the runtime configuration: initCfg.NodeRegistration.IgnorePreflightErrors = sets.List(ignorePreflightErrorsSet) @@ -303,7 +300,7 @@ func (d *applyData) RenewCerts() bool { return d.renewCerts } -// Cfg returns upgradeConfiguration. +// Cfg returns the UpgradeConfiguration. func (d *applyData) Cfg() *kubeadmapi.UpgradeConfiguration { return d.cfg } @@ -338,17 +335,17 @@ func (d *applyData) SessionIsInteractive() bool { return !(d.nonInteractiveMode || d.dryRun || d.force) } -// AllowExperimentalUpgrades returns true if allow upgrading to an alpha/beta/release candidate version of Kubernetes. +// AllowExperimentalUpgrades returns true if upgrading to an alpha/beta/release candidate version of Kubernetes is allowed. func (d *applyData) AllowExperimentalUpgrades() bool { return d.allowExperimentalUpgrades } -// AllowRCUpgrades returns true if allow upgrading to a release candidate version of Kubernetes. +// AllowRCUpgrades returns true if upgrading to a release candidate version of Kubernetes is allowed. func (d *applyData) AllowRCUpgrades() bool { return d.allowRCUpgrades } -// ForceUpgrade returns true if force upgrading although some requirements might not be met. +// ForceUpgrade returns true if force-upgrading is enabled. func (d *applyData) ForceUpgrade() bool { return d.force } diff --git a/cmd/kubeadm/app/cmd/upgrade/apply_test.go b/cmd/kubeadm/app/cmd/upgrade/apply_test.go index 99fa48c320f..5c16784f211 100644 --- a/cmd/kubeadm/app/cmd/upgrade/apply_test.go +++ b/cmd/kubeadm/app/cmd/upgrade/apply_test.go @@ -17,7 +17,15 @@ limitations under the License. package upgrade import ( + "fmt" + "os" + "path/filepath" + "strings" "testing" + + kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4" + "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" ) func TestSessionIsInteractive(t *testing.T) { @@ -61,3 +69,128 @@ func TestSessionIsInteractive(t *testing.T) { }) } } + +var testApplyConfig = fmt.Sprintf(`--- +apiVersion: %s +apply: + certificateRenewal: true + etcdUpgrade: true + imagePullPolicy: IfNotPresent + imagePullSerial: true +diff: {} +kind: UpgradeConfiguration +node: + certificateRenewal: true + etcdUpgrade: true + imagePullPolicy: IfNotPresent + imagePullSerial: true +plan: {} +timeouts: + controlPlaneComponentHealthCheck: 4m0s + discovery: 5m0s + etcdAPICall: 2m0s + kubeletHealthCheck: 4m0s + kubernetesAPICall: 1m0s + tlsBootstrap: 5m0s + upgradeManifests: 5m0s +`, kubeadmapiv1.SchemeGroupVersion.String()) + +func TestNewApplyData(t *testing.T) { + // create temp directory + tmpDir, err := os.MkdirTemp("", "kubeadm-upgrade-apply-test") + if err != nil { + t.Errorf("Unable to create temporary directory: %v", err) + } + defer func() { + _ = os.RemoveAll(tmpDir) + }() + + // create config file + configFilePath := filepath.Join(tmpDir, "test-config-file") + cfgFile, err := os.Create(configFilePath) + if err != nil { + t.Errorf("Unable to create file %q: %v", configFilePath, err) + } + defer func() { + _ = cfgFile.Close() + }() + if _, err = cfgFile.WriteString(testApplyConfig); err != nil { + t.Fatalf("Unable to write file %q: %v", configFilePath, err) + } + + testCases := []struct { + name string + args []string + flags map[string]string + validate func(*testing.T, *applyData) + expectedError string + }{ + { + name: "fails if no upgrade version set", + flags: map[string]string{ + options.CfgPath: configFilePath, + }, + expectedError: "missing one or more required arguments. Required arguments: [version]", + }, + { + name: "fails if invalid preflight checks are provided", + args: []string{"v1.1.0"}, + flags: map[string]string{ + options.IgnorePreflightErrors: "all,something-else", + }, + expectedError: "ignore-preflight-errors: Invalid value", + }, + { + name: "fails if kubeconfig file doesn't exists", + args: []string{"v1.1.0"}, + flags: map[string]string{ + options.CfgPath: configFilePath, + options.KubeconfigPath: "invalid-kubeconfig-path", + }, + expectedError: "couldn't create a Kubernetes client from file", + }, + + // TODO: add more test cases here when the fake client for `kubeadm upgrade apply` can be injected + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // initialize an external apply flags and inject it to the apply cmd + apf := &applyPlanFlags{ + kubeConfigPath: kubeadmconstants.GetAdminKubeConfigPath(), + cfgPath: "", + featureGatesString: "", + allowExperimentalUpgrades: false, + allowRCUpgrades: false, + printConfig: false, + out: os.Stdout, + } + + cmd := newCmdApply(apf) + + // sets cmd flags (that will be reflected on the init options) + for f, v := range tc.flags { + _ = cmd.Flags().Set(f, v) + } + + flags := &applyFlags{ + applyPlanFlags: apf, + etcdUpgrade: true, + renewCerts: true, + } + + // test newApplyData method + data, err := newApplyData(cmd, tc.args, flags) + if err == nil && len(tc.expectedError) != 0 { + t.Error("Expected error, but got success") + } + if err != nil && (len(tc.expectedError) == 0 || !strings.Contains(err.Error(), tc.expectedError)) { + t.Fatalf("newApplyData returned unexpected error, expected: %s, got %v", tc.expectedError, err) + } + + // exec additional validation on the returned value + if tc.validate != nil { + tc.validate(t, data) + } + }) + } +}