Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kubectl run to produce deployment and job #17195

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 9 additions & 6 deletions docs/man/man1/kubectl-run.1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ kubectl run \- Run a particular image on the cluster.
.SH DESCRIPTION
.PP
Create and run a particular image, possibly replicated.
Creates a replication controller to manage the created container(s).
Creates a replication controller or job to manage the created container(s).


.SH OPTIONS
Expand All @@ -40,7 +40,7 @@ Creates a replication controller to manage the created container(s).

.PP
\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'.
The name of the API generator to use. Default is 'run/v1' if \-\-restart=Always, otherwise the default is 'job/v1beta1'.

.PP
\fB\-\-hostport\fP=\-1
Expand Down Expand Up @@ -94,7 +94,7 @@ Creates a replication controller to manage the created container(s).

.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'
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, a job is created for this pod and \-\-replicas must be 1. Default 'Always'

.PP
\fB\-\-save\-config\fP=false
Expand Down Expand Up @@ -247,15 +247,18 @@ $ 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.
$ 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 a single instance of busybox and keep it in the foreground, don't restart it if it exits.
$ kubectl run \-i \-\-tty busybox \-\-image=busybox \-\-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
# Start the nginx container using a different command and custom arguments.
$ kubectl run nginx \-\-image=nginx \-\-command \-\- <cmd> <arg1> ... <argN>

# Start the perl container to compute π to 2000 places and print it out.
$ kubectl run pi \-\-image=perl \-\-restart=OnFailure \-\- perl \-Mbignum=bpi \-wle 'print bpi(2000)'

.fi
.RE

Expand Down
15 changes: 9 additions & 6 deletions docs/user-guide/kubectl/kubectl_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Run a particular image on the cluster.


Create and run a particular image, possibly replicated.
Creates a replication controller to manage the created container(s).
Creates a replication controller or job to manage the created container(s).

```
kubectl run NAME --image=image [--env="key=value"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]
Expand All @@ -66,14 +66,17 @@ $ 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.
$ 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 a single instance of busybox and keep it in the foreground, don't restart it if it exits.
$ kubectl run -i --tty busybox --image=busybox --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
# Start the nginx container using a different command and custom arguments.
$ kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>

# Start the perl container to compute π to 2000 places and print it out.
$ kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(2000)'
```

### Options
Expand All @@ -84,7 +87,7 @@ $ kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
--dry-run[=false]: If true, only print the object that would be sent, without sending it.
--env=[]: Environment variables to set in the container
--expose[=false]: If true, a public, external service is created for the container(s) which are run
--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 'job/v1beta1'.
--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).
Expand All @@ -97,7 +100,7 @@ $ kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
--port=-1: The port that this container exposes. If --expose is true, this is also the port used by the service that is created.
-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'
--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, a job is created for this pod and --replicas must be 1. Default 'Always'
--save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future.
--service-generator="service/v2": The name of the generator to use for creating a service. Only used if --expose is true
--service-overrides="": An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. Only used if --expose is true.
Expand Down
20 changes: 19 additions & 1 deletion hack/test-cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ runTests() {
# Pre-Condition: no RC is running
kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" ''
# Command: create the rc "nginx" with image nginx
kubectl run nginx --image=nginx --save-config "${kube_flags[@]}"
kubectl run nginx --image=nginx --save-config --generator=run/v1 "${kube_flags[@]}"
# Post-Condition: rc "nginx" has configuration annotation
[[ "$(kubectl get rc nginx -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]]
## 5. kubectl expose --save-config should generate configuration annotation
Expand Down Expand Up @@ -647,6 +647,24 @@ runTests() {
# Clean up
kubectl delete pods test-pod "${kube_flags[@]}"

## kubectl run should create deployments or jobs
# Pre-Condition: no Job is running
kube::test::get_object_assert jobs "{{range.items}}{{$id_field}}:{{end}}" ''
# Command
kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(20)'
# Post-Condition: Job "pi" is created
kube::test::get_object_assert jobs "{{range.items}}{{$id_field}}:{{end}}" 'pi:'
# Clean up
kubectl delete jobs pi
# Pre-Condition: no Deployment is running
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" ''
# Command
kubectl run nginx --image=nginx --generator=deployment/v1beta1
# Post-Condition: Deployment "nginx" is created
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" 'nginx:'
# Clean up
kubectl delete deployment nginx

##############
# Namespaces #
##############
Expand Down
5 changes: 4 additions & 1 deletion pkg/apis/extensions/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,10 @@ func ValidateDeploymentSpec(spec *extensions.DeploymentSpec) validation.ErrorLis
allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(spec.Replicas), "replicas")...)
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpecForRC(&spec.Template, spec.Selector, spec.Replicas, "template")...)
allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, "strategy")...)
allErrs = append(allErrs, apivalidation.ValidateLabelName(spec.UniqueLabelKey, "uniqueLabel")...)
// empty string is a valid UniqueLabelKey
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is empty string valid? It should be set by default prior to validation.
cc @nikhiljindal

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/extensions/v1beta1/types.go#L210-L211:

Users can set this to an empty string to indicate that the system should not add any selector and label.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API allows users to set it to empty string to indicate that no label should be added. The default value will be set if user does not set it, not if user sets it to empty string (its a string pointer)
From API comments:

    // Users can set this to an empty string to indicate that the system should
// not add any selector and label. If unspecified, system uses
// "deployment.kubernetes.io/podTemplateHash".
// Value of this key is hash of DeploymentSpec.PodTemplateSpec.
// No label is added if this is set to empty string.

if len(spec.UniqueLabelKey) > 0 {
allErrs = append(allErrs, apivalidation.ValidateLabelName(spec.UniqueLabelKey, "uniqueLabel")...)
}
return allErrs
}

Expand Down
21 changes: 13 additions & 8 deletions pkg/kubectl/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (

const (
run_long = `Create and run a particular image, possibly replicated.
Creates a replication controller to manage the created container(s).`
Creates a replication controller or job to manage the created container(s).`
run_example = `# Start a single instance of nginx.
$ kubectl run nginx --image=nginx

Expand All @@ -53,14 +53,17 @@ $ 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.
$ 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 a single instance of busybox and keep it in the foreground, don't restart it if it exits.
$ kubectl run -i --tty busybox --image=busybox --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>`
# Start the nginx container using a different command and custom arguments.
$ kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>

# Start the perl container to compute π to 2000 places and print it out.
$ kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(2000)'`
)

func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
Expand All @@ -84,7 +87,8 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
}

func addRunFlags(cmd *cobra.Command) {
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'.")
// TODO: Change the default to "deployment/v1beta1" (which is a valid generator) when deployment reaches beta (#15313)
cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'run/v1' if --restart=Always, otherwise the default is 'job/v1beta1'.")
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.")
Expand All @@ -98,7 +102,7 @@ func addRunFlags(cmd *cobra.Command) {
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("leave-stdin-open", false, "If the pod is started in interactive mode or with stdin, leave stdin open after the first attach completes. By default, stdin will be closed after the first attach completes.")
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, a job is created for this pod 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'")
Expand Down Expand Up @@ -142,10 +146,11 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob

generatorName := cmdutil.GetFlagString(cmd, "generator")
if len(generatorName) == 0 {
// TODO: Change the default to "deployment/v1beta1" when deployment reaches beta (#15313)
if restartPolicy == api.RestartPolicyAlways {
generatorName = "run/v1"
} else {
generatorName = "run-pod/v1"
generatorName = "job/v1beta1"
}
}
generator, found := f.Generator(generatorName)
Expand Down
36 changes: 24 additions & 12 deletions pkg/kubectl/cmd/util/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/registered"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/fields"
Expand Down Expand Up @@ -116,6 +117,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
"service/v1": kubectl.ServiceGeneratorV1{},
"service/v2": kubectl.ServiceGeneratorV2{},
"horizontalpodautoscaler/v1beta1": kubectl.HorizontalPodAutoscalerV1Beta1{},
"deployment/v1beta1": kubectl.DeploymentV1Beta1{},
"job/v1beta1": kubectl.JobV1Beta1{},
}

clientConfig := optionalClientConfig
Expand Down Expand Up @@ -312,18 +315,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
switch t := object.(type) {
case *api.ReplicationController:
var pods *api.PodList
for pods == nil || len(pods.Items) == 0 {
var err error
if pods, err = client.Pods(t.Namespace).List(labels.SelectorFromSet(t.Spec.Selector), fields.Everything()); err != nil {
return nil, err
}
if len(pods.Items) == 0 {
time.Sleep(2 * time.Second)
}
}
pod := &pods.Items[0]
return pod, nil
return GetFirstPod(client, t.Namespace, t.Spec.Selector)
case *extensions.Deployment:
return GetFirstPod(client, t.Namespace, t.Spec.Selector)
case *extensions.Job:
return GetFirstPod(client, t.Namespace, t.Spec.Selector.MatchLabels)
case *api.Pod:
return t, nil
default:
Expand All @@ -337,6 +333,22 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
}

// GetFirstPod returns the first pod of an object from its namespace and selector
func GetFirstPod(client *client.Client, namespace string, selector map[string]string) (*api.Pod, error) {
var pods *api.PodList
for pods == nil || len(pods.Items) == 0 {
var err error
if pods, err = client.Pods(namespace).List(labels.SelectorFromSet(selector), fields.Everything()); err != nil {
return nil, err
}
if len(pods.Items) == 0 {
time.Sleep(2 * time.Second)
}
}
pod := &pods.Items[0]
return pod, nil
}

// BindFlags adds any flags that are common to all kubectl sub commands.
func (f *Factory) BindFlags(flags *pflag.FlagSet) {
// any flags defined by external projects (not part of pflags)
Expand Down