Add requests and limits to kubectl run

This commit is contained in:
derekwaynecarr 2015-09-09 17:22:56 -04:00
parent 90ba96d486
commit 295b8cdf16
6 changed files with 162 additions and 4 deletions

View File

@ -687,6 +687,7 @@ _kubectl_run()
flags+=("--image=")
flags+=("--labels=")
two_word_flags+=("-l")
flags+=("--limits=")
flags+=("--no-headers")
flags+=("--output=")
two_word_flags+=("-o")
@ -695,6 +696,7 @@ _kubectl_run()
flags+=("--port=")
flags+=("--replicas=")
two_word_flags+=("-r")
flags+=("--requests=")
flags+=("--restart=")
flags+=("--show-all")
flags+=("-a")

View File

@ -50,6 +50,10 @@ Creates a replication controller to manage the created container(s).
\fB\-l\fP, \fB\-\-labels\fP=""
Labels to apply to the pod(s).
.PP
\fB\-\-limits\fP=""
The resource requirement limits for this container. For example, 'cpu=200m,memory=512Mi'
.PP
\fB\-\-no\-headers\fP=false
When using the default output, don't print headers.
@ -76,6 +80,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\-\-requests\fP=""
The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'
.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'

View File

@ -87,12 +87,14 @@ $ kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
--hostport=-1: The host port mapping for the container port. To demonstrate a single-machine container.
--image="": The image for the container to run.
-l, --labels="": Labels to apply to the pod(s).
--limits="": The resource requirement limits for this container. For example, 'cpu=200m,memory=512Mi'
--no-headers[=false]: When using the default output, don't print headers.
-o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md].
--output-version="": Output the formatted object with the given version (default api-version).
--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.
--requests="": The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'
--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'
-a, --show-all[=false]: When printing, show all resources (default hide terminated pods.)
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.

View File

@ -92,6 +92,8 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
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().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.")
cmd.Flags().String("requests", "", "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'")
cmd.Flags().String("limits", "", "The resource requirement limits for this container. For example, 'cpu=200m,memory=512Mi'")
return cmd
}

View File

@ -22,6 +22,7 @@ import (
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util"
)
@ -42,9 +43,51 @@ func (BasicReplicationController) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"requests", false},
{"limits", false},
}
}
// populateResourceList takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
func populateResourceList(spec string) (api.ResourceList, error) {
// empty input gets a nil response to preserve generator test expected behaviors
if spec == "" {
return nil, nil
}
result := api.ResourceList{}
resourceStatements := strings.Split(spec, ",")
for _, resourceStatement := range resourceStatements {
parts := strings.Split(resourceStatement, "=")
if len(parts) != 2 {
return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
}
resourceName := api.ResourceName(parts[0])
resourceQuantity, err := resource.ParseQuantity(parts[1])
if err != nil {
return nil, err
}
result[resourceName] = *resourceQuantity
}
return result, nil
}
// HandleResourceRequirements parses the limits and requests parameters if specified
func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) {
result := api.ResourceRequirements{}
limits, err := populateResourceList(params["limits"])
if err != nil {
return result, err
}
result.Limits = limits
requests, err := populateResourceList(params["requests"])
if err != nil {
return result, err
}
result.Requests = requests
return result, nil
}
func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
stdin, err := GetBool(params, "stdin", false)
if err != nil {
@ -56,13 +99,19 @@ func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
return nil, err
}
resourceRequirements, err := HandleResourceRequirements(params)
if err != nil {
return nil, err
}
spec := api.PodSpec{
Containers: []api.Container{
{
Name: name,
Image: params["image"],
Stdin: stdin,
TTY: tty,
Name: name,
Image: params["image"],
Stdin: stdin,
TTY: tty,
Resources: resourceRequirements,
},
},
}
@ -223,6 +272,8 @@ func (BasicPod) ParamNames() []GeneratorParam {
{"command", false},
{"args", false},
{"env", false},
{"requests", false},
{"limits", false},
}
}
@ -288,6 +339,11 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
return nil, err
}
resourceRequirements, err := HandleResourceRequirements(params)
if err != nil {
return nil, err
}
restartPolicy := api.RestartPolicy(params["restart"])
if len(restartPolicy) == 0 {
restartPolicy = api.RestartPolicyAlways
@ -305,6 +361,7 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
ImagePullPolicy: api.PullIfNotPresent,
Stdin: stdin,
TTY: tty,
Resources: resourceRequirements,
},
},
DNSPolicy: api.DNSClusterFirst,

View File

@ -21,6 +21,7 @@ import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
)
func TestGenerate(t *testing.T) {
@ -286,6 +287,92 @@ func TestGenerate(t *testing.T) {
},
},
},
{
params: map[string]interface{}{
"name": "foo",
"image": "someimage",
"replicas": "1",
"hostport": "80",
},
expected: nil,
expectErr: true,
},
{
params: map[string]interface{}{
"name": "foo",
"image": "someimage",
"replicas": "1",
"labels": "foo=bar,baz=blah",
"requests": "cpu100m,memory=100Mi",
},
expected: nil,
expectErr: true,
},
{
params: map[string]interface{}{
"name": "foo",
"image": "someimage",
"replicas": "1",
"labels": "foo=bar,baz=blah",
"requests": "cpu=100m&memory=100Mi",
},
expected: nil,
expectErr: true,
},
{
params: map[string]interface{}{
"name": "foo",
"image": "someimage",
"replicas": "1",
"labels": "foo=bar,baz=blah",
"requests": "cpu=",
},
expected: nil,
expectErr: true,
},
{
params: map[string]interface{}{
"name": "foo",
"image": "someimage",
"replicas": "1",
"labels": "foo=bar,baz=blah",
"requests": "cpu=100m,memory=100Mi",
"limits": "cpu=400m,memory=200Mi",
},
expected: &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"foo": "bar", "baz": "blah"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("100Mi"),
},
Limits: api.ResourceList{
api.ResourceCPU: resource.MustParse("400m"),
api.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
},
},
},
},
},
},
}
generator := BasicReplicationController{}
for _, test := range tests {