diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index ff6fa41815..afadd6687b 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -485,10 +485,6 @@ _kubectl_exec() flags+=("-t") must_have_one_flag=() - must_have_one_flag+=("--container=") - must_have_one_flag+=("-c") - must_have_one_flag+=("--pod=") - must_have_one_flag+=("-p") must_have_one_noun=() } diff --git a/docs/kubectl_exec.md b/docs/kubectl_exec.md index 6bae1d1b1c..ad236cfe75 100644 --- a/docs/kubectl_exec.md +++ b/docs/kubectl_exec.md @@ -8,17 +8,20 @@ Execute a command in a container. Execute a command in a container. ``` -kubectl exec -p POD -c CONTAINER -- COMMAND [args...] +kubectl exec POD -c CONTAINER -- COMMAND [args...] ``` ### Examples ``` +// get output from running 'date' from pod 123456-7890, using the first container by default +$ kubectl exec 123456-7890 date + // get output from running 'date' in ruby-container from pod 123456-7890 -$ kubectl exec -p 123456-7890 -c ruby-container date +$ kubectl exec 123456-7890 -c ruby-container date //switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod 123456-780 and sends stdout/stderr from 'bash' back to the client -$ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il +$ kubectl exec 123456-7890 -c ruby-container -i -t -- bash -il ``` ### Options @@ -63,6 +66,6 @@ $ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-05-21 10:33:11.186469192 +0000 UTC +###### Auto generated by spf13/cobra at 2015-05-27 22:47:02.898315735 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_exec.md?pixel)]() diff --git a/docs/man/man1/kubectl-exec.1 b/docs/man/man1/kubectl-exec.1 index 927ddfa52f..c06547c818 100644 --- a/docs/man/man1/kubectl-exec.1 +++ b/docs/man/man1/kubectl-exec.1 @@ -141,11 +141,14 @@ Execute a command in a container. .RS .nf +// get output from running 'date' from pod 123456\-7890, using the first container by default +$ kubectl exec 123456\-7890 date + // get output from running 'date' in ruby\-container from pod 123456\-7890 -$ kubectl exec \-p 123456\-7890 \-c ruby\-container date +$ kubectl exec 123456\-7890 \-c ruby\-container date //switch to raw terminal mode, sends stdin to 'bash' in ruby\-container from pod 123456\-780 and sends stdout/stderr from 'bash' back to the client -$ kubectl exec \-p 123456\-7890 \-c ruby\-container \-i \-t \-\- bash \-il +$ kubectl exec 123456\-7890 \-c ruby\-container \-i \-t \-\- bash \-il .fi .RE diff --git a/pkg/kubectl/cmd/exec.go b/pkg/kubectl/cmd/exec.go index e91a4e7b37..5febff45ef 100644 --- a/pkg/kubectl/cmd/exec.go +++ b/pkg/kubectl/cmd/exec.go @@ -32,17 +32,20 @@ import ( ) const ( - exec_example = `// get output from running 'date' in ruby-container from pod 123456-7890 -$ kubectl exec -p 123456-7890 -c ruby-container date + exec_example = `// get output from running 'date' from pod 123456-7890, using the first container by default +$ kubectl exec 123456-7890 date + +// get output from running 'date' in ruby-container from pod 123456-7890 +$ kubectl exec 123456-7890 -c ruby-container date //switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod 123456-780 and sends stdout/stderr from 'bash' back to the client -$ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il` +$ kubectl exec 123456-7890 -c ruby-container -i -t -- bash -il` ) func NewCmdExec(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { params := &execParams{} cmd := &cobra.Command{ - Use: "exec -p POD -c CONTAINER -- COMMAND [args...]", + Use: "exec POD -c CONTAINER -- COMMAND [args...]", Short: "Execute a command in a container.", Long: "Execute a command in a container.", Example: exec_example, @@ -52,10 +55,8 @@ func NewCmdExec(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) * }, } cmd.Flags().StringVarP(¶ms.podName, "pod", "p", "", "Pod name") - cmd.MarkFlagRequired("pod") // TODO support UID cmd.Flags().StringVarP(¶ms.containerName, "container", "c", "", "Container name") - cmd.MarkFlagRequired("container") cmd.Flags().BoolVarP(¶ms.stdin, "stdin", "i", false, "Pass stdin to the container") cmd.Flags().BoolVarP(¶ms.tty, "tty", "t", false, "Stdin is a TTY") return cmd @@ -79,14 +80,27 @@ type execParams struct { tty bool } -func RunExec(f *cmdutil.Factory, cmd *cobra.Command, cmdIn io.Reader, cmdOut, cmdErr io.Writer, p *execParams, args []string, re remoteExecutor) error { - if len(p.podName) == 0 { - return cmdutil.UsageError(cmd, "POD is required for exec") +func extractPodAndContainer(cmd *cobra.Command, args []string, p *execParams) (podName string, containerName string, err error) { + if len(p.podName) == 0 && len(args) == 0 { + return "", "", cmdutil.UsageError(cmd, "POD is required for exec") } + if len(p.podName) != 0 { + printDeprecationWarning("exec POD", "-p POD") + podName = p.podName + if len(args) < 1 { + return "", "", cmdutil.UsageError(cmd, "COMMAND is required for exec") + } + } else { + podName = args[0] + if len(args) < 2 { + return "", "", cmdutil.UsageError(cmd, "COMMAND is required for exec") + } + } + return podName, p.containerName, nil +} - if len(args) < 1 { - return cmdutil.UsageError(cmd, "COMMAND is required for exec") - } +func RunExec(f *cmdutil.Factory, cmd *cobra.Command, cmdIn io.Reader, cmdOut, cmdErr io.Writer, p *execParams, args []string, re remoteExecutor) error { + podName, containerName, err := extractPodAndContainer(cmd, args, p) namespace, err := f.DefaultNamespace() if err != nil { return err @@ -97,17 +111,17 @@ func RunExec(f *cmdutil.Factory, cmd *cobra.Command, cmdIn io.Reader, cmdOut, cm return err } - pod, err := client.Pods(namespace).Get(p.podName) + pod, err := client.Pods(namespace).Get(podName) if err != nil { return err } if pod.Status.Phase != api.PodRunning { - glog.Fatalf("Unable to execute command because pod is not running. Current status=%v", pod.Status.Phase) + glog.Fatalf("Unable to execute command because pod %s is not running. Current status=%v", podName, pod.Status.Phase) } - containerName := p.containerName if len(containerName) == 0 { + glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name) containerName = pod.Spec.Containers[0].Name } diff --git a/pkg/kubectl/cmd/exec_test.go b/pkg/kubectl/cmd/exec_test.go index 766ed72b6c..05e5f9ecb7 100644 --- a/pkg/kubectl/cmd/exec_test.go +++ b/pkg/kubectl/cmd/exec_test.go @@ -39,8 +39,67 @@ func (f *fakeRemoteExecutor) Execute(req *client.Request, config *client.Config, return f.execErr } -func TestExec(t *testing.T) { +func TestPodAndContainer(t *testing.T) { + tests := []struct { + args []string + p *execParams + expectError bool + expectedPod string + expectedContainer string + }{ + { + p: &execParams{}, + expectError: true, + }, + { + p: &execParams{podName: "foo"}, + expectError: true, + }, + { + p: &execParams{podName: "foo", containerName: "bar"}, + expectError: true, + }, + { + p: &execParams{podName: "foo"}, + args: []string{"cmd"}, + expectedPod: "foo", + }, + { + p: &execParams{}, + args: []string{"foo"}, + expectError: true, + }, + { + p: &execParams{}, + args: []string{"foo", "cmd"}, + expectedPod: "foo", + }, + { + p: &execParams{containerName: "bar"}, + args: []string{"foo", "cmd"}, + expectedPod: "foo", + expectedContainer: "bar", + }, + } + for _, test := range tests { + cmd := &cobra.Command{} + podName, containerName, err := extractPodAndContainer(cmd, test.args, test.p) + if podName != test.expectedPod { + t.Errorf("expected: %s, got: %s", test.expectedPod, podName) + } + if containerName != test.expectedContainer { + t.Errorf("expected: %s, got: %s", test.expectedContainer, containerName) + } + if test.expectError && err == nil { + t.Error("unexpected non-error") + } + if !test.expectError && err != nil { + t.Errorf("unexpected error: %v", err) + } + } +} +func TestExec(t *testing.T) { tests := []struct { name, version, podPath, execPath, container string nsInQuery bool diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index 80f8450ea9..5ce43aded1 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -1021,7 +1021,7 @@ func (dm *DockerManager) ExecInContainer(containerId string, cmd []string, stdin } else { if stdin != nil { // Use an os.Pipe here as it returns true *os.File objects. - // This way, if you run 'kubectl exec -p -i bash' (no tty) and type 'exit', + // This way, if you run 'kubectl exec -i bash' (no tty) and type 'exit', // the call below to command.Run() can unblock because its Stdin is the read half // of the pipe. r, w, err := os.Pipe() diff --git a/pkg/kubelet/rkt/rkt.go b/pkg/kubelet/rkt/rkt.go index 37d8f8f77f..ba2da9c58f 100644 --- a/pkg/kubelet/rkt/rkt.go +++ b/pkg/kubelet/rkt/rkt.go @@ -980,7 +980,7 @@ func (r *runtime) ExecInContainer(containerID string, cmd []string, stdin io.Rea } if stdin != nil { // Use an os.Pipe here as it returns true *os.File objects. - // This way, if you run 'kubectl exec -p -i bash' (no tty) and type 'exit', + // This way, if you run 'kubectl exec -i bash' (no tty) and type 'exit', // the call below to command.Run() can unblock because its Stdin is the read half // of the pipe. r, w, err := os.Pipe()