diff --git a/pkg/cmd/exec/exec.go b/pkg/cmd/exec/exec.go index d4513a8e..0dc3b290 100644 --- a/pkg/cmd/exec/exec.go +++ b/pkg/cmd/exec/exec.go @@ -45,10 +45,10 @@ import ( var ( execExample = templates.Examples(i18n.T(` # Get output from running 'date' command from pod mypod, using the first container by default - kubectl exec mypod date + kubectl exec mypod -- date # Get output from running 'date' command in ruby-container from pod mypod - kubectl exec mypod -c ruby-container date + kubectl exec mypod -c ruby-container -- date # Switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod mypod # and sends stdout/stderr from 'bash' back to the client @@ -62,15 +62,14 @@ var ( kubectl exec mypod -i -t -- ls -t /usr # Get output from running 'date' command from the first pod of the deployment mydeployment, using the first container by default - kubectl exec deploy/mydeployment date + kubectl exec deploy/mydeployment -- date # Get output from running 'date' command from the first pod of the service myservice, using the first container by default - kubectl exec svc/myservice date + kubectl exec svc/myservice -- date `)) ) const ( - execUsageStr = "expected 'exec (POD | TYPE/NAME) COMMAND [ARG1] [ARG2] ... [ARGN]'.\nPOD or TYPE/NAME and COMMAND are required arguments for the exec command" defaultPodExecTimeout = 60 * time.Second ) @@ -96,6 +95,7 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C }, } cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodExecTimeout) + cmdutil.AddJsonFilenameFlag(cmd.Flags(), &options.FilenameOptions.Filenames, "to use to exec into the resource") // TODO support UID cmd.Flags().StringVarP(&options.ContainerName, "container", "c", options.ContainerName, "Container name. If omitted, the first container in the pod will be chosen") cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", options.Stdin, "Pass stdin to the container") @@ -146,9 +146,11 @@ type StreamOptions struct { // ExecOptions declare the arguments accepted by the Exec command type ExecOptions struct { StreamOptions + resource.FilenameOptions - ResourceName string - Command []string + ResourceName string + Command []string + EnforceNamespace bool ParentCommandName string EnableSuggestedCmdUsage bool @@ -166,17 +168,22 @@ type ExecOptions struct { // Complete verifies command line arguments and loads data from the command environment func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []string, argsLenAtDash int) error { - // Let kubectl exec follow rules for `--`, see #13004 issue - if len(argsIn) == 0 || argsLenAtDash == 0 { - return cmdutil.UsageErrorf(cmd, execUsageStr) + if len(argsIn) > 0 && argsLenAtDash != 0 { + p.ResourceName = argsIn[0] + } + if argsLenAtDash > -1 { + p.Command = argsIn[argsLenAtDash:] + } else if len(argsIn) > 1 { + fmt.Fprint(p.ErrOut, "kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.\n") + p.Command = argsIn[1:] + } else if len(argsIn) > 0 && len(p.FilenameOptions.Filenames) != 0 { + fmt.Fprint(p.ErrOut, "kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.\n") + p.Command = argsIn[0:] + p.ResourceName = "" } - p.ResourceName = argsIn[0] - p.Command = argsIn[1:] - var err error - - p.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() + p.Namespace, p.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } @@ -215,8 +222,8 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s // Validate checks that the provided exec options are specified. func (p *ExecOptions) Validate() error { - if len(p.PodName) == 0 && len(p.ResourceName) == 0 { - return fmt.Errorf("pod or type/name must be specified") + if len(p.PodName) == 0 && len(p.ResourceName) == 0 && len(p.FilenameOptions.Filenames) == 0 { + return fmt.Errorf("pod, type/name or --filename must be specified") } if len(p.Command) == 0 { return fmt.Errorf("you must specify at least one command for the container") @@ -293,7 +300,11 @@ func (p *ExecOptions) Run() error { } else { builder := p.Builder(). WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). - NamespaceParam(p.Namespace).DefaultNamespace().ResourceNames("pods", p.ResourceName) + FilenameParam(p.EnforceNamespace, &p.FilenameOptions). + NamespaceParam(p.Namespace).DefaultNamespace() + if len(p.ResourceName) > 0 { + builder = builder.ResourceNames("pods", p.ResourceName) + } obj, err := builder.Do().Object() if err != nil { diff --git a/pkg/cmd/exec/exec_test.go b/pkg/cmd/exec/exec_test.go index 74944e24..814e3e87 100644 --- a/pkg/cmd/exec/exec_test.go +++ b/pkg/cmd/exec/exec_test.go @@ -103,7 +103,7 @@ func TestPodAndContainer(t *testing.T) { { p: &ExecOptions{}, args: []string{"foo", "cmd"}, - argsLenAtDash: -1, + argsLenAtDash: 1, expectedPod: "foo", expectedArgs: []string{"cmd"}, name: "cmd, w/o flags", @@ -112,7 +112,7 @@ func TestPodAndContainer(t *testing.T) { { p: &ExecOptions{}, args: []string{"foo", "cmd"}, - argsLenAtDash: 1, + argsLenAtDash: -1, expectedPod: "foo", expectedArgs: []string{"cmd"}, name: "cmd, cmd is behind dash",