diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index bfb00cfa5c8..a9a67a7156f 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -616,6 +616,7 @@ _kubectl_run() flags_with_completion=() flags_completion=() + flags+=("--attach") flags+=("--dry-run") flags+=("--generator=") flags+=("--help") @@ -632,8 +633,11 @@ _kubectl_run() flags+=("--port=") flags+=("--replicas=") two_word_flags+=("-r") + flags+=("--stdin") + flags+=("-i") flags+=("--template=") two_word_flags+=("-t") + flags+=("--tty") must_have_one_flag=() must_have_one_flag+=("--image=") diff --git a/docs/man/man1/kubectl-run.1 b/docs/man/man1/kubectl-run.1 index 7216949bda0..8604389559f 100644 --- a/docs/man/man1/kubectl-run.1 +++ b/docs/man/man1/kubectl-run.1 @@ -18,6 +18,10 @@ Creates a replication controller to manage the created container(s). .SH OPTIONS +.PP +\fB\-\-attach\fP=false + If true, wait for the Pod to start running, and then attach to the Pod as if 'kubectl attach ...' were called. Default false, unless '\-i/\-\-interactive' is set, in which case the default is true. + .PP \fB\-\-dry\-run\fP=false If true, only print the object that would be sent, without sending it. @@ -66,11 +70,19 @@ Creates a replication controller to manage the created container(s). \fB\-r\fP, \fB\-\-replicas\fP=1 Number of replicas to create for this container. Default is 1. +.PP +\fB\-i\fP, \fB\-\-stdin\fP=false + Keep stdin open on the container(s) in the pod, even if nothing is attached. + .PP \fB\-t\fP, \fB\-\-template\fP="" Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [ \[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] +.PP +\fB\-\-tty\fP=false + Allocated a TTY for each container in the pod. Because \-t is currently shorthand for \-\-template, \-t is not supported for \-\-tty. This shorthand is deprecated and we expect to adopt \-t for \-\-tty soon. + .SH OPTIONS INHERITED FROM PARENT COMMANDS .PP diff --git a/docs/user-guide/docker-cli-to-kubectl.md b/docs/user-guide/docker-cli-to-kubectl.md index ebdae76c707..a73b409347e 100644 --- a/docs/user-guide/docker-cli-to-kubectl.md +++ b/docs/user-guide/docker-cli-to-kubectl.md @@ -80,6 +80,17 @@ nginx-http run=nginx-app run=nginx-app 80/TCP With kubectl, we create a [replication controller](replication-controller.md) which will make sure that N pods are running nginx (where N is the number of replicas stated in the spec, which defaults to 1). We also create a [service](services.md) with a selector that matches the replication controller's selector. See the [Quick start](quick-start.md) for more information. +By default images are run in the background, similar to `docker run -d ...`, if you want to run things in the foreground, use: + +```console +kubectl run [-i] [--tty] --attach --image= +``` + +Unlike `docker run ...`, if `--attach` is specified, we attach to `stdin`, `stdout` and `stderr`, there is no ability to control which streams are attached (`docker -a ...`). + +Because we start a replication controller for your container, it will be restarted if you terminate the attached process (e.g. `ctrl-c`), this is different than `docker run -it`. +To destroy the replication controller (and it's pods) you need to run `kubectl delete rc ` + #### docker ps How do I list what is currently running? Checkout [kubectl get](kubectl/kubectl_get.md). diff --git a/docs/user-guide/kubectl/kubectl_annotate.md b/docs/user-guide/kubectl/kubectl_annotate.md index b15e4c37442..7f65cbaeaf3 100644 --- a/docs/user-guide/kubectl/kubectl_annotate.md +++ b/docs/user-guide/kubectl/kubectl_annotate.md @@ -121,7 +121,7 @@ $ kubectl annotate pods foo description- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 14:22:30.875870257 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 03:15:49.183633333 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_annotate.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_config_view.md b/docs/user-guide/kubectl/kubectl_config_view.md index 7e3665c986e..b7e3a23119b 100644 --- a/docs/user-guide/kubectl/kubectl_config_view.md +++ b/docs/user-guide/kubectl/kubectl_config_view.md @@ -103,7 +103,7 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-08-05 14:22:30.876088386 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 03:15:49.184001914 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_config_view.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_expose.md b/docs/user-guide/kubectl/kubectl_expose.md index 032c55e3554..b7a00014503 100644 --- a/docs/user-guide/kubectl/kubectl_expose.md +++ b/docs/user-guide/kubectl/kubectl_expose.md @@ -118,7 +118,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 23:27:50.888628432 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 03:15:49.182748201 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_expose.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_get.md b/docs/user-guide/kubectl/kubectl_get.md index e92a80a9a49..808bc7774bd 100644 --- a/docs/user-guide/kubectl/kubectl_get.md +++ b/docs/user-guide/kubectl/kubectl_get.md @@ -125,7 +125,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 23:27:50.884869862 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 03:15:49.175798628 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_get.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_label.md b/docs/user-guide/kubectl/kubectl_label.md index 41ef38093d2..9254f16f035 100644 --- a/docs/user-guide/kubectl/kubectl_label.md +++ b/docs/user-guide/kubectl/kubectl_label.md @@ -115,7 +115,7 @@ $ kubectl label pods foo bar- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 23:27:50.888803859 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 03:15:49.183216488 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_label.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_rolling-update.md b/docs/user-guide/kubectl/kubectl_rolling-update.md index 9ebf18efe10..8264cf619b7 100644 --- a/docs/user-guide/kubectl/kubectl_rolling-update.md +++ b/docs/user-guide/kubectl/kubectl_rolling-update.md @@ -117,7 +117,7 @@ $ kubectl rolling-update frontend --image=image:v2 * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 23:28:32.886715844 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 03:15:49.179537741 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rolling-update.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_run.md b/docs/user-guide/kubectl/kubectl_run.md index e0945a2b6c9..21309f2d442 100644 --- a/docs/user-guide/kubectl/kubectl_run.md +++ b/docs/user-guide/kubectl/kubectl_run.md @@ -64,6 +64,7 @@ $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { . ### Options ``` + --attach[=false]: If true, wait for the Pod to start running, and then attach to the Pod as if 'kubectl attach ...' were called. Default false, unless '-i/--interactive' is set, in which case the default is true. --dry-run[=false]: If true, only print the object that would be sent, without sending it. --generator="run/v1": The name of the API generator to use. Default is 'run-controller/v1'. -h, --help[=false]: help for run @@ -76,7 +77,9 @@ $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { . --overrides="": An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. --port=-1: The port that this container exposes. -r, --replicas=1: Number of replicas to create for this container. Default is 1. + -i, --stdin[=false]: Keep stdin open on the container(s) in the pod, even if nothing is attached. -t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview] + --tty[=false]: Allocated a TTY for each container in the pod. Because -t is currently shorthand for --template, -t is not supported for --tty. This shorthand is deprecated and we expect to adopt -t for --tty soon. ``` ### Options inherited from parent commands @@ -112,7 +115,7 @@ $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { . * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 14:22:30.874977162 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 03:15:49.181215106 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]() diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index f4725647023..a36c3cefa38 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -141,7 +141,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, cmds.AddCommand(NewCmdPortForward(f)) cmds.AddCommand(NewCmdProxy(f, out)) - cmds.AddCommand(NewCmdRun(f, out)) + cmds.AddCommand(NewCmdRun(f, in, out, err)) cmds.AddCommand(NewCmdStop(f, out)) cmds.AddCommand(NewCmdExposeService(f, out)) diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 2823337e7c6..2c80d24ce22 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -261,7 +261,7 @@ func ExamplePrintReplicationControllerWithNamespace() { Codec: codec, Client: nil, } - cmd := NewCmdRun(f, os.Stdout) + cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) ctrl := &api.ReplicationController{ ObjectMeta: api.ObjectMeta{ Name: "foo", @@ -303,7 +303,7 @@ func ExamplePrintPodWithWideFormat() { Client: nil, } nodeName := "kubernetes-minion-abcd" - cmd := NewCmdRun(f, os.Stdout) + cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "test1", @@ -337,7 +337,7 @@ func ExamplePrintServiceWithNamespacesAndLabels() { Codec: codec, Client: nil, } - cmd := NewCmdRun(f, os.Stdout) + cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) svc := &api.ServiceList{ Items: []api.Service{ { diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index aba473a5186..78304276a87 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -20,11 +20,15 @@ import ( "fmt" "io" "os" + "time" "github.com/spf13/cobra" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client" + "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/labels" ) const ( @@ -43,7 +47,7 @@ $ kubectl run nginx --image=nginx --dry-run $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }'` ) -func NewCmdRun(f *cmdutil.Factory, out io.Writer) *cobra.Command { +func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "run NAME --image=image [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json]", // run-container is deprecated @@ -52,7 +56,7 @@ func NewCmdRun(f *cmdutil.Factory, out io.Writer) *cobra.Command { Long: run_long, Example: run_example, Run: func(cmd *cobra.Command, args []string) { - err := Run(f, out, cmd, args) + err := Run(f, cmdIn, cmdOut, cmdErr, cmd, args) cmdutil.CheckErr(err) }, } @@ -66,10 +70,13 @@ func NewCmdRun(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Int("port", -1, "The port that this container exposes.") cmd.Flags().Int("hostport", -1, "The host port mapping for the container port. To demonstrate a single-machine container.") cmd.Flags().StringP("labels", "l", "", "Labels to apply to the pod(s).") + cmd.Flags().BoolP("stdin", "i", false, "Keep stdin open on the container(s) in the pod, even if nothing is attached.") + cmd.Flags().Bool("tty", false, "Allocated a TTY for each container in the pod. Because -t is currently shorthand for --template, -t is not supported for --tty. This shorthand is deprecated and we expect to adopt -t for --tty soon.") + cmd.Flags().Bool("attach", false, "If true, wait for the Pod to start running, and then attach to the Pod as if 'kubectl attach ...' were called. Default false, unless '-i/--interactive' is set, in which case the default is true.") return cmd } -func Run(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { +func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string) error { if len(os.Args) > 1 && os.Args[1] == "run-container" { printDeprecationWarning("run", "run-container") } @@ -78,6 +85,16 @@ func Run(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) e return cmdutil.UsageError(cmd, "NAME is required for run") } + interactive := cmdutil.GetFlagBool(cmd, "stdin") + tty := cmdutil.GetFlagBool(cmd, "tty") + if tty && !interactive { + return cmdutil.UsageError(cmd, "-i/--stdin is required for containers with --tty=true") + } + replicas := cmdutil.GetFlagInt(cmd, "replicas") + if interactive && replicas != 1 { + return cmdutil.UsageError(cmd, fmt.Sprintf("-i/--stdin requires that replicas is 1, found %d", replicas)) + } + namespace, _, err := f.DefaultNamespace() if err != nil { return err @@ -123,5 +140,82 @@ func Run(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) e } } - return f.PrintObject(cmd, controller, out) + attachFlag := cmd.Flags().Lookup("attach") + attach := cmdutil.GetFlagBool(cmd, "attach") + + if !attachFlag.Changed && interactive { + attach = true + } + + if attach { + opts := &AttachOptions{ + In: cmdIn, + Out: cmdOut, + Err: cmdErr, + Stdin: interactive, + TTY: tty, + + Attach: &DefaultRemoteAttach{}, + } + config, err := f.ClientConfig() + if err != nil { + return err + } + opts.Config = config + + client, err := f.Client() + if err != nil { + return err + } + opts.Client = client + return handleAttach(client, controller.(*api.ReplicationController), opts) + } + return f.PrintObject(cmd, controller, cmdOut) +} + +func waitForPodRunning(c *client.Client, pod *api.Pod, out io.Writer) error { + for { + pod, err := c.Pods(pod.Namespace).Get(pod.Name) + if err != nil { + return err + } + if pod.Status.Phase == api.PodRunning { + ready := true + for _, status := range pod.Status.ContainerStatuses { + if !status.Ready { + ready = false + break + } + } + if ready { + return nil + } + } + fmt.Fprintf(out, "Waiting for pod %s/%s to be running\n", pod.Namespace, pod.Name) + time.Sleep(2 * time.Second) + continue + } +} + +func handleAttach(c *client.Client, controller *api.ReplicationController, opts *AttachOptions) error { + var pods *api.PodList + for pods == nil || len(pods.Items) == 0 { + var err error + if pods, err = c.Pods(controller.Namespace).List(labels.SelectorFromSet(controller.Spec.Selector), fields.Everything()); err != nil { + return err + } + if len(pods.Items) == 0 { + fmt.Fprint(opts.Out, "Waiting for pod to be scheduled\n") + time.Sleep(2 * time.Second) + } + } + pod := &pods.Items[0] + + if err := waitForPodRunning(c, pod, opts.Out); err != nil { + return err + } + opts.Client = c + opts.PodName = pod.Name + opts.Namespace = pod.Namespace + return opts.Run() } diff --git a/pkg/kubectl/generate.go b/pkg/kubectl/generate.go index ab8a3c9c3bb..c360f2ec4dd 100644 --- a/pkg/kubectl/generate.go +++ b/pkg/kubectl/generate.go @@ -18,6 +18,7 @@ package kubectl import ( "fmt" + "strconv" "strings" "github.com/spf13/cobra" @@ -88,3 +89,11 @@ func ParseLabels(labelString string) (map[string]string, error) { } return labels, nil } + +func GetBool(params map[string]string, key string, defValue bool) (bool, error) { + if val, found := params[key]; !found { + return defValue, nil + } else { + return strconv.ParseBool(val) + } +} diff --git a/pkg/kubectl/run.go b/pkg/kubectl/run.go index 90195cc90a3..1f4c1d2f661 100644 --- a/pkg/kubectl/run.go +++ b/pkg/kubectl/run.go @@ -35,6 +35,8 @@ func (BasicReplicationController) ParamNames() []GeneratorParam { {"image", true}, {"port", false}, {"hostport", false}, + {"stdin", false}, + {"tty", false}, } } @@ -64,6 +66,16 @@ func (BasicReplicationController) Generate(params map[string]string) (runtime.Ob if err != nil { return nil, err } + stdin, err := GetBool(params, "stdin", false) + if err != nil { + return nil, err + } + + tty, err := GetBool(params, "tty", false) + if err != nil { + return nil, err + } + controller := api.ReplicationController{ ObjectMeta: api.ObjectMeta{ Name: name, @@ -81,6 +93,8 @@ func (BasicReplicationController) Generate(params map[string]string) (runtime.Ob { Name: name, Image: params["image"], + Stdin: stdin, + TTY: tty, }, }, },