diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 46f5e4a50e..b29dec888f 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -639,6 +639,7 @@ _kubectl_run() flags+=("--port=") flags+=("--replicas=") two_word_flags+=("-r") + flags+=("--restart=") flags+=("--stdin") flags+=("-i") flags+=("--template=") diff --git a/docs/man/man1/kubectl-run.1 b/docs/man/man1/kubectl-run.1 index 8604389559..53b2cccadb 100644 --- a/docs/man/man1/kubectl-run.1 +++ b/docs/man/man1/kubectl-run.1 @@ -27,8 +27,8 @@ Creates a replication controller to manage the created container(s). If true, only print the object that would be sent, without sending it. .PP -\fB\-\-generator\fP="run/v1" - The name of the API generator to use. Default is 'run\-controller/v1'. +\fB\-\-generator\fP="" + The name of the API generator to use. Default is 'run/v1' if \-\-restart=Always, otherwise the default is 'run\-pod/v1'. .PP \fB\-h\fP, \fB\-\-help\fP=false @@ -70,6 +70,10 @@ 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\-\-restart\fP="Always" + The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and \-\-replicas must be 1. Default 'Always' + .PP \fB\-i\fP, \fB\-\-stdin\fP=false Keep stdin open on the container(s) in the pod, even if nothing is attached. diff --git a/docs/user-guide/kubectl/kubectl_annotate.md b/docs/user-guide/kubectl/kubectl_annotate.md index 7f65cbaeaf..e9da5651c4 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-07 03:15:49.183633333 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 19:25:02.005112262 +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 b7e3a23119..873b0a7c5d 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-07 03:15:49.184001914 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 19:25:02.005424182 +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 b7a0001450..3e4f40e2b2 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-07 03:15:49.182748201 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 19:25:02.004335817 +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 808bc7774b..a3e5f810aa 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-07 03:15:49.175798628 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 19:25:01.995301464 +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 9254f16f03..ec05d81f55 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-07 03:15:49.183216488 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 19:25:02.004743482 +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 8264cf619b..6d481e4274 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-07 03:15:49.179537741 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 19:25:01.999165947 +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 21309f2d44..60872be501 100644 --- a/docs/user-guide/kubectl/kubectl_run.md +++ b/docs/user-guide/kubectl/kubectl_run.md @@ -66,7 +66,7 @@ $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { . ``` --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'. + --generator="": The name of the API generator to use. Default is 'run/v1' if --restart=Always, otherwise the default is 'run-pod/v1'. -h, --help[=false]: help for run --hostport=-1: The host port mapping for the container port. To demonstrate a single-machine container. --image="": The image for the container to run. @@ -77,6 +77,7 @@ $ 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. + --restart="Always": The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and --replicas must be 1. Default 'Always' -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. @@ -115,7 +116,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-07 03:15:49.181215106 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 19:25:02.003600767 +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/run.go b/pkg/kubectl/cmd/run.go index 78304276a8..8ae1a62c03 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -61,7 +61,7 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c }, } cmdutil.AddPrinterFlags(cmd) - cmd.Flags().String("generator", "run/v1", "The name of the API generator to use. Default is 'run-controller/v1'.") + cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'run/v1' if --restart=Always, otherwise the default is 'run-pod/v1'.") cmd.Flags().String("image", "", "The image for the container to run.") cmd.MarkFlagRequired("image") cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.") @@ -73,6 +73,7 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c 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.") + cmd.Flags().String("restart", "Always", "The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and --replicas must be 1. Default 'Always'") return cmd } @@ -105,10 +106,24 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob return err } + restartPolicy, err := getRestartPolicy(cmd, interactive) + if err != nil { + return err + } + if restartPolicy != api.RestartPolicyAlways && replicas != 1 { + return cmdutil.UsageError(cmd, fmt.Sprintf("--restart=%s requires that --repliacs=1, found %d", restartPolicy, replicas)) + } generatorName := cmdutil.GetFlagString(cmd, "generator") + if len(generatorName) == 0 { + if restartPolicy == api.RestartPolicyAlways { + generatorName = "run/v1" + } else { + generatorName = "run-pod/v1" + } + } generator, found := f.Generator(generatorName) if !found { - return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not found.", generator)) + return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not found.", generatorName)) } names := generator.ParamNames() params := kubectl.MakeParams(cmd, names) @@ -119,14 +134,20 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob return err } - controller, err := generator.Generate(params) + obj, err := generator.Generate(params) if err != nil { return err } inline := cmdutil.GetFlagString(cmd, "overrides") if len(inline) > 0 { - controller, err = cmdutil.Merge(controller, inline, "ReplicationController") + var objType string + if restartPolicy == api.RestartPolicyAlways { + objType = "ReplicationController" + } else { + objType = "Pod" + } + obj, err = cmdutil.Merge(obj, inline, objType) if err != nil { return err } @@ -134,7 +155,11 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob // TODO: extract this flag to a central location, when such a location exists. if !cmdutil.GetFlagBool(cmd, "dry-run") { - controller, err = client.ReplicationControllers(namespace).Create(controller.(*api.ReplicationController)) + if restartPolicy == api.RestartPolicyAlways { + obj, err = client.ReplicationControllers(namespace).Create(obj.(*api.ReplicationController)) + } else { + obj, err = client.Pods(namespace).Create(obj.(*api.Pod)) + } if err != nil { return err } @@ -168,9 +193,13 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob return err } opts.Client = client - return handleAttach(client, controller.(*api.ReplicationController), opts) + if restartPolicy == api.RestartPolicyAlways { + return handleAttachReplicationController(client, obj.(*api.ReplicationController), opts) + } else { + return handleAttachPod(client, obj.(*api.Pod), opts) + } } - return f.PrintObject(cmd, controller, cmdOut) + return f.PrintObject(cmd, obj, cmdOut) } func waitForPodRunning(c *client.Client, pod *api.Pod, out io.Writer) error { @@ -197,7 +226,7 @@ func waitForPodRunning(c *client.Client, pod *api.Pod, out io.Writer) error { } } -func handleAttach(c *client.Client, controller *api.ReplicationController, opts *AttachOptions) error { +func handleAttachReplicationController(c *client.Client, controller *api.ReplicationController, opts *AttachOptions) error { var pods *api.PodList for pods == nil || len(pods.Items) == 0 { var err error @@ -210,7 +239,10 @@ func handleAttach(c *client.Client, controller *api.ReplicationController, opts } } pod := &pods.Items[0] + return handleAttachPod(c, pod, opts) +} +func handleAttachPod(c *client.Client, pod *api.Pod, opts *AttachOptions) error { if err := waitForPodRunning(c, pod, opts.Out); err != nil { return err } @@ -219,3 +251,24 @@ func handleAttach(c *client.Client, controller *api.ReplicationController, opts opts.Namespace = pod.Namespace return opts.Run() } + +func getRestartPolicy(cmd *cobra.Command, interactive bool) (api.RestartPolicy, error) { + restart := cmdutil.GetFlagString(cmd, "restart") + if len(restart) == 0 { + if interactive { + return api.RestartPolicyOnFailure, nil + } else { + return api.RestartPolicyAlways, nil + } + } + switch api.RestartPolicy(restart) { + case api.RestartPolicyAlways: + return api.RestartPolicyAlways, nil + case api.RestartPolicyOnFailure: + return api.RestartPolicyOnFailure, nil + case api.RestartPolicyNever: + return api.RestartPolicyNever, nil + default: + return "", cmdutil.UsageError(cmd, fmt.Sprintf("invalid restart policy: %s", restart)) + } +} diff --git a/pkg/kubectl/cmd/run_test.go b/pkg/kubectl/cmd/run_test.go new file mode 100644 index 0000000000..56185a5f1a --- /dev/null +++ b/pkg/kubectl/cmd/run_test.go @@ -0,0 +1,80 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "testing" + + "github.com/spf13/cobra" + "k8s.io/kubernetes/pkg/api" +) + +func TestGetRestartPolicy(t *testing.T) { + tests := []struct { + input string + interactive bool + expected api.RestartPolicy + expectErr bool + }{ + { + input: "", + expected: api.RestartPolicyAlways, + }, + { + input: "", + interactive: true, + expected: api.RestartPolicyOnFailure, + }, + { + input: string(api.RestartPolicyAlways), + interactive: true, + expected: api.RestartPolicyAlways, + }, + { + input: string(api.RestartPolicyNever), + interactive: true, + expected: api.RestartPolicyNever, + }, + { + input: string(api.RestartPolicyAlways), + expected: api.RestartPolicyAlways, + }, + { + input: string(api.RestartPolicyNever), + expected: api.RestartPolicyNever, + }, + { + input: "foo", + expectErr: true, + }, + } + for _, test := range tests { + cmd := &cobra.Command{} + cmd.Flags().String("restart", "", "dummy restart flag") + cmd.Flags().Lookup("restart").Value.Set(test.input) + policy, err := getRestartPolicy(cmd, test.interactive) + if test.expectErr && err == nil { + t.Error("unexpected non-error") + } + if !test.expectErr && err != nil { + t.Errorf("unexpected error: %v", err) + } + if !test.expectErr && policy != test.expected { + t.Errorf("expected: %s, saw: %s (%s:%v)", test.expected, policy, test.input, test.interactive) + } + } +} diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index cc4e3380ba..486ad9fc36 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -97,6 +97,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { generators := map[string]kubectl.Generator{ "run/v1": kubectl.BasicReplicationController{}, + "run-pod/v1": kubectl.BasicPod{}, "service/v1": kubectl.ServiceGeneratorV1{}, "service/v2": kubectl.ServiceGeneratorV2{}, } diff --git a/pkg/kubectl/run.go b/pkg/kubectl/run.go index 1f4c1d2f66..5283c0d090 100644 --- a/pkg/kubectl/run.go +++ b/pkg/kubectl/run.go @@ -40,6 +40,30 @@ func (BasicReplicationController) ParamNames() []GeneratorParam { } } +func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) { + stdin, err := GetBool(params, "stdin", false) + if err != nil { + return nil, err + } + + tty, err := GetBool(params, "tty", false) + if err != nil { + return nil, err + } + + spec := api.PodSpec{ + Containers: []api.Container{ + { + Name: name, + Image: params["image"], + Stdin: stdin, + TTY: tty, + }, + }, + } + return &spec, nil +} + func (BasicReplicationController) Generate(params map[string]string) (runtime.Object, error) { name, found := params["name"] if !found || len(name) == 0 { @@ -66,16 +90,11 @@ func (BasicReplicationController) Generate(params map[string]string) (runtime.Ob if err != nil { return nil, err } - stdin, err := GetBool(params, "stdin", false) + + podSpec, err := makePodSpec(params, name) 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, @@ -88,49 +107,119 @@ func (BasicReplicationController) Generate(params map[string]string) (runtime.Ob ObjectMeta: api.ObjectMeta{ Labels: labels, }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: name, - Image: params["image"], - Stdin: stdin, - TTY: tty, - }, - }, - }, + Spec: *podSpec, }, }, } + if err := updatePodPorts(params, &controller.Spec.Template.Spec); err != nil { + return nil, err + } + return &controller, nil +} +func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error) { port := -1 hostPort := -1 if len(params["port"]) > 0 { port, err = strconv.Atoi(params["port"]) if err != nil { - return nil, err + return err } } if len(params["hostport"]) > 0 { hostPort, err = strconv.Atoi(params["hostport"]) if err != nil { - return nil, err + return err } if hostPort > 0 && port < 0 { - return nil, fmt.Errorf("--hostport requires --port to be specified") + return fmt.Errorf("--hostport requires --port to be specified") } } // Don't include the port if it was not specified. if port > 0 { - controller.Spec.Template.Spec.Containers[0].Ports = []api.ContainerPort{ + podSpec.Containers[0].Ports = []api.ContainerPort{ { ContainerPort: port, }, } if hostPort > 0 { - controller.Spec.Template.Spec.Containers[0].Ports[0].HostPort = hostPort + podSpec.Containers[0].Ports[0].HostPort = hostPort } } - return &controller, nil + return nil +} + +type BasicPod struct{} + +func (BasicPod) ParamNames() []GeneratorParam { + return []GeneratorParam{ + {"labels", false}, + {"default-name", false}, + {"name", true}, + {"image", true}, + {"port", false}, + {"hostport", false}, + {"stdin", false}, + {"tty", false}, + {"restart", false}, + } +} + +func (BasicPod) Generate(params map[string]string) (runtime.Object, error) { + name, found := params["name"] + if !found || len(name) == 0 { + name, found = params["default-name"] + if !found || len(name) == 0 { + return nil, fmt.Errorf("'name' is a required parameter.") + } + } + // TODO: extract this flag to a central location. + labelString, found := params["labels"] + var labels map[string]string + var err error + if found && len(labelString) > 0 { + labels, err = ParseLabels(labelString) + 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 + } + + restartPolicy := api.RestartPolicy(params["restart"]) + if len(restartPolicy) == 0 { + restartPolicy = api.RestartPolicyAlways + } + pod := api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: name, + Labels: labels, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: name, + Image: params["image"], + ImagePullPolicy: api.PullIfNotPresent, + Stdin: stdin, + TTY: tty, + }, + }, + DNSPolicy: api.DNSClusterFirst, + RestartPolicy: restartPolicy, + }, + } + if err := updatePodPorts(params, &pod.Spec); err != nil { + return nil, err + } + return &pod, nil } diff --git a/pkg/kubectl/run_test.go b/pkg/kubectl/run_test.go index 886f5b008e..65ca8f32be 100644 --- a/pkg/kubectl/run_test.go +++ b/pkg/kubectl/run_test.go @@ -190,3 +190,140 @@ func TestGenerate(t *testing.T) { } } } + +func TestGeneratePod(t *testing.T) { + tests := []struct { + params map[string]string + expected *api.Pod + expectErr bool + }{ + { + params: map[string]string{ + "name": "foo", + "image": "someimage", + "port": "-1", + }, + expected: &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullIfNotPresent, + }, + }, + DNSPolicy: api.DNSClusterFirst, + RestartPolicy: api.RestartPolicyAlways, + }, + }, + }, + { + params: map[string]string{ + "name": "foo", + "image": "someimage", + "port": "80", + }, + expected: &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullIfNotPresent, + Ports: []api.ContainerPort{ + { + ContainerPort: 80, + }, + }, + }, + }, + DNSPolicy: api.DNSClusterFirst, + RestartPolicy: api.RestartPolicyAlways, + }, + }, + }, + { + params: map[string]string{ + "name": "foo", + "image": "someimage", + "port": "80", + "hostport": "80", + }, + expected: &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullIfNotPresent, + Ports: []api.ContainerPort{ + { + ContainerPort: 80, + HostPort: 80, + }, + }, + }, + }, + DNSPolicy: api.DNSClusterFirst, + RestartPolicy: api.RestartPolicyAlways, + }, + }, + }, + { + params: map[string]string{ + "name": "foo", + "image": "someimage", + "hostport": "80", + }, + expected: nil, + expectErr: true, + }, + { + params: map[string]string{ + "name": "foo", + "image": "someimage", + "replicas": "1", + "labels": "foo=bar,baz=blah", + }, + expected: &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Labels: map[string]string{"foo": "bar", "baz": "blah"}, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "foo", + Image: "someimage", + ImagePullPolicy: api.PullIfNotPresent, + }, + }, + DNSPolicy: api.DNSClusterFirst, + RestartPolicy: api.RestartPolicyAlways, + }, + }, + }, + } + generator := BasicPod{} + for _, test := range tests { + obj, err := generator.Generate(test.params) + if !test.expectErr && err != nil { + t.Errorf("unexpected error: %v", err) + } + if test.expectErr && err != nil { + continue + } + if !reflect.DeepEqual(obj.(*api.Pod), test.expected) { + t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Pod)) + } + } +} diff --git a/test/e2e/kubectl.go b/test/e2e/kubectl.go index 6a21e668b3..d6d6b27a4a 100644 --- a/test/e2e/kubectl.go +++ b/test/e2e/kubectl.go @@ -485,7 +485,7 @@ var _ = Describe("Kubectl client", func() { }) }) - Describe("Kubectl run", func() { + Describe("Kubectl run rc", func() { var nsFlag string var rcName string @@ -528,6 +528,59 @@ var _ = Describe("Kubectl client", func() { }) + Describe("Kubectl run pod", func() { + var nsFlag string + var podName string + + BeforeEach(func() { + nsFlag = fmt.Sprintf("--namespace=%v", ns) + podName = "e2e-test-nginx-pod" + }) + + AfterEach(func() { + runKubectl("stop", "pods", podName, nsFlag) + }) + + It("should create a pod from an image when restart is OnFailure", func() { + image := "nginx" + + By("running the image " + image) + runKubectl("run", podName, "--restart=OnFailure", "--image="+image, nsFlag) + By("verifying the pod " + podName + " was created") + pod, err := c.Pods(ns).Get(podName) + if err != nil { + Failf("Failed getting pod %s: %v", podName, err) + } + containers := pod.Spec.Containers + if containers == nil || len(containers) != 1 || containers[0].Image != image { + Failf("Failed creating pod %s for 1 pod with expected image %s", podName, image) + } + if pod.Spec.RestartPolicy != api.RestartPolicyOnFailure { + Failf("Failed creating a pod with correct restart policy for --restart=OnFailure") + } + }) + + It("should create a pod from an image when restart is Never", func() { + image := "nginx" + + By("running the image " + image) + runKubectl("run", podName, "--restart=Never", "--image="+image, nsFlag) + By("verifying the pod " + podName + " was created") + pod, err := c.Pods(ns).Get(podName) + if err != nil { + Failf("Failed getting pod %s: %v", podName, err) + } + containers := pod.Spec.Containers + if containers == nil || len(containers) != 1 || containers[0].Image != image { + Failf("Failed creating pod %s for 1 pod with expected image %s", podName, image) + } + if pod.Spec.RestartPolicy != api.RestartPolicyNever { + Failf("Failed creating a pod with correct restart policy for --restart=OnFailure") + } + }) + + }) + Describe("Proxy server", func() { // TODO: test proxy options (static, prefix, etc) It("should support proxy with --port 0", func() {