kubeadm: improve some grammar issues and add some unit test cases

This commit is contained in:
SataQiu 2024-08-15 18:05:38 +08:00
parent 595482d264
commit a2f8d31c65
9 changed files with 185 additions and 102 deletions

View File

@ -18,32 +18,27 @@ limitations under the License.
package apply package apply
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"github.com/pkg/errors" "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" clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy" proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
) )
// NewAddonPhase returns the addon Cobra command // NewAddonPhase returns a new addon phase.
func NewAddonPhase() workflow.Phase { func NewAddonPhase() workflow.Phase {
return workflow.Phase{ return workflow.Phase{
Name: "addon", Name: "addon",
Short: "Install required addons for passing conformance tests", Short: "Install the default kubeadm addons",
Long: cmdutil.MacroCommandLongDescription, Long: cmdutil.MacroCommandLongDescription,
Phases: []workflow.Phase{ Phases: []workflow.Phase{
{ {
@ -54,13 +49,13 @@ func NewAddonPhase() workflow.Phase {
}, },
{ {
Name: "coredns", Name: "coredns",
Short: "Install the CoreDNS addon to a Kubernetes cluster", Short: "Install the CoreDNS addon",
InheritFlags: getAddonPhaseFlags("coredns"), InheritFlags: getAddonPhaseFlags("coredns"),
Run: runCoreDNSAddon, Run: runCoreDNSAddon,
}, },
{ {
Name: "kube-proxy", Name: "kube-proxy",
Short: "Install the kube-proxy addon to a Kubernetes cluster", Short: "Install the kube-proxy addon",
InheritFlags: getAddonPhaseFlags("kube-proxy"), InheritFlags: getAddonPhaseFlags("kube-proxy"),
Run: runKubeProxyAddon, 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") return false, errors.Wrapf(err, "failed to determine whether all the control plane instances have been upgraded")
} }
if len(unupgradedControlPlanes) > 0 { 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 false, nil
} }
return true, 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 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 { func runCoreDNSAddon(c workflow.RunData) error {
cfg, client, patchesDir, out, dryRun, err := getInitData(c) cfg, client, patchesDir, out, dryRun, err := getInitData(c)
if err != nil { if err != nil {
@ -104,25 +98,6 @@ func runCoreDNSAddon(c workflow.RunData) error {
return nil 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 // Upgrade CoreDNS
if err := dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, dryRun); err != nil { if err := dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, dryRun); err != nil {
return err return err
@ -131,7 +106,7 @@ func runCoreDNSAddon(c workflow.RunData) error {
return nil return nil
} }
// runKubeProxyAddon installs KubeProxy addon to a Kubernetes cluster // runKubeProxyAddon installs the KubeProxy addon.
func runKubeProxyAddon(c workflow.RunData) error { func runKubeProxyAddon(c workflow.RunData) error {
cfg, client, _, out, dryRun, err := getInitData(c) cfg, client, _, out, dryRun, err := getInitData(c)
if err != nil { if err != nil {
@ -146,25 +121,6 @@ func runKubeProxyAddon(c workflow.RunData) error {
return nil 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 // Upgrade kube-proxy
if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, dryRun); err != nil { if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, dryRun); err != nil {
return err return err

View File

@ -30,12 +30,11 @@ import (
nodebootstraptoken "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" 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 { func NewBootstrapTokenPhase() workflow.Phase {
return workflow.Phase{ return workflow.Phase{
Name: "bootstrap-token", Name: "bootstrap-token",
Aliases: []string{"bootstraptoken"}, Short: "Configures bootstrap token and cluster-info RBAC rules",
Short: "Generates bootstrap tokens used to join a node to a cluster",
InheritFlags: []string{ InheritFlags: []string{
options.CfgPath, options.CfgPath,
options.KubeconfigPath, options.KubeconfigPath,
@ -56,7 +55,7 @@ func runBootstrapToken(c workflow.RunData) error {
return nil 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() client := data.Client()

View File

@ -29,7 +29,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "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 { func NewControlPlanePhase() workflow.Phase {
phase := workflow.Phase{ phase := workflow.Phase{
Name: "control-plane", Name: "control-plane",

View File

@ -28,7 +28,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" "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 { func NewKubeconfigPhase() workflow.Phase {
phase := workflow.Phase{ phase := workflow.Phase{
Name: "kubeconfig", 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 return nil
} }

View File

@ -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 { func NewKubeletConfigPhase() workflow.Phase {
phase := workflow.Phase{ phase := workflow.Phase{
Name: "kubelet-config", Name: "kubelet-config",
@ -59,15 +59,14 @@ func runKubeletConfigPhase(c workflow.RunData) error {
initCfg, dryRun := data.InitCfg(), data.DryRun() 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 // 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()) err := upgrade.WriteKubeletConfigFiles(initCfg, data.PatchesDir(), dryRun, data.OutputWriter())
if err != nil { if err != nil {
return err return err
} }
fmt.Println("[upgrade] The configuration for this node was successfully updated!") fmt.Println("[upgrade] The kubelet configuration for this node was successfully updated!")
fmt.Println("[upgrade] Now you should go ahead and upgrade the kubelet package using your package manager.")
return nil return nil
} }

View File

@ -38,12 +38,12 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/output" "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 { func NewPreflightPhase() workflow.Phase {
return workflow.Phase{ return workflow.Phase{
Name: "preflight", Name: "preflight",
Short: "Run upgrade apply pre-flight checks", Short: "Run upgrade apply preflight checks",
Long: "Run pre-flight checks for kubeadm upgrade apply.", Long: "Run preflight checks for kubeadm upgrade apply.",
Run: runPreflight, Run: runPreflight,
InheritFlags: []string{ InheritFlags: []string{
options.CfgPath, options.CfgPath,
@ -58,13 +58,12 @@ func NewPreflightPhase() workflow.Phase {
} }
} }
// runPreflight executes preflight checks logic.
func runPreflight(c workflow.RunData) error { func runPreflight(c workflow.RunData) error {
data, ok := c.(Data) data, ok := c.(Data)
if !ok { if !ok {
return errors.New("preflight phase invoked with an invalid data struct") 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{} printer := &output.TextPrinter{}
@ -81,7 +80,7 @@ func runPreflight(c workflow.RunData) error {
} }
// Run healthchecks against the cluster // 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 { if err := upgrade.CheckClusterHealth(client, &initCfg.ClusterConfiguration, ignorePreflightErrors, printer); err != nil {
return err return err
} }
@ -94,7 +93,7 @@ func runPreflight(c workflow.RunData) error {
} }
// Validate requested and validate actual version // 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 { if err := configutil.NormalizeKubernetesVersion(&initCfg.ClusterConfiguration); err != nil {
return err return err
} }
@ -110,7 +109,7 @@ func runPreflight(c workflow.RunData) error {
} }
versionGetter := upgrade.NewOfflineVersionGetter(upgrade.NewKubeVersionGetter(client), initCfg.KubernetesVersion) 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 return err
} }
@ -134,23 +133,23 @@ func runPreflight(c workflow.RunData) error {
return nil 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 // There are both fatal and skippable (with --force) errors
func EnforceVersionPolicies(newK8sVersionStr string, newK8sVersion *version.Version, allowExperimentalUpgrades, allowRCUpgrades, force bool, versionGetter upgrade.VersionGetter) error { 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) fmt.Printf("[upgrade/version] You have chosen to upgrade the cluster version to %q\n", newK8sVersionStr)
versionSkewErrs := upgrade.EnforceVersionPolicies(versionGetter, newK8sVersionStr, newK8sVersion, allowExperimentalUpgrades, allowRCUpgrades) versionSkewErrs := upgrade.EnforceVersionPolicies(versionGetter, newK8sVersionStr, newK8sVersion, allowExperimentalUpgrades, allowRCUpgrades)
if versionSkewErrs != nil { if versionSkewErrs != nil {
if len(versionSkewErrs.Mandatory) > 0 { 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)) kubeadmutil.FormatErrMsg(versionSkewErrs.Mandatory))
} }
if len(versionSkewErrs.Skippable) > 0 { if len(versionSkewErrs.Skippable) > 0 {
// Return the error if the user hasn't specified the --force flag // Return the error if the user hasn't specified the --force flag
if !force { 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)) kubeadmutil.FormatErrMsg(versionSkewErrs.Skippable))
} }
// Soft errors found, but --force was specified // Soft errors found, but --force was specified

View File

@ -39,12 +39,12 @@ func NewUploadConfigPhase() workflow.Phase {
return workflow.Phase{ return workflow.Phase{
Name: "upload-config", Name: "upload-config",
Aliases: []string{"uploadconfig"}, 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, Long: cmdutil.MacroCommandLongDescription,
Phases: []workflow.Phase{ Phases: []workflow.Phase{
{ {
Name: "all", Name: "all",
Short: "Upload all configuration to a config map", Short: "Upload all the configurations to ConfigMaps",
RunAllSiblings: true, RunAllSiblings: true,
InheritFlags: getUploadConfigPhaseFlags(), InheritFlags: getUploadConfigPhaseFlags(),
}, },
@ -56,7 +56,7 @@ func NewUploadConfigPhase() workflow.Phase {
}, },
{ {
Name: "kubelet", Name: "kubelet",
Short: "Upload the kubelet component config to a ConfigMap", Short: "Upload the kubelet configuration config to a ConfigMap",
Run: runUploadKubeletConfig, Run: runUploadKubeletConfig,
InheritFlags: getUploadConfigPhaseFlags(), InheritFlags: getUploadConfigPhaseFlags(),
}, },
@ -99,19 +99,19 @@ func runUploadKubeletConfig(c workflow.RunData) error {
} }
if dryRun { 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") fmt.Println("[dryrun] Would write the CRISocket annotation for the control-plane node")
return nil 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 { if err = kubeletphase.CreateConfigMap(&cfg.ClusterConfiguration, client); err != nil {
return errors.Wrap(err, "error creating kubelet configuration ConfigMap") return errors.Wrap(err, "error creating kubelet configuration ConfigMap")
} }
klog.V(1).Infoln("[upload-config] Preserving the CRISocket information for the control-plane node") 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 { 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 return nil

View File

@ -99,25 +99,21 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
applyData, ok := data.(*applyData) applyData, ok := data.(*applyData)
if !ok { if !ok {
return errors.New("invalid data struct") return errors.New("invalid data struct")
} }
if err := applyRunner.Run(args); err != nil { if err := applyRunner.Run(args); err != nil {
return err return err
} }
if flags.dryRun { if flags.dryRun {
fmt.Println("[upgrade/successful] Finished dryrunning successfully!") fmt.Println("[upgrade/successful] Finished dryrunning successfully!")
return nil return nil
} }
fmt.Println("") fmt.Println("")
fmt.Printf("[upgrade/successful] SUCCESS! Your cluster was upgraded to %q. Enjoy!\n", applyData.InitCfg().KubernetesVersion) fmt.Printf("[upgrade/successful] SUCCESS! A control plane node of your cluster was upgraded to %q.\n\n", applyData.InitCfg().KubernetesVersion)
fmt.Println("") fmt.Println("[upgrade/kubelet] Now please proceed with upgrading the rest of the nodes by following the right order.")
fmt.Println("[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.")
return nil 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.") 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) 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.NewPreflightPhase())
applyRunner.AppendPhase(phases.NewControlPlanePhase()) applyRunner.AppendPhase(phases.NewControlPlanePhase())
applyRunner.AppendPhase(phases.NewUploadConfigPhase()) applyRunner.AppendPhase(phases.NewUploadConfigPhase())
@ -142,7 +138,7 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command {
applyRunner.AppendPhase(phases.NewBootstrapTokenPhase()) applyRunner.AppendPhase(phases.NewBootstrapTokenPhase())
applyRunner.AppendPhase(phases.NewAddonPhase()) 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 // both when running the entire workflow or single phases
applyRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) { applyRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) {
data, err := newApplyData(cmd, args, flags) data, err := newApplyData(cmd, args, flags)
@ -156,7 +152,7 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command {
return data, nil 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 // command help, adding --skip-phases flag and by adding phases subcommands
applyRunner.BindToCommand(cmd) applyRunner.BindToCommand(cmd)
@ -173,18 +169,23 @@ func newApplyData(cmd *cobra.Command, args []string, applyFlags *applyFlags) (*a
} }
upgradeVersion := upgradeCfg.Apply.KubernetesVersion 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 upgradeVersion == "" {
if err := cmdutil.ValidateExactArgNumber(args, []string{"version"}); err != nil { if err := cmdutil.ValidateExactArgNumber(args, []string{"version"}); err != nil {
return nil, err 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 { if len(args) == 1 {
upgradeVersion = args[0] 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) force, ok := cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), "force", upgradeCfg.Apply.ForceUpgrade, &applyFlags.force).(*bool)
if !ok { if !ok {
return nil, cmdutil.TypeMismatchErr("forceUpgrade", "bool") 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") 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: // 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) initCfg.NodeRegistration.IgnorePreflightErrors = sets.List(ignorePreflightErrorsSet)
@ -303,7 +300,7 @@ func (d *applyData) RenewCerts() bool {
return d.renewCerts return d.renewCerts
} }
// Cfg returns upgradeConfiguration. // Cfg returns the UpgradeConfiguration.
func (d *applyData) Cfg() *kubeadmapi.UpgradeConfiguration { func (d *applyData) Cfg() *kubeadmapi.UpgradeConfiguration {
return d.cfg return d.cfg
} }
@ -338,17 +335,17 @@ func (d *applyData) SessionIsInteractive() bool {
return !(d.nonInteractiveMode || d.dryRun || d.force) 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 { func (d *applyData) AllowExperimentalUpgrades() bool {
return d.allowExperimentalUpgrades 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 { func (d *applyData) AllowRCUpgrades() bool {
return d.allowRCUpgrades 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 { func (d *applyData) ForceUpgrade() bool {
return d.force return d.force
} }

View File

@ -17,7 +17,15 @@ limitations under the License.
package upgrade package upgrade
import ( import (
"fmt"
"os"
"path/filepath"
"strings"
"testing" "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) { 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)
}
})
}
}