Add optional arguments to kubectl run ...

This commit is contained in:
Brendan Burns 2015-08-11 22:48:00 -07:00
parent a6148e79c3
commit 586931fe16
12 changed files with 305 additions and 57 deletions

View File

@ -630,6 +630,7 @@ _kubectl_run()
flags_completion=() flags_completion=()
flags+=("--attach") flags+=("--attach")
flags+=("--command")
flags+=("--dry-run") flags+=("--dry-run")
flags+=("--generator=") flags+=("--generator=")
flags+=("--help") flags+=("--help")

View File

@ -22,6 +22,10 @@ Creates a replication controller to manage the created container(s).
\fB\-\-attach\fP=false \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. 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\-\-command\fP=false
If true and extra arguments are present, use them as the 'command' field in the container, rather than the 'args' field which is the default.
.PP .PP
\fB\-\-dry\-run\fP=false \fB\-\-dry\-run\fP=false
If true, only print the object that would be sent, without sending it. If true, only print the object that would be sent, without sending it.
@ -207,6 +211,15 @@ $ kubectl run nginx \-\-image=nginx \-\-dry\-run
# Start a single instance of nginx, but overload the spec of the replication controller with a partial set of values parsed from JSON. # Start a single instance of nginx, but overload the spec of the replication controller with a partial set of values parsed from JSON.
$ kubectl run nginx \-\-image=nginx \-\-overrides='{ "apiVersion": "v1", "spec": { ... } }' $ kubectl run nginx \-\-image=nginx \-\-overrides='{ "apiVersion": "v1", "spec": { ... } }'
# Start a single instance of nginx and keep it in the foreground, don't restart it if it exits.
$ kubectl run \-i \-tty nginx \-\-image=nginx \-\-restart=Never
# Start the nginx container using the default command, but use custom arguments (arg1 .. argN) for that command.
$ kubectl run nginx \-\-image=nginx \-\- <arg1> <arg2> ... <argN>
# Start the nginx container using a different command and custom arguments
$ kubectl run nginx \-\-image=nginx \-\-command \-\- <cmd> <arg1> ... <argN>
.fi .fi
.RE .RE

View File

@ -59,12 +59,22 @@ $ kubectl run nginx --image=nginx --dry-run
# Start a single instance of nginx, but overload the spec of the replication controller with a partial set of values parsed from JSON. # Start a single instance of nginx, but overload the spec of the replication controller with a partial set of values parsed from JSON.
$ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }' $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }'
# Start a single instance of nginx and keep it in the foreground, don't restart it if it exits.
$ kubectl run -i -tty nginx --image=nginx --restart=Never
# Start the nginx container using the default command, but use custom arguments (arg1 .. argN) for that command.
$ kubectl run nginx --image=nginx -- <arg1> <arg2> ... <argN>
# Start the nginx container using a different command and custom arguments
$ kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
``` ```
### Options ### 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. --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.
--command[=false]: If true and extra arguments are present, use them as the 'command' field in the container, rather than the 'args' field which is the default.
--dry-run[=false]: If true, only print the object that would be sent, without sending it. --dry-run[=false]: If true, only print the object that would be sent, without sending it.
--generator="": The name of the API generator to use. Default is 'run/v1' if --restart=Always, otherwise the default is 'run-pod/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 -h, --help[=false]: help for run
@ -117,7 +127,7 @@ $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { .
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.307766241 +0000 UTC ###### Auto generated by spf13/cobra at 2015-08-13 16:41:44.465440991 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS --> <!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]()

View File

@ -127,8 +127,8 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
names := generator.ParamNames() names := generator.ParamNames()
params := kubectl.MakeParams(cmd, names) params := kubectl.MakeParams(cmd, names)
params["default-name"] = info.Name params["default-name"] = info.Name
if s, found := params["selector"]; !found || len(s) == 0 || cmdutil.GetFlagInt(cmd, "port") < 1 { if s, found := params["selector"]; !found || kubectl.IsZero(s) || cmdutil.GetFlagInt(cmd, "port") < 1 {
if len(s) == 0 { if kubectl.IsZero(s) {
s, err := f.PodSelectorForObject(inputObject) s, err := f.PodSelectorForObject(inputObject)
if err != nil { if err != nil {
return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find selectors via --selector flag or introspection: %s", err)) return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find selectors via --selector flag or introspection: %s", err))
@ -160,7 +160,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") { if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") {
params["create-external-load-balancer"] = "true" params["create-external-load-balancer"] = "true"
} }
if len(params["labels"]) == 0 { if kubectl.IsZero(params["labels"]) {
labels, err := f.LabelsForObject(inputObject) labels, err := f.LabelsForObject(inputObject)
if err != nil { if err != nil {
return err return err

View File

@ -30,16 +30,50 @@ import (
func TestRunExposeService(t *testing.T) { func TestRunExposeService(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args []string args []string
ns string ns string
calls map[string]string calls map[string]string
input runtime.Object input runtime.Object
flags map[string]string flags map[string]string
output runtime.Object output runtime.Object
expected string expected string
status int status int
podSelector string
}{ }{
{
name: "expose-service-from-service-no-selector",
args: []string{"service", "baz"},
ns: "test",
calls: map[string]string{
"GET": "/namespaces/test/services/baz",
"POST": "/namespaces/test/services",
},
input: &api.Service{
ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"},
Spec: api.ServiceSpec{
Selector: map[string]string{"app": "go"},
},
},
podSelector: "app=go",
flags: map[string]string{"protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"},
output: &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "12", Labels: map[string]string{"svc": "test"}},
TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"},
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{
Name: "default",
Protocol: api.Protocol("UDP"),
Port: 14,
},
},
Selector: map[string]string{"app": "go"},
},
},
status: 200,
},
{ {
name: "expose-service-from-service", name: "expose-service-from-service",
args: []string{"service", "baz"}, args: []string{"service", "baz"},
@ -194,6 +228,8 @@ func TestRunExposeService(t *testing.T) {
}), }),
} }
tf.Namespace = test.ns tf.Namespace = test.ns
f.PodSelectorForObject = func(obj runtime.Object) (string, error) { return test.podSelector, nil }
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdExposeService(f, buf) cmd := NewCmdExposeService(f, buf)

View File

@ -44,7 +44,16 @@ $ kubectl run nginx --image=nginx --replicas=5
$ kubectl run nginx --image=nginx --dry-run $ kubectl run nginx --image=nginx --dry-run
# Start a single instance of nginx, but overload the spec of the replication controller with a partial set of values parsed from JSON. # Start a single instance of nginx, but overload the spec of the replication controller with a partial set of values parsed from JSON.
$ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }'` $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }'
# Start a single instance of nginx and keep it in the foreground, don't restart it if it exits.
$ kubectl run -i -tty nginx --image=nginx --restart=Never
# Start the nginx container using the default command, but use custom arguments (arg1 .. argN) for that command.
$ kubectl run nginx --image=nginx -- <arg1> <arg2> ... <argN>
# Start the nginx container using a different command and custom arguments
$ kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>`
) )
func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
@ -74,6 +83,7 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
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("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().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'") 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'")
cmd.Flags().Bool("command", false, "If true and extra arguments are present, use them as the 'command' field in the container, rather than the 'args' field which is the default.")
return cmd return cmd
} }
@ -82,7 +92,7 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob
printDeprecationWarning("run", "run-container") printDeprecationWarning("run", "run-container")
} }
if len(args) != 1 { if len(args) == 0 {
return cmdutil.UsageError(cmd, "NAME is required for run") return cmdutil.UsageError(cmd, "NAME is required for run")
} }
@ -128,7 +138,9 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob
names := generator.ParamNames() names := generator.ParamNames()
params := kubectl.MakeParams(cmd, names) params := kubectl.MakeParams(cmd, names)
params["name"] = args[0] params["name"] = args[0]
if len(args) > 1 {
params["args"] = args[1:]
}
err = kubectl.ValidateParams(names, params) err = kubectl.ValidateParams(names, params)
if err != nil { if err != nil {
return err return err

View File

@ -18,6 +18,7 @@ package kubectl
import ( import (
"fmt" "fmt"
"reflect"
"strconv" "strconv"
"strings" "strings"
@ -35,17 +36,24 @@ type GeneratorParam struct {
// Generator is an interface for things that can generate API objects from input parameters. // Generator is an interface for things that can generate API objects from input parameters.
type Generator interface { type Generator interface {
// Generate creates an API object given a set of parameters // Generate creates an API object given a set of parameters
Generate(params map[string]string) (runtime.Object, error) Generate(params map[string]interface{}) (runtime.Object, error)
// ParamNames returns the list of parameters that this generator uses // ParamNames returns the list of parameters that this generator uses
ParamNames() []GeneratorParam ParamNames() []GeneratorParam
} }
func IsZero(i interface{}) bool {
if i == nil {
return true
}
return reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface())
}
// ValidateParams ensures that all required params are present in the params map // ValidateParams ensures that all required params are present in the params map
func ValidateParams(paramSpec []GeneratorParam, params map[string]string) error { func ValidateParams(paramSpec []GeneratorParam, params map[string]interface{}) error {
for ix := range paramSpec { for ix := range paramSpec {
if paramSpec[ix].Required { if paramSpec[ix].Required {
value, found := params[paramSpec[ix].Name] value, found := params[paramSpec[ix].Name]
if !found || len(value) == 0 { if !found || IsZero(value) {
return fmt.Errorf("Parameter: %s is required", paramSpec[ix].Name) return fmt.Errorf("Parameter: %s is required", paramSpec[ix].Name)
} }
} }
@ -54,8 +62,8 @@ func ValidateParams(paramSpec []GeneratorParam, params map[string]string) error
} }
// MakeParams is a utility that creates generator parameters from a command line // MakeParams is a utility that creates generator parameters from a command line
func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]string { func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]interface{} {
result := map[string]string{} result := map[string]interface{}{}
for ix := range params { for ix := range params {
f := cmd.Flags().Lookup(params[ix].Name) f := cmd.Flags().Lookup(params[ix].Name)
if f != nil { if f != nil {
@ -74,7 +82,11 @@ func MakeLabels(labels map[string]string) string {
} }
// ParseLabels turns a string representation of a label set into a map[string]string // ParseLabels turns a string representation of a label set into a map[string]string
func ParseLabels(labelString string) (map[string]string, error) { func ParseLabels(labelSpec interface{}) (map[string]string, error) {
labelString, isString := labelSpec.(string)
if !isString {
return nil, fmt.Errorf("expected string, found %v", labelSpec)
}
if len(labelString) == 0 { if len(labelString) == 0 {
return nil, fmt.Errorf("no label spec passed") return nil, fmt.Errorf("no label spec passed")
} }

View File

@ -23,29 +23,55 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type TestStruct struct {
val int
}
func TestIsZero(t *testing.T) {
tests := []struct {
val interface{}
expectZero bool
}{
{"", true},
{nil, true},
{0, true},
{TestStruct{}, true},
{"foo", false},
{1, false},
{TestStruct{val: 2}, false},
}
for _, test := range tests {
output := IsZero(test.val)
if output != test.expectZero {
t.Errorf("expected: %v, saw %v", test.expectZero, output)
}
}
}
func TestValidateParams(t *testing.T) { func TestValidateParams(t *testing.T) {
tests := []struct { tests := []struct {
paramSpec []GeneratorParam paramSpec []GeneratorParam
params map[string]string params map[string]interface{}
valid bool valid bool
}{ }{
{ {
paramSpec: []GeneratorParam{}, paramSpec: []GeneratorParam{},
params: map[string]string{}, params: map[string]interface{}{},
valid: true, valid: true,
}, },
{ {
paramSpec: []GeneratorParam{ paramSpec: []GeneratorParam{
{Name: "foo"}, {Name: "foo"},
}, },
params: map[string]string{}, params: map[string]interface{}{},
valid: true, valid: true,
}, },
{ {
paramSpec: []GeneratorParam{ paramSpec: []GeneratorParam{
{Name: "foo", Required: true}, {Name: "foo", Required: true},
}, },
params: map[string]string{ params: map[string]interface{}{
"foo": "bar", "foo": "bar",
}, },
valid: true, valid: true,
@ -54,7 +80,7 @@ func TestValidateParams(t *testing.T) {
paramSpec: []GeneratorParam{ paramSpec: []GeneratorParam{
{Name: "foo", Required: true}, {Name: "foo", Required: true},
}, },
params: map[string]string{ params: map[string]interface{}{
"baz": "blah", "baz": "blah",
"foo": "bar", "foo": "bar",
}, },
@ -65,7 +91,7 @@ func TestValidateParams(t *testing.T) {
{Name: "foo", Required: true}, {Name: "foo", Required: true},
{Name: "baz", Required: true}, {Name: "baz", Required: true},
}, },
params: map[string]string{ params: map[string]interface{}{
"baz": "blah", "baz": "blah",
"foo": "bar", "foo": "bar",
}, },
@ -76,7 +102,7 @@ func TestValidateParams(t *testing.T) {
{Name: "foo", Required: true}, {Name: "foo", Required: true},
{Name: "baz", Required: true}, {Name: "baz", Required: true},
}, },
params: map[string]string{ params: map[string]interface{}{
"foo": "bar", "foo": "bar",
}, },
valid: false, valid: false,
@ -103,7 +129,7 @@ func TestMakeParams(t *testing.T) {
{Name: "foo", Required: true}, {Name: "foo", Required: true},
{Name: "baz", Required: true}, {Name: "baz", Required: true},
} }
expected := map[string]string{ expected := map[string]interface{}{
"foo": "bar", "foo": "bar",
"baz": "blah", "baz": "blah",
} }

View File

@ -37,6 +37,8 @@ func (BasicReplicationController) ParamNames() []GeneratorParam {
{"hostport", false}, {"hostport", false},
{"stdin", false}, {"stdin", false},
{"tty", false}, {"tty", false},
{"command", false},
{"args", false},
} }
} }
@ -64,7 +66,25 @@ func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
return &spec, nil return &spec, nil
} }
func (BasicReplicationController) Generate(params map[string]string) (runtime.Object, error) { func (BasicReplicationController) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
args := []string{}
val, found := genericParams["args"]
if found {
var isArray bool
args, isArray = val.([]string)
if !isArray {
return nil, fmt.Errorf("expected []string, found: %v", val)
}
delete(genericParams, "args")
}
params := map[string]string{}
for key, value := range genericParams {
strVal, isString := value.(string)
if !isString {
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
}
params[key] = strVal
}
name, found := params["name"] name, found := params["name"]
if !found || len(name) == 0 { if !found || len(name) == 0 {
name, found = params["default-name"] name, found = params["default-name"]
@ -95,6 +115,18 @@ func (BasicReplicationController) Generate(params map[string]string) (runtime.Ob
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(args) > 0 {
command, err := GetBool(params, "command", false)
if err != nil {
return nil, err
}
if command {
podSpec.Containers[0].Command = args
} else {
podSpec.Containers[0].Args = args
}
}
controller := api.ReplicationController{ controller := api.ReplicationController{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: name, Name: name,
@ -164,10 +196,30 @@ func (BasicPod) ParamNames() []GeneratorParam {
{"stdin", false}, {"stdin", false},
{"tty", false}, {"tty", false},
{"restart", false}, {"restart", false},
{"command", false},
{"args", false},
} }
} }
func (BasicPod) Generate(params map[string]string) (runtime.Object, error) { func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
args := []string{}
val, found := genericParams["args"]
if found {
var isArray bool
args, isArray = val.([]string)
if !isArray {
return nil, fmt.Errorf("expected []string, found: %v", val)
}
delete(genericParams, "args")
}
params := map[string]string{}
for key, value := range genericParams {
strVal, isString := value.(string)
if !isString {
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
}
params[key] = strVal
}
name, found := params["name"] name, found := params["name"]
if !found || len(name) == 0 { if !found || len(name) == 0 {
name, found = params["default-name"] name, found = params["default-name"]
@ -218,6 +270,17 @@ func (BasicPod) Generate(params map[string]string) (runtime.Object, error) {
RestartPolicy: restartPolicy, RestartPolicy: restartPolicy,
}, },
} }
if len(args) > 0 {
command, err := GetBool(params, "command", false)
if err != nil {
return nil, err
}
if command {
pod.Spec.Containers[0].Command = args
} else {
pod.Spec.Containers[0].Args = args
}
}
if err := updatePodPorts(params, &pod.Spec); err != nil { if err := updatePodPorts(params, &pod.Spec); err != nil {
return nil, err return nil, err
} }

View File

@ -25,12 +25,12 @@ import (
func TestGenerate(t *testing.T) { func TestGenerate(t *testing.T) {
tests := []struct { tests := []struct {
params map[string]string params map[string]interface{}
expected *api.ReplicationController expected *api.ReplicationController
expectErr bool expectErr bool
}{ }{
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"replicas": "1", "replicas": "1",
@ -61,7 +61,74 @@ func TestGenerate(t *testing.T) {
}, },
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo",
"image": "someimage",
"replicas": "1",
"port": "-1",
"args": []string{"bar", "baz", "blah"},
},
expected: &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
Args: []string{"bar", "baz", "blah"},
},
},
},
},
},
},
},
{
params: map[string]interface{}{
"name": "foo",
"image": "someimage",
"replicas": "1",
"port": "-1",
"args": []string{"bar", "baz", "blah"},
"command": "true",
},
expected: &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
Command: []string{"bar", "baz", "blah"},
},
},
},
},
},
},
},
{
params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"replicas": "1", "replicas": "1",
@ -97,7 +164,7 @@ func TestGenerate(t *testing.T) {
}, },
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"replicas": "1", "replicas": "1",
@ -135,7 +202,7 @@ func TestGenerate(t *testing.T) {
}, },
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"replicas": "1", "replicas": "1",
@ -145,7 +212,7 @@ func TestGenerate(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"replicas": "1", "replicas": "1",
@ -193,12 +260,12 @@ func TestGenerate(t *testing.T) {
func TestGeneratePod(t *testing.T) { func TestGeneratePod(t *testing.T) {
tests := []struct { tests := []struct {
params map[string]string params map[string]interface{}
expected *api.Pod expected *api.Pod
expectErr bool expectErr bool
}{ }{
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"port": "-1", "port": "-1",
@ -221,7 +288,7 @@ func TestGeneratePod(t *testing.T) {
}, },
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"port": "80", "port": "80",
@ -249,7 +316,7 @@ func TestGeneratePod(t *testing.T) {
}, },
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"port": "80", "port": "80",
@ -279,7 +346,7 @@ func TestGeneratePod(t *testing.T) {
}, },
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"hostport": "80", "hostport": "80",
@ -288,7 +355,7 @@ func TestGeneratePod(t *testing.T) {
expectErr: true, expectErr: true,
}, },
{ {
params: map[string]string{ params: map[string]interface{}{
"name": "foo", "name": "foo",
"image": "someimage", "image": "someimage",
"replicas": "1", "replicas": "1",

View File

@ -32,7 +32,7 @@ func (ServiceGeneratorV1) ParamNames() []GeneratorParam {
return paramNames() return paramNames()
} }
func (ServiceGeneratorV1) Generate(params map[string]string) (runtime.Object, error) { func (ServiceGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
params["port-name"] = "default" params["port-name"] = "default"
return generate(params) return generate(params)
} }
@ -43,7 +43,7 @@ func (ServiceGeneratorV2) ParamNames() []GeneratorParam {
return paramNames() return paramNames()
} }
func (ServiceGeneratorV2) Generate(params map[string]string) (runtime.Object, error) { func (ServiceGeneratorV2) Generate(params map[string]interface{}) (runtime.Object, error) {
return generate(params) return generate(params)
} }
@ -65,7 +65,15 @@ func paramNames() []GeneratorParam {
} }
} }
func generate(params map[string]string) (runtime.Object, error) { func generate(genericParams map[string]interface{}) (runtime.Object, error) {
params := map[string]string{}
for key, value := range genericParams {
strVal, isString := value.(string)
if !isString {
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
}
params[key] = strVal
}
selectorString, found := params["selector"] selectorString, found := params["selector"]
if !found || len(selectorString) == 0 { if !found || len(selectorString) == 0 {
return nil, fmt.Errorf("'selector' is a required parameter.") return nil, fmt.Errorf("'selector' is a required parameter.")

View File

@ -27,12 +27,12 @@ import (
func TestGenerateService(t *testing.T) { func TestGenerateService(t *testing.T) {
tests := []struct { tests := []struct {
generator Generator generator Generator
params map[string]string params map[string]interface{}
expected api.Service expected api.Service
}{ }{
{ {
generator: ServiceGeneratorV2{}, generator: ServiceGeneratorV2{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",
@ -61,7 +61,7 @@ func TestGenerateService(t *testing.T) {
{ {
generator: ServiceGeneratorV2{}, generator: ServiceGeneratorV2{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",
@ -89,7 +89,7 @@ func TestGenerateService(t *testing.T) {
}, },
{ {
generator: ServiceGeneratorV2{}, generator: ServiceGeneratorV2{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"labels": "key1=value1,key2=value2", "labels": "key1=value1,key2=value2",
"name": "test", "name": "test",
@ -122,7 +122,7 @@ func TestGenerateService(t *testing.T) {
}, },
{ {
generator: ServiceGeneratorV2{}, generator: ServiceGeneratorV2{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",
@ -152,7 +152,7 @@ func TestGenerateService(t *testing.T) {
}, },
{ {
generator: ServiceGeneratorV2{}, generator: ServiceGeneratorV2{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",
@ -184,7 +184,7 @@ func TestGenerateService(t *testing.T) {
}, },
{ {
generator: ServiceGeneratorV2{}, generator: ServiceGeneratorV2{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",
@ -214,7 +214,7 @@ func TestGenerateService(t *testing.T) {
}, },
{ {
generator: ServiceGeneratorV2{}, generator: ServiceGeneratorV2{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",
@ -245,7 +245,7 @@ func TestGenerateService(t *testing.T) {
}, },
{ {
generator: ServiceGeneratorV1{}, generator: ServiceGeneratorV1{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",
@ -274,7 +274,7 @@ func TestGenerateService(t *testing.T) {
}, },
{ {
generator: ServiceGeneratorV1{}, generator: ServiceGeneratorV1{},
params: map[string]string{ params: map[string]interface{}{
"selector": "foo=bar,baz=blah", "selector": "foo=bar,baz=blah",
"name": "test", "name": "test",
"port": "80", "port": "80",