Merge pull request #12220 from brendandburns/stdin

Add interactive run that combines run and attach.
This commit is contained in:
Jerzy Szczepkowski 2015-08-07 08:17:00 +02:00
commit 7a875a1ac0
15 changed files with 162 additions and 15 deletions

View File

@ -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=")

View File

@ -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

View File

@ -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 <name> --image=<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 <name>`
#### docker ps
How do I list what is currently running? Checkout [kubectl get](kubectl/kubectl_get.md).

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_annotate.md?pixel)]()

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_config_view.md?pixel)]()

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_expose.md?pixel)]()

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_get.md?pixel)]()

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_label.md?pixel)]()

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rolling-update.md?pixel)]()

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]()

View File

@ -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))

View File

@ -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{
{

View File

@ -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()
}

View File

@ -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)
}
}

View File

@ -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,
},
},
},