cmd/*: fail on unrecognized flags/arguments for component CLI

In case a malformed flag is passed to k8s components
such as "–foo", where "–" is not an ASCII dash character,
the components currently silently ignore the flag
and treat it as a positional argument.

Make k8s components/commands exit with an error if a positional argument
that is not empty is found. Include a custom error message for all
components except kubeadm, as cobra.NoArgs is used in a lot of
places already (can be fixed in a followup).

The kubelet already handles this properly - e.g.:
'unknown command: "–foo"'

This change affects:
- cloud-controller-manager
- kube-apiserver
- kube-controller-manager
- kube-proxy
- kubeadm {alpha|config|token|version}
- kubemark

Signed-off-by: Monis Khan <mok@vmware.com>
Signed-off-by: Lubomir I. Ivanov <lubomirivanov@vmware.com>
This commit is contained in:
Monis Khan 2020-04-28 13:51:29 -04:00 committed by Lubomir I. Ivanov
parent 60559bc919
commit fc4f91f10b
16 changed files with 78 additions and 18 deletions

View File

@ -80,6 +80,14 @@ the cloud specific control loops shipped with Kubernetes.`,
}
},
Args: func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
fs := cmd.Flags()

View File

@ -117,6 +117,14 @@ cluster's shared state through which all other components interact.`,
return Run(completedOptions, genericapiserver.SetupSignalHandler())
},
Args: func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
fs := cmd.Flags()

View File

@ -119,6 +119,14 @@ controller, and serviceaccounts controller.`,
os.Exit(1)
}
},
Args: func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
fs := cmd.Flags()

View File

@ -294,10 +294,7 @@ func (o *Options) processHostnameOverrideFlag() error {
}
// Validate validates all the required options.
func (o *Options) Validate(args []string) error {
if len(args) != 0 {
return errors.New("no arguments are supported")
}
func (o *Options) Validate() error {
if errs := validation.Validate(o.config); len(errs) != 0 {
return errs.ToAggregate()
}
@ -490,7 +487,7 @@ with the apiserver API to configure the proxy.`,
if err := opts.Complete(); err != nil {
klog.Fatalf("failed complete: %v", err)
}
if err := opts.Validate(args); err != nil {
if err := opts.Validate(); err != nil {
klog.Fatalf("failed validate: %v", err)
}
@ -498,6 +495,14 @@ with the apiserver API to configure the proxy.`,
klog.Exit(err)
}
},
Args: func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
var err error

View File

@ -27,7 +27,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
eventsv1beta1 "k8s.io/api/events/v1beta1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apiserver/pkg/authentication/authenticator"
@ -83,11 +83,19 @@ kube-scheduler is the reference implementation.
See [scheduling](https://kubernetes.io/docs/concepts/scheduling/)
for more information about scheduling and the kube-scheduler component.`,
Run: func(cmd *cobra.Command, args []string) {
if err := runCommand(cmd, args, opts, registryOptions...); err != nil {
if err := runCommand(cmd, opts, registryOptions...); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
},
Args: func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
fs := cmd.Flags()
namedFlagSets := opts.Flags()
@ -114,14 +122,14 @@ for more information about scheduling and the kube-scheduler component.`,
}
// runCommand runs the scheduler.
func runCommand(cmd *cobra.Command, args []string, opts *options.Options, registryOptions ...Option) error {
func runCommand(cmd *cobra.Command, opts *options.Options, registryOptions ...Option) error {
verflag.PrintAndExitIfRequested()
cliflag.PrintFlags(cmd.Flags())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cc, sched, err := Setup(ctx, args, opts, registryOptions...)
cc, sched, err := Setup(ctx, opts, registryOptions...)
if err != nil {
return err
}
@ -298,11 +306,7 @@ func WithPlugin(name string, factory framework.PluginFactory) Option {
}
// Setup creates a completed config and a scheduler based on the command args and options
func Setup(ctx context.Context, args []string, opts *options.Options, outOfTreeRegistryOptions ...Option) (*schedulerserverconfig.CompletedConfig, *scheduler.Scheduler, error) {
if len(args) != 0 {
fmt.Fprint(os.Stderr, "arguments are not supported\n")
}
func Setup(ctx context.Context, opts *options.Options, outOfTreeRegistryOptions ...Option) (*schedulerserverconfig.CompletedConfig, *scheduler.Scheduler, error) {
if errs := opts.Validate(); len(errs) > 0 {
return nil, nil, utilerrors.NewAggregate(errs)
}

View File

@ -373,10 +373,9 @@ profiles:
t.Fatal(err)
}
var args []string
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cc, sched, err := Setup(ctx, args, opts)
cc, sched, err := Setup(ctx, opts)
if err != nil {
t.Fatal(err)
}

View File

@ -114,7 +114,7 @@ func StartTestServer(t Logger, customFlags []string) (result TestServer, err err
t.Logf("kube-scheduler will listen insecurely on port %d...", opts.CombinedInsecureServing.BindPort)
}
cc, sched, err := app.Setup(ctx, []string{}, opts)
cc, sched, err := app.Setup(ctx, opts)
if err != nil {
return result, fmt.Errorf("failed to create config from options: %v", err)
}

View File

@ -100,6 +100,7 @@ func NewCmdCertificateKey() *cobra.Command {
fmt.Println(key)
return nil
},
Args: cobra.NoArgs,
}
}
@ -195,6 +196,7 @@ func getRenewSubCommands(out io.Writer, kdir string) []*cobra.Command {
}
return nil
},
Args: cobra.NoArgs,
}
addRenewFlags(allCmd, flags)
@ -378,6 +380,7 @@ func newCmdCertsExpiration(out io.Writer, kdir string) *cobra.Command {
w.Flush()
return nil
},
Args: cobra.NoArgs,
}
addExpirationFlags(cmd, flags)

View File

@ -94,6 +94,7 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
// Otherwise, write a kubeconfig file with a generate client cert
return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalcfg, clientName, organizations)
},
Args: cobra.NoArgs,
}
// Add ClusterConfiguration backed flags to the command

View File

@ -104,6 +104,7 @@ func newCmdKubeletConfigEnableDynamic() *cobra.Command {
return kubeletphase.EnableDynamicConfigForNode(client, nodeName, kubeletVersion)
},
Args: cobra.NoArgs,
}
options.AddKubeConfigFlag(cmd.Flags(), &kubeConfigFile)

View File

@ -136,6 +136,7 @@ func getSelfhostingSubCommand(in io.Reader) *cobra.Command {
waiter := apiclient.NewKubeWaiter(client, 2*time.Minute, os.Stdout)
return selfhosting.CreateSelfHostedControlPlane(constants.GetStaticPodDirectory(), constants.KubernetesDir, internalcfg, client, waiter, false, certsInSecrets)
},
Args: cobra.NoArgs,
}
// Add flags to the command

View File

@ -140,6 +140,7 @@ func newCmdConfigPrintActionDefaults(out io.Writer, action string, configBytesPr
}
return runConfigPrintActionDefaults(out, groups, configBytesProc)
},
Args: cobra.NoArgs,
}
cmd.Flags().StringSliceVar(&kinds, "component-configs", kinds,
fmt.Sprintf("A comma-separated list for component config API objects to print the default values for. Available values: %v. If this flag is not set, no component configs will be printed.", getSupportedComponentConfigKinds()))
@ -295,6 +296,7 @@ func NewCmdConfigMigrate(out io.Writer) *cobra.Command {
}
return nil
},
Args: cobra.NoArgs,
}
cmd.Flags().StringVar(&oldCfgPath, "old-config", "", "Path to the kubeadm config file that is using an old API version and should be converted. This flag is mandatory.")
cmd.Flags().StringVar(&newCfgPath, "new-config", "", "Path to the resulting equivalent kubeadm config file using the new API version. Optional, if not specified output will be sent to STDOUT.")
@ -335,6 +337,7 @@ func NewCmdConfigView(out io.Writer, kubeConfigFile *string) *cobra.Command {
return RunConfigView(out, client)
},
Args: cobra.NoArgs,
}
}
@ -375,6 +378,7 @@ func NewCmdConfigUploadFromFile(out io.Writer, kubeConfigFile *string) *cobra.Co
klog.V(1).Infof("[config] uploading configuration")
return uploadconfig.UploadConfiguration(internalcfg, client)
},
Args: cobra.NoArgs,
}
options.AddConfigFlag(cmd.Flags(), &cfgPath)
return cmd
@ -429,6 +433,7 @@ func NewCmdConfigUploadFromFlags(out io.Writer, kubeConfigFile *string) *cobra.C
klog.V(1).Infof("[config] uploading configuration")
return uploadconfig.UploadConfiguration(internalcfg, client)
},
Args: cobra.NoArgs,
}
AddInitConfigFlags(cmd.PersistentFlags(), initCfg)
AddClusterConfigFlags(cmd.PersistentFlags(), clusterCfg, &featureGatesString)
@ -487,6 +492,7 @@ func NewCmdConfigImagesPull() *cobra.Command {
}
return PullControlPlaneImages(containerRuntime, &internalcfg.ClusterConfiguration)
},
Args: cobra.NoArgs,
}
AddImagesCommonConfigFlags(cmd.PersistentFlags(), externalClusterCfg, &cfgPath, &featureGatesString)
cmdutil.AddCRISocketFlag(cmd.PersistentFlags(), &externalInitCfg.NodeRegistration.CRISocket)
@ -556,6 +562,7 @@ func NewCmdConfigImagesList(out io.Writer, mockK8sVersion *string) *cobra.Comman
return imagesList.Run(out, printer)
},
Args: cobra.NoArgs,
}
outputFlags.AddFlags(cmd)
AddImagesCommonConfigFlags(cmd.PersistentFlags(), externalcfg, &cfgPath, &featureGatesString)

View File

@ -173,6 +173,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
return RunListTokens(out, errW, client, printer)
},
Args: cobra.NoArgs,
}
outputFlags.AddFlags(listCmd)
@ -226,6 +227,7 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return RunGenerateToken(out)
},
Args: cobra.NoArgs,
}
}

View File

@ -43,6 +43,7 @@ func NewCmdVersion(out io.Writer) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return RunVersion(out, cmd)
},
Args: cobra.NoArgs,
}
cmd.Flags().StringP("output", "o", "", "Output format; available options are 'yaml', 'json' and 'short'")
return cmd

View File

@ -48,7 +48,11 @@ func TestCmdVersion(t *testing.T) {
kubeadmPath := getKubeadmPath()
for _, rt := range versionTest {
t.Run(rt.name, func(t *testing.T) {
stdout, _, _, actual := RunCmd(kubeadmPath, "version", rt.args)
args := []string{"version"}
if len(rt.args) > 0 {
args = append(args, rt.args)
}
stdout, _, _, actual := RunCmd(kubeadmPath, args...)
if (actual == nil) != rt.expected {
t.Errorf(
"failed CmdVersion running 'kubeadm version %s' with an error: %v\n\texpected: %t\n\t actual: %t",

View File

@ -148,6 +148,14 @@ func newHollowNodeCommand() *cobra.Command {
verflag.PrintAndExitIfRequested()
run(s)
},
Args: func(cmd *cobra.Command, args []string) error {
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
s.addFlags(cmd.Flags())