diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 48e800aced7..ba40ca46e6c 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -155,7 +155,7 @@ func NewCmdInit(out io.Writer) *cobra.Command { // defines additional flag that are not used by the init command but that could be eventually used // by the sub-commands automatically generated for phases - initRunner.SetPhaseSubcommandsAdditionalFlags(func(flags *flag.FlagSet) { + initRunner.SetAdditionalFlags(func(flags *flag.FlagSet) { options.AddImageMetaFlags(flags, &initOptions.externalcfg.ImageRepository) options.AddKubeConfigFlag(flags, &initOptions.kubeconfigPath) options.AddKubeConfigDirFlag(flags, &initOptions.kubeconfigDir) diff --git a/cmd/kubeadm/app/cmd/phases/addons.go b/cmd/kubeadm/app/cmd/phases/addons.go index be6057677c3..4a4b7089986 100644 --- a/cmd/kubeadm/app/cmd/phases/addons.go +++ b/cmd/kubeadm/app/cmd/phases/addons.go @@ -47,23 +47,23 @@ type addonData interface { // NewAddonPhase returns the addon Cobra command func NewAddonPhase() workflow.Phase { return workflow.Phase{ - Name: "addon", - Short: "Installs required addons for passing Conformance tests", - CmdFlags: getAddonPhaseFlags("all"), + Name: "addon", + Short: "Installs required addons for passing Conformance tests", + InheritFlags: getAddonPhaseFlags("all"), Phases: []workflow.Phase{ { - Name: "coredns", - Short: "Installs the CoreDNS addon to a Kubernetes cluster", - Long: coreDNSAddonLongDesc, - CmdFlags: getAddonPhaseFlags("coredns"), - Run: runCoreDNSAddon, + Name: "coredns", + Short: "Installs the CoreDNS addon to a Kubernetes cluster", + Long: coreDNSAddonLongDesc, + InheritFlags: getAddonPhaseFlags("coredns"), + Run: runCoreDNSAddon, }, { - Name: "kube-proxy", - Short: "Installs the kube-proxy addon to a Kubernetes cluster", - Long: kubeProxyAddonLongDesc, - CmdFlags: getAddonPhaseFlags("kube-proxy"), - Run: runKubeProxyAddon, + Name: "kube-proxy", + Short: "Installs the kube-proxy addon to a Kubernetes cluster", + Long: kubeProxyAddonLongDesc, + InheritFlags: getAddonPhaseFlags("kube-proxy"), + Run: runKubeProxyAddon, }, }, } diff --git a/cmd/kubeadm/app/cmd/phases/bootstraptoken.go b/cmd/kubeadm/app/cmd/phases/bootstraptoken.go index d17fe3d1f3f..b6492960f8b 100644 --- a/cmd/kubeadm/app/cmd/phases/bootstraptoken.go +++ b/cmd/kubeadm/app/cmd/phases/bootstraptoken.go @@ -64,7 +64,7 @@ func NewBootstrapTokenPhase() workflow.Phase { Short: "Generates bootstrap tokens used to join a node to a cluster", Example: bootstrapTokenExamples, Long: bootstrapTokenLongDesc, - CmdFlags: []string{ + InheritFlags: []string{ options.CfgPath, options.KubeconfigDir, options.SkipTokenPrint, diff --git a/cmd/kubeadm/app/cmd/phases/certs.go b/cmd/kubeadm/app/cmd/phases/certs.go index 42aee88a3f5..d33bc68829a 100644 --- a/cmd/kubeadm/app/cmd/phases/certs.go +++ b/cmd/kubeadm/app/cmd/phases/certs.go @@ -60,11 +60,11 @@ type certsData interface { // NewCertsPhase returns the phase for the certs func NewCertsPhase() workflow.Phase { return workflow.Phase{ - Name: "certs", - Short: "Certificate generation", - Phases: newCertSubPhases(), - Run: runCerts, - CmdFlags: getCertPhaseFlags("all"), + Name: "certs", + Short: "Certificate generation", + Phases: newCertSubPhases(), + Run: runCerts, + InheritFlags: getCertPhaseFlags("all"), } } @@ -107,8 +107,8 @@ func newCertSubPhase(certSpec *certsphase.KubeadmCert, run func(c workflow.RunDa certSpec.BaseName, getSANDescription(certSpec), ), - Run: run, - CmdFlags: getCertPhaseFlags(certSpec.Name), + Run: run, + InheritFlags: getCertPhaseFlags(certSpec.Name), } return phase } diff --git a/cmd/kubeadm/app/cmd/phases/controlplane.go b/cmd/kubeadm/app/cmd/phases/controlplane.go index a76bedbcce7..7960e5c84c4 100644 --- a/cmd/kubeadm/app/cmd/phases/controlplane.go +++ b/cmd/kubeadm/app/cmd/phases/controlplane.go @@ -77,18 +77,18 @@ func NewControlPlanePhase() workflow.Phase { newControlPlaneSubPhase(kubeadmconstants.KubeControllerManager), newControlPlaneSubPhase(kubeadmconstants.KubeScheduler), }, - Run: runControlPlanePhase, - CmdFlags: getControlPlanePhaseFlags("all"), + Run: runControlPlanePhase, + InheritFlags: getControlPlanePhaseFlags("all"), } return phase } func newControlPlaneSubPhase(component string) workflow.Phase { phase := workflow.Phase{ - Name: controlPlanePhaseProperties[component].name, - Short: controlPlanePhaseProperties[component].short, - Run: runControlPlaneSubPhase(component), - CmdFlags: getControlPlanePhaseFlags(component), + Name: controlPlanePhaseProperties[component].name, + Short: controlPlanePhaseProperties[component].short, + Run: runControlPlaneSubPhase(component), + InheritFlags: getControlPlanePhaseFlags(component), } return phase } diff --git a/cmd/kubeadm/app/cmd/phases/etcd.go b/cmd/kubeadm/app/cmd/phases/etcd.go index 384ae23beb9..85a82c54be2 100644 --- a/cmd/kubeadm/app/cmd/phases/etcd.go +++ b/cmd/kubeadm/app/cmd/phases/etcd.go @@ -54,18 +54,18 @@ func NewEtcdPhase() workflow.Phase { Phases: []workflow.Phase{ newEtcdLocalSubPhase(), }, - CmdFlags: getEtcdPhaseFlags(), + InheritFlags: getEtcdPhaseFlags(), } return phase } func newEtcdLocalSubPhase() workflow.Phase { phase := workflow.Phase{ - Name: "local", - Short: "Generates the static Pod manifest file for a local, single-node local etcd instance.", - Example: etcdLocalExample, - Run: runEtcdPhaseLocal(), - CmdFlags: getEtcdPhaseFlags(), + Name: "local", + Short: "Generates the static Pod manifest file for a local, single-node local etcd instance.", + Example: etcdLocalExample, + Run: runEtcdPhaseLocal(), + InheritFlags: getEtcdPhaseFlags(), } return phase } diff --git a/cmd/kubeadm/app/cmd/phases/kubeconfig.go b/cmd/kubeadm/app/cmd/phases/kubeconfig.go index d7160ccef92..d9240242d28 100644 --- a/cmd/kubeadm/app/cmd/phases/kubeconfig.go +++ b/cmd/kubeadm/app/cmd/phases/kubeconfig.go @@ -83,19 +83,19 @@ func NewKubeConfigPhase() workflow.Phase { NewKubeConfigFilePhase(kubeadmconstants.ControllerManagerKubeConfigFileName), NewKubeConfigFilePhase(kubeadmconstants.SchedulerKubeConfigFileName), }, - Run: runKubeConfig, - CmdFlags: getKubeConfigPhaseFlags("all"), + Run: runKubeConfig, + InheritFlags: getKubeConfigPhaseFlags("all"), } } // NewKubeConfigFilePhase creates a kubeadm workflow phase that creates a kubeconfig file. func NewKubeConfigFilePhase(kubeConfigFileName string) workflow.Phase { return workflow.Phase{ - Name: kubeconfigFilePhaseProperties[kubeConfigFileName].name, - Short: kubeconfigFilePhaseProperties[kubeConfigFileName].short, - Long: fmt.Sprintf(kubeconfigFilePhaseProperties[kubeConfigFileName].long, kubeConfigFileName), - Run: runKubeConfigFile(kubeConfigFileName), - CmdFlags: getKubeConfigPhaseFlags(kubeConfigFileName), + Name: kubeconfigFilePhaseProperties[kubeConfigFileName].name, + Short: kubeconfigFilePhaseProperties[kubeConfigFileName].short, + Long: fmt.Sprintf(kubeconfigFilePhaseProperties[kubeConfigFileName].long, kubeConfigFileName), + Run: runKubeConfigFile(kubeConfigFileName), + InheritFlags: getKubeConfigPhaseFlags(kubeConfigFileName), } } diff --git a/cmd/kubeadm/app/cmd/phases/kubelet.go b/cmd/kubeadm/app/cmd/phases/kubelet.go index 555d996a096..2d37fda4a03 100644 --- a/cmd/kubeadm/app/cmd/phases/kubelet.go +++ b/cmd/kubeadm/app/cmd/phases/kubelet.go @@ -50,7 +50,7 @@ func NewKubeletStartPhase() workflow.Phase { Long: "Writes a file with KubeletConfiguration and an environment file with node specific kubelet settings, and then (re)starts kubelet.", Example: kubeletStartPhaseExample, Run: runKubeletStart, - CmdFlags: []string{ + InheritFlags: []string{ options.CfgPath, options.NodeCRISocket, options.NodeName, diff --git a/cmd/kubeadm/app/cmd/phases/markcontrolplane.go b/cmd/kubeadm/app/cmd/phases/markcontrolplane.go index 966bfeca723..eac8a1ed4f9 100644 --- a/cmd/kubeadm/app/cmd/phases/markcontrolplane.go +++ b/cmd/kubeadm/app/cmd/phases/markcontrolplane.go @@ -48,7 +48,7 @@ func NewMarkControlPlanePhase() workflow.Phase { Name: "mark-control-plane", Short: "Mark a node as a control-plane", Example: markControlPlaneExample, - CmdFlags: []string{ + InheritFlags: []string{ options.NodeName, }, Run: runMarkControlPlane, diff --git a/cmd/kubeadm/app/cmd/phases/preflight.go b/cmd/kubeadm/app/cmd/phases/preflight.go index 3f3aa67b2a6..8884a9fe864 100644 --- a/cmd/kubeadm/app/cmd/phases/preflight.go +++ b/cmd/kubeadm/app/cmd/phases/preflight.go @@ -53,7 +53,7 @@ func NewPreflightMasterPhase() workflow.Phase { Long: "Run master pre-flight checks, functionally equivalent to what implemented by kubeadm init.", Example: masterPreflightExample, Run: runPreflightMaster, - CmdFlags: []string{ + InheritFlags: []string{ options.CfgPath, options.IgnorePreflightErrors, }, diff --git a/cmd/kubeadm/app/cmd/phases/uploadconfig.go b/cmd/kubeadm/app/cmd/phases/uploadconfig.go index 0aa4f17a26e..88a6ba5d147 100644 --- a/cmd/kubeadm/app/cmd/phases/uploadconfig.go +++ b/cmd/kubeadm/app/cmd/phases/uploadconfig.go @@ -72,20 +72,20 @@ func NewUploadConfigPhase() workflow.Phase { Long: cmdutil.MacroCommandLongDescription, Phases: []workflow.Phase{ { - Name: "kubeadm", - Short: "Uploads the kubeadm ClusterConfiguration to a ConfigMap", - Long: uploadKubeadmConfigLongDesc, - Example: uploadKubeadmConfigExample, - Run: runUploadKubeadmConfig, - CmdFlags: getUploadConfigPhaseFlags(), + Name: "kubeadm", + Short: "Uploads the kubeadm ClusterConfiguration to a ConfigMap", + Long: uploadKubeadmConfigLongDesc, + Example: uploadKubeadmConfigExample, + Run: runUploadKubeadmConfig, + InheritFlags: getUploadConfigPhaseFlags(), }, { - Name: "kubelet", - Short: "Uploads the kubelet component config to a ConfigMap", - Long: uploadKubeletConfigLongDesc, - Example: uploadKubeletConfigExample, - Run: runUploadKubeletConfig, - CmdFlags: getUploadConfigPhaseFlags(), + Name: "kubelet", + Short: "Uploads the kubelet component config to a ConfigMap", + Long: uploadKubeletConfigLongDesc, + Example: uploadKubeletConfigExample, + Run: runUploadKubeletConfig, + InheritFlags: getUploadConfigPhaseFlags(), }, }, } diff --git a/cmd/kubeadm/app/cmd/phases/workflow/phase.go b/cmd/kubeadm/app/cmd/phases/workflow/phase.go index 5aac7c5a1a2..f3d14e22490 100644 --- a/cmd/kubeadm/app/cmd/phases/workflow/phase.go +++ b/cmd/kubeadm/app/cmd/phases/workflow/phase.go @@ -16,6 +16,8 @@ limitations under the License. package workflow +import "github.com/spf13/pflag" + // Phase provides an implementation of a workflow phase that allows // creation of new phases by simply instantiating a variable of this type. type Phase struct { @@ -53,11 +55,17 @@ type Phase struct { // If this function return nil, the phase action is always executed. RunIf func(data RunData) (bool, error) - // CmdFlags defines the list of flags that should be assigned to the cobra command generated - // for this phase; flags are inherited from the parent command or defined as additional flags - // in the phase runner. If the values is not set or empty, no flags will be assigned to the command + // InheritFlags defines the list of flags that the cobra command generated for this phase should Inherit + // from local flags defined in the parent command / or additional flags defined in the phase runner. + // If the values is not set or empty, no flags will be assigned to the command // Nb. global flags are automatically inherited by nested cobra command - CmdFlags []string + InheritFlags []string + + // LocalFlags defines the list of flags that should be assigned to the cobra command generated + // for this phase. + // Nb. if two or phases have the same local flags, please consider using local flags in the parent command + // or additional flags defined in the phase runner. + LocalFlags *pflag.FlagSet } // AppendPhase adds the given phase to the nested, ordered sequence of phases. diff --git a/cmd/kubeadm/app/cmd/phases/workflow/runner.go b/cmd/kubeadm/app/cmd/phases/workflow/runner.go index 7bf8df24338..7474d5e5a86 100644 --- a/cmd/kubeadm/app/cmd/phases/workflow/runner.go +++ b/cmd/kubeadm/app/cmd/phases/workflow/runner.go @@ -60,8 +60,8 @@ type Runner struct { // more than one time) runData RunData - // cmdAdditionalFlags holds additional flags that could be added to the subcommands generated - // for each phase. Flags could be inherited from the parent command too + // cmdAdditionalFlags holds additional, shared flags that could be added to the subcommands generated + // for phases. Flags could be inherited from the parent command too or added directly to each phase cmdAdditionalFlags *pflag.FlagSet // phaseRunners is part of the internal state of the runner and provides @@ -269,10 +269,11 @@ func (e *Runner) Help(cmdUse string) string { return line } -// SetPhaseSubcommandsAdditionalFlags allows to define flags to be added +// SetAdditionalFlags allows to define flags to be added // to the subcommands generated for each phase (but not existing in the parent command). // Please note that this command needs to be done before BindToCommand. -func (e *Runner) SetPhaseSubcommandsAdditionalFlags(fn func(*pflag.FlagSet)) { +// Nb. if a flag is used only by one phase, please consider using phase LocalFlags. +func (e *Runner) SetAdditionalFlags(fn func(*pflag.FlagSet)) { // creates a new NewFlagSet e.cmdAdditionalFlags = pflag.NewFlagSet("phaseAdditionalFlags", pflag.ContinueOnError) // invokes the function that sets additional flags @@ -325,11 +326,18 @@ func (e *Runner) BindToCommand(cmd *cobra.Command) { // makes the new command inherits local flags from the parent command // Nb. global flags will be inherited automatically - inheritsFlags(cmd.Flags(), phaseCmd.Flags(), p.CmdFlags) + inheritsFlags(cmd.Flags(), phaseCmd.Flags(), p.InheritFlags) - // If defined, additional flags for phases should be added as well + // makes the new command inherits additional flags for phases if e.cmdAdditionalFlags != nil { - inheritsFlags(e.cmdAdditionalFlags, phaseCmd.Flags(), p.CmdFlags) + inheritsFlags(e.cmdAdditionalFlags, phaseCmd.Flags(), p.InheritFlags) + } + + // If defined, added phase local flags + if p.LocalFlags != nil { + p.LocalFlags.VisitAll(func(f *pflag.Flag) { + phaseCmd.Flags().AddFlag(f) + }) } // adds the command to parent diff --git a/cmd/kubeadm/app/cmd/phases/workflow/runner_test.go b/cmd/kubeadm/app/cmd/phases/workflow/runner_test.go index fe5184417d8..15ad369d073 100644 --- a/cmd/kubeadm/app/cmd/phases/workflow/runner_test.go +++ b/cmd/kubeadm/app/cmd/phases/workflow/runner_test.go @@ -284,14 +284,25 @@ func TestHelp(t *testing.T) { func phaseBuilder4(name string, cmdFlags []string, phases ...Phase) Phase { return Phase{ - Name: name, - Phases: phases, - CmdFlags: cmdFlags, + Name: name, + Phases: phases, + InheritFlags: cmdFlags, + } +} + +func phaseBuilder5(name string, flags *pflag.FlagSet) Phase { + return Phase{ + Name: name, + LocalFlags: flags, } } func TestBindToCommand(t *testing.T) { + var dummy string + localFlags := pflag.NewFlagSet("dummy", pflag.ContinueOnError) + localFlags.StringVarP(&dummy, "flag4", "d", "d", "d") + var usecases = []struct { name string runner Runner @@ -339,6 +350,15 @@ func TestBindToCommand(t *testing.T) { "phase baz": {"flag1"}, }, }, + { + name: "it should be possible to apply custom flags to single phases", + runner: Runner{ + Phases: []Phase{phaseBuilder5("foo", localFlags)}, + }, + expectedCmdAndFlags: map[string][]string{ + "phase foo": {"flag4"}, + }, + }, { name: "all the above applies to nested phases too", runner: Runner{ @@ -346,6 +366,7 @@ func TestBindToCommand(t *testing.T) { phaseBuilder4("foo", []string{"flag3"}, phaseBuilder4("bar", []string{"flag1", "flag2", "flag3"}), phaseBuilder4("baz", []string{"flag1"}), //test if additional flags are filtered too + phaseBuilder5("qux", localFlags), ), }, }, @@ -357,6 +378,7 @@ func TestBindToCommand(t *testing.T) { "phase foo": {"flag3"}, "phase foo bar": {"flag1", "flag2", "flag3"}, "phase foo baz": {"flag1"}, + "phase foo qux": {"flag4"}, }, }, } @@ -372,7 +394,7 @@ func TestBindToCommand(t *testing.T) { cmd.Flags().StringVarP(&dummy2, "flag2", "b", "b", "b") if rt.setAdditionalFlags != nil { - rt.runner.SetPhaseSubcommandsAdditionalFlags(rt.setAdditionalFlags) + rt.runner.SetAdditionalFlags(rt.setAdditionalFlags) } rt.runner.BindToCommand(cmd)