mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
Merge pull request #21776 from erictune/job-sel-gen
Selector generation for batch/v1 Job
This commit is contained in:
commit
3dab166da4
@ -991,7 +991,7 @@
|
||||
"completions": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"description": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job."
|
||||
"description": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md"
|
||||
},
|
||||
"activeDeadlineSeconds": {
|
||||
"type": "integer",
|
||||
@ -1000,7 +1000,11 @@
|
||||
},
|
||||
"selector": {
|
||||
"$ref": "v1.LabelSelector",
|
||||
"description": "Selector is a label query over pods that should match the pod count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors"
|
||||
"description": "Selector is a label query over pods that should match the pod count. Normally, the system sets this field for you. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors"
|
||||
},
|
||||
"manualSelector": {
|
||||
"type": "boolean",
|
||||
"description": "ManualSelector controls generation of pod labels and pod selectors. Leave `manualSelector` unset unless you are certain what you are doing. When false or unset, the system pick labels unique to this job and appends those labels to the pod template. When true, the user is responsible for picking unique labels and specifying the selector. Failure to pick a unique label may cause this and other jobs to not function correctly. However, You may see `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` API. More info: http://releases.k8s.io/HEAD/docs/design/selector-generation.md"
|
||||
},
|
||||
"template": {
|
||||
"$ref": "v1.PodTemplateSpec",
|
||||
|
@ -7326,7 +7326,7 @@
|
||||
"completions": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"description": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job."
|
||||
"description": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md"
|
||||
},
|
||||
"activeDeadlineSeconds": {
|
||||
"type": "integer",
|
||||
@ -7335,7 +7335,11 @@
|
||||
},
|
||||
"selector": {
|
||||
"$ref": "v1beta1.LabelSelector",
|
||||
"description": "Selector is a label query over pods that should match the pod count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors"
|
||||
"description": "Selector is a label query over pods that should match the pod count. Normally, the system sets this field for you. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors"
|
||||
},
|
||||
"autoSelector": {
|
||||
"type": "boolean",
|
||||
"description": "AutoSelector controls generation of pod labels and pod selectors. It was not present in the original extensions/v1beta1 Job definition, but exists to allow conversion from batch/v1 Jobs, where it corresponds to, but has the opposite meaning as, ManualSelector. More info: http://releases.k8s.io/HEAD/docs/design/selector-generation.md"
|
||||
},
|
||||
"template": {
|
||||
"$ref": "v1.PodTemplateSpec",
|
||||
|
@ -4437,7 +4437,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">completions</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/jobs.md">http://releases.k8s.io/HEAD/docs/user-guide/jobs.md</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
@ -4451,12 +4451,19 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">selector</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Selector is a label query over pods that should match the pod count. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors">http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Selector is a label query over pods that should match the pod count. Normally, the system sets this field for you. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors">http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1beta1_labelselector">v1beta1.LabelSelector</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">autoSelector</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">AutoSelector controls generation of pod labels and pod selectors. It was not present in the original extensions/v1beta1 Job definition, but exists to allow conversion from batch/v1 Jobs, where it corresponds to, but has the opposite meaning as, ManualSelector. More info: <a href="http://releases.k8s.io/HEAD/docs/design/selector-generation.md">http://releases.k8s.io/HEAD/docs/design/selector-generation.md</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">template</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Template is the object that describes the pod that will be created when executing a job. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/jobs.md">http://releases.k8s.io/HEAD/docs/user-guide/jobs.md</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
|
@ -1,16 +1,11 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: pi
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: pi
|
||||
template:
|
||||
metadata:
|
||||
name: pi
|
||||
labels:
|
||||
app: pi
|
||||
spec:
|
||||
containers:
|
||||
- name: pi
|
||||
|
@ -47,6 +47,8 @@ Documentation for other releases can be found at
|
||||
- [Controlling Parallelism](#controlling-parallelism)
|
||||
- [Handling Pod and Container Failures](#handling-pod-and-container-failures)
|
||||
- [Job Patterns](#job-patterns)
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Specifying your own pod selector](#specifying-your-own-pod-selector)
|
||||
- [Alternatives](#alternatives)
|
||||
- [Bare Pods](#bare-pods)
|
||||
- [Replication Controller](#replication-controller)
|
||||
@ -76,19 +78,14 @@ It takes around 10s to complete.
|
||||
<!-- BEGIN MUNGE: EXAMPLE job.yaml -->
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: pi
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: pi
|
||||
template:
|
||||
metadata:
|
||||
name: pi
|
||||
labels:
|
||||
app: pi
|
||||
spec:
|
||||
containers:
|
||||
- name: pi
|
||||
@ -170,21 +167,9 @@ Only a [`RestartPolicy`](pod-states.md) equal to `Never` or `OnFailure` are allo
|
||||
|
||||
### Pod Selector
|
||||
|
||||
The `.spec.selector` field is a label query over a set of pods.
|
||||
The `.spec.selector` field is optional. In almost all cases you should not specify it.
|
||||
See section [specifying your own pod selector](#specifying-your-own-pod-selector).
|
||||
|
||||
The `spec.selector` is an object consisting of two fields:
|
||||
* `matchLabels` - works the same as the `.spec.selector` of a [ReplicationController](replication-controller.md)
|
||||
* `matchExpressions` - allows to build more sophisticated selectors by specifying key,
|
||||
list of values and an operator that relates the key and values.
|
||||
|
||||
When the two are specified the result is ANDed.
|
||||
|
||||
If `.spec.selector` is unspecified, `.spec.selector.matchLabels` will be defaulted to
|
||||
`.spec.template.metadata.labels`.
|
||||
|
||||
Also you should not normally create any pods whose labels match this selector, either directly,
|
||||
via another Job, or via another controller such as ReplicationController. Otherwise, the Job will
|
||||
think that those pods were created by it. Kubernetes will not stop you from doing this.
|
||||
|
||||
### Parallel Jobs
|
||||
|
||||
@ -323,6 +308,70 @@ Here, `W` is the number of work items.
|
||||
| Single Job with Static Work Assignment | W | any |
|
||||
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Specifying your own pod selector
|
||||
|
||||
Normally, when you create a job object, you do not specify `spec.selector`.
|
||||
The system defaulting logic adds this field when the job is created.
|
||||
It picks a selector value that will not overlap with any other jobs.
|
||||
|
||||
However, in some cases, you might need to override this automatically set selector.
|
||||
To do this, you can specify the `spec.selector` of the job.
|
||||
|
||||
Be very careful when doing this. If you specify a label selector which is not
|
||||
unique to the pods of that job, and which matches unrelated pods, then pods of the unrelated
|
||||
job may be deleted, or this job may count other pods as completing it, or one or both
|
||||
of the jobs may refuse to create pods or run to completion. If a non-unique selector is
|
||||
chosen, then other controllers (e.g. ReplicationController) and their pods may behave
|
||||
in unpredicatable ways too. Kubernetes will not stop you from making a mistake when
|
||||
specifying `spec.selector`.
|
||||
|
||||
Here is an example of a case when you might want to use this feature.
|
||||
|
||||
Say job `old` is already running. You want existing pods
|
||||
to keep running, but you want the rest of the pods it creates
|
||||
to use a different pod template and for the job to have a new name.
|
||||
You cannot update the job because these fields are not updatable.
|
||||
Therefore, you delete job `old` but leave its pods
|
||||
running, using `kubectl delete jobs/old-one --cascade=false`.
|
||||
Before deleting it, you make a note of what selector it uses:
|
||||
|
||||
```
|
||||
kind: Job
|
||||
metadata:
|
||||
name: old
|
||||
...
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
job-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
|
||||
...
|
||||
```
|
||||
|
||||
Then you create a new job with name `new` and you explicitly specify the same selector.
|
||||
Since the existing pods have label `job-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002`,
|
||||
they are controlled by job `new` as well.
|
||||
|
||||
You need to specify `manualSelector: true` in the new job since you are not using
|
||||
the selector that the system normally generates for you automatically.
|
||||
|
||||
```
|
||||
kind: Job
|
||||
metadata:
|
||||
name: new
|
||||
...
|
||||
spec:
|
||||
manualSelector: true
|
||||
selector:
|
||||
matchLabels:
|
||||
job-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002
|
||||
...
|
||||
```
|
||||
|
||||
The new Job itself will have a different uid from `a8f3d00d-c6d2-11e5-9f87-42010af00002`. Setting
|
||||
`manualSelector: true` tells the system to that you know what you are doing and to allow this
|
||||
mismatch.
|
||||
|
||||
## Alternatives
|
||||
|
||||
|
@ -32,7 +32,9 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
expvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation"
|
||||
"k8s.io/kubernetes/pkg/capabilities"
|
||||
"k8s.io/kubernetes/pkg/registry/job"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/pkg/util/yaml"
|
||||
schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
|
||||
@ -111,7 +113,10 @@ func validateObject(obj runtime.Object) (errors field.ErrorList) {
|
||||
if t.Namespace == "" {
|
||||
t.Namespace = api.NamespaceDefault
|
||||
}
|
||||
errors = expvalidation.ValidateJob(t)
|
||||
// Job needs generateSelector called before validation, and job.Validate does this.
|
||||
// See: https://github.com/kubernetes/kubernetes/issues/20951#issuecomment-187787040
|
||||
t.ObjectMeta.UID = types.UID("fakeuid")
|
||||
errors = job.Strategy.Validate(nil, t)
|
||||
case *extensions.Ingress:
|
||||
if t.Namespace == "" {
|
||||
t.Namespace = api.NamespaceDefault
|
||||
|
@ -40,21 +40,18 @@ First, create a template of a Job object:
|
||||
<!-- BEGIN MUNGE: EXAMPLE job.yaml.txt -->
|
||||
|
||||
```
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: process-item-$ITEM
|
||||
labels:
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jobexample
|
||||
item: $ITEM
|
||||
template:
|
||||
metadata:
|
||||
name: jobexample
|
||||
labels:
|
||||
app: jobexample
|
||||
item: $ITEM
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
@ -75,12 +72,14 @@ In a real use case, the processing would be some substantial computation, such a
|
||||
of a movie, or processing a range of rows in a database. The "$ITEM" parameter would specify for
|
||||
example, the frame number or the row range.
|
||||
|
||||
This Job has two labels. The first label, `app=jobexample`, distinguishes this group of jobs from
|
||||
other groups of jobs (these are not shown, but there might be other ones). This label
|
||||
makes it convenient to operate on all the jobs in the group at once. The second label, with
|
||||
key `item`, distinguishes individual jobs in the group. Each Job object needs to have
|
||||
a unique label that no other job has. This is it.
|
||||
Neither of these label keys are special to kubernetes -- you can pick your own label scheme.
|
||||
This Job and its Pod template have a label: `jobgroup=jobexample`. There is nothing special
|
||||
to the system about this label. This label
|
||||
makes it convenient to operate on all the jobs in this group at once.
|
||||
We also put the same label on the pod template so that we can check on all Pods of these Jobs
|
||||
with a single command.
|
||||
After the job is created, the system will add more labels that distinguish one Job's pods
|
||||
from another Job's pods.
|
||||
Note that the label key `jobgroup` is not special to Kubernetes. you can pick your own label scheme.
|
||||
|
||||
Next, expand the template into multiple files, one for each item to be processed.
|
||||
|
||||
@ -113,27 +112,25 @@ job "process-item-cherry" created
|
||||
Now, check on the jobs:
|
||||
|
||||
```console
|
||||
$ kubectl get jobs -l app=jobexample -L item
|
||||
JOB CONTAINER(S) IMAGE(S) SELECTOR SUCCESSFUL ITEM
|
||||
process-item-apple c busybox app in (jobexample),item in (apple) 1 apple
|
||||
process-item-banana c busybox app in (jobexample),item in (banana) 1 banana
|
||||
process-item-cherry c busybox app in (jobexample),item in (cherry) 1 cherry
|
||||
$ kubectl get jobs -l app=jobexample
|
||||
JOB CONTAINER(S) IMAGE(S) SELECTOR SUCCESSFUL
|
||||
process-item-apple c busybox app in (jobexample),item in (apple) 1
|
||||
process-item-banana c busybox app in (jobexample),item in (banana) 1
|
||||
process-item-cherry c busybox app in (jobexample),item in (cherry) 1
|
||||
```
|
||||
|
||||
Here we use the `-l` option to select all jobs that are part of this
|
||||
group of jobs. (There might be other unrelated jobs in the system that we
|
||||
do not care to see.)
|
||||
|
||||
The `-L` option adds an extra column with just the `item` label value.
|
||||
|
||||
We can check on the pods as well using the same label selector:
|
||||
|
||||
```console
|
||||
$ kubectl get pods -l app=jobexample -L item
|
||||
NAME READY STATUS RESTARTS AGE ITEM
|
||||
process-item-apple-kixwv 0/1 Completed 0 4m apple
|
||||
process-item-banana-wrsf7 0/1 Completed 0 4m banana
|
||||
process-item-cherry-dnfu9 0/1 Completed 0 4m cherry
|
||||
$ kubectl get pods -l app=jobexample
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
process-item-apple-kixwv 0/1 Completed 0 4m
|
||||
process-item-banana-wrsf7 0/1 Completed 0 4m
|
||||
process-item-cherry-dnfu9 0/1 Completed 0 4m
|
||||
```
|
||||
|
||||
There is not a single command to check on the output of all jobs at once,
|
||||
@ -170,21 +167,17 @@ First, download or paste the following template file to a file called `job.yaml.
|
||||
{%- for p in params %}
|
||||
{%- set name = p["name"] %}
|
||||
{%- set url = p["url"] %}
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: jobexample-{{ name }}
|
||||
labels:
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jobexample
|
||||
item: {{ name }}
|
||||
template:
|
||||
metadata:
|
||||
name: jobexample
|
||||
labels:
|
||||
app: jobexample
|
||||
item: {{ name }}
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
|
@ -5,21 +5,17 @@
|
||||
{%- for p in params %}
|
||||
{%- set name = p["name"] %}
|
||||
{%- set url = p["url"] %}
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: jobexample-{{ name }}
|
||||
labels:
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jobexample
|
||||
item: {{ name }}
|
||||
template:
|
||||
metadata:
|
||||
name: jobexample
|
||||
labels:
|
||||
app: jobexample
|
||||
item: {{ name }}
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
|
@ -1,18 +1,15 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: process-item-$ITEM
|
||||
labels:
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jobexample
|
||||
item: $ITEM
|
||||
template:
|
||||
metadata:
|
||||
name: jobexample
|
||||
labels:
|
||||
app: jobexample
|
||||
item: $ITEM
|
||||
jobgroup: jobexample
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
|
@ -262,21 +262,16 @@ image to match the name you used, and call it `./job.yaml`.
|
||||
<!-- BEGIN MUNGE: EXAMPLE job.yaml -->
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: job-wq-1
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: job-wq-1
|
||||
completions: 8
|
||||
parallelism: 2
|
||||
template:
|
||||
metadata:
|
||||
name: job-wq-1
|
||||
labels:
|
||||
app: job-wq-1
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
|
@ -1,18 +1,13 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: job-wq-1
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: job-wq-1
|
||||
completions: 8
|
||||
parallelism: 2
|
||||
template:
|
||||
metadata:
|
||||
name: job-wq-1
|
||||
labels:
|
||||
app: job-wq-1
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
|
@ -217,20 +217,15 @@ Here is the job definition:
|
||||
<!-- BEGIN MUNGE: EXAMPLE job.yaml -->
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: job-wq-2
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: job-wq-2
|
||||
parallelism: 2
|
||||
template:
|
||||
metadata:
|
||||
name: job-wq-2
|
||||
labels:
|
||||
app: job-wq-2
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
|
@ -1,17 +1,12 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: job-wq-2
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: job-wq-2
|
||||
parallelism: 2
|
||||
template:
|
||||
metadata:
|
||||
name: job-wq-2
|
||||
labels:
|
||||
app: job-wq-2
|
||||
spec:
|
||||
containers:
|
||||
- name: c
|
||||
|
@ -162,6 +162,11 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source)
|
||||
parallelism := int(c.Rand.Int31())
|
||||
j.Completions = &completions
|
||||
j.Parallelism = ¶llelism
|
||||
if c.Rand.Int31()%2 == 0 {
|
||||
j.ManualSelector = newBool(true)
|
||||
} else {
|
||||
j.ManualSelector = nil
|
||||
}
|
||||
},
|
||||
func(j *api.List, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(j) // fuzz self without calling this function again
|
||||
@ -411,3 +416,9 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source)
|
||||
)
|
||||
return f
|
||||
}
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
@ -2681,6 +2681,12 @@ func autoConvert_v1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.J
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
if in.ManualSelector != nil {
|
||||
out.ManualSelector = new(bool)
|
||||
*out.ManualSelector = *in.ManualSelector
|
||||
} else {
|
||||
out.ManualSelector = nil
|
||||
}
|
||||
if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -2885,6 +2891,12 @@ func autoConvert_extensions_JobSpec_To_v1_JobSpec(in *extensions.JobSpec, out *J
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
if in.ManualSelector != nil {
|
||||
out.ManualSelector = new(bool)
|
||||
*out.ManualSelector = *in.ManualSelector
|
||||
} else {
|
||||
out.ManualSelector = nil
|
||||
}
|
||||
if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1076,6 +1076,12 @@ func deepCopy_v1_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) error {
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
if in.ManualSelector != nil {
|
||||
out.ManualSelector = new(bool)
|
||||
*out.ManualSelector = *in.ManualSelector
|
||||
} else {
|
||||
out.ManualSelector = nil
|
||||
}
|
||||
if err := deepCopy_v1_PodTemplateSpec(in.Template, &out.Template, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -23,18 +23,6 @@ import (
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) {
|
||||
scheme.AddDefaultingFuncs(
|
||||
func(obj *Job) {
|
||||
labels := obj.Spec.Template.Labels
|
||||
// TODO: support templates defined elsewhere when we support them in the API
|
||||
if labels != nil {
|
||||
if obj.Spec.Selector == nil {
|
||||
obj.Spec.Selector = &LabelSelector{
|
||||
MatchLabels: labels,
|
||||
}
|
||||
}
|
||||
if len(obj.Labels) == 0 {
|
||||
obj.Labels = labels
|
||||
}
|
||||
}
|
||||
// For a non-parallel job, you can leave both `.spec.completions` and
|
||||
// `.spec.parallelism` unset. When both are unset, both are defaulted to 1.
|
||||
if obj.Spec.Completions == nil && obj.Spec.Parallelism == nil {
|
||||
|
@ -778,16 +778,17 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [5]bool
|
||||
var yyq2 [6]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[0] = x.Parallelism != nil
|
||||
yyq2[1] = x.Completions != nil
|
||||
yyq2[2] = x.ActiveDeadlineSeconds != nil
|
||||
yyq2[3] = x.Selector != nil
|
||||
yyq2[4] = x.ManualSelector != nil
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(5)
|
||||
r.EncodeArrayStart(6)
|
||||
} else {
|
||||
yynn2 = 1
|
||||
for _, b := range yyq2 {
|
||||
@ -928,14 +929,49 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yy22 := &x.Template
|
||||
yy22.CodecEncodeSelf(e)
|
||||
if yyq2[4] {
|
||||
if x.ManualSelector == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yy22 := *x.ManualSelector
|
||||
yym23 := z.EncBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeBool(bool(yy22))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.EncodeNil()
|
||||
}
|
||||
} else {
|
||||
if yyq2[4] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("manualSelector"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
if x.ManualSelector == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yy24 := *x.ManualSelector
|
||||
yym25 := z.EncBinary()
|
||||
_ = yym25
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeBool(bool(yy24))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yy27 := &x.Template
|
||||
yy27.CodecEncodeSelf(e)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("template"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yy24 := &x.Template
|
||||
yy24.CodecEncodeSelf(e)
|
||||
yy29 := &x.Template
|
||||
yy29.CodecEncodeSelf(e)
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
@ -1057,12 +1093,28 @@ func (x *JobSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
}
|
||||
x.Selector.CodecDecodeSelf(d)
|
||||
}
|
||||
case "manualSelector":
|
||||
if r.TryDecodeAsNil() {
|
||||
if x.ManualSelector != nil {
|
||||
x.ManualSelector = nil
|
||||
}
|
||||
} else {
|
||||
if x.ManualSelector == nil {
|
||||
x.ManualSelector = new(bool)
|
||||
}
|
||||
yym12 := z.DecBinary()
|
||||
_ = yym12
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(x.ManualSelector)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
case "template":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Template = pkg2_v1.PodTemplateSpec{}
|
||||
} else {
|
||||
yyv11 := &x.Template
|
||||
yyv11.CodecDecodeSelf(d)
|
||||
yyv13 := &x.Template
|
||||
yyv13.CodecDecodeSelf(d)
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
@ -1075,16 +1127,16 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj12 int
|
||||
var yyb12 bool
|
||||
var yyhl12 bool = l >= 0
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
var yyj14 int
|
||||
var yyb14 bool
|
||||
var yyhl14 bool = l >= 0
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -1097,20 +1149,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.Parallelism == nil {
|
||||
x.Parallelism = new(int32)
|
||||
}
|
||||
yym14 := z.DecBinary()
|
||||
_ = yym14
|
||||
yym16 := z.DecBinary()
|
||||
_ = yym16
|
||||
if false {
|
||||
} else {
|
||||
*((*int32)(x.Parallelism)) = int32(r.DecodeInt(32))
|
||||
}
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -1123,20 +1175,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.Completions == nil {
|
||||
x.Completions = new(int32)
|
||||
}
|
||||
yym16 := z.DecBinary()
|
||||
_ = yym16
|
||||
yym18 := z.DecBinary()
|
||||
_ = yym18
|
||||
if false {
|
||||
} else {
|
||||
*((*int32)(x.Completions)) = int32(r.DecodeInt(32))
|
||||
}
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -1149,20 +1201,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.ActiveDeadlineSeconds == nil {
|
||||
x.ActiveDeadlineSeconds = new(int64)
|
||||
}
|
||||
yym18 := z.DecBinary()
|
||||
_ = yym18
|
||||
yym20 := z.DecBinary()
|
||||
_ = yym20
|
||||
if false {
|
||||
} else {
|
||||
*((*int64)(x.ActiveDeadlineSeconds)) = int64(r.DecodeInt(64))
|
||||
}
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -1177,13 +1229,39 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
}
|
||||
x.Selector.CodecDecodeSelf(d)
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
if x.ManualSelector != nil {
|
||||
x.ManualSelector = nil
|
||||
}
|
||||
} else {
|
||||
if x.ManualSelector == nil {
|
||||
x.ManualSelector = new(bool)
|
||||
}
|
||||
yym23 := z.DecBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(x.ManualSelector)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -1191,21 +1269,21 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Template = pkg2_v1.PodTemplateSpec{}
|
||||
} else {
|
||||
yyv20 := &x.Template
|
||||
yyv20.CodecDecodeSelf(d)
|
||||
yyv24 := &x.Template
|
||||
yyv24.CodecDecodeSelf(d)
|
||||
}
|
||||
for {
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj12-1, "")
|
||||
z.DecStructFieldNotFound(yyj14-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
@ -2789,7 +2867,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) {
|
||||
|
||||
yyrg1 := len(yyv1) > 0
|
||||
yyv21 := yyv1
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 632)
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 640)
|
||||
if yyrt1 {
|
||||
if yyrl1 <= cap(yyv1) {
|
||||
yyv1 = yyv1[:yyrl1]
|
||||
|
@ -63,6 +63,7 @@ type JobSpec struct {
|
||||
// pod signals the success of all pods, and allows parallelism to have any positive
|
||||
// value. Setting to 1 means that parallelism is limited to 1 and the success of that
|
||||
// pod signals the success of the job.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md
|
||||
Completions *int32 `json:"completions,omitempty"`
|
||||
|
||||
// Optional duration in seconds relative to the startTime that the job may be active
|
||||
@ -70,9 +71,22 @@ type JobSpec struct {
|
||||
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
|
||||
|
||||
// Selector is a label query over pods that should match the pod count.
|
||||
// Normally, the system sets this field for you.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors
|
||||
Selector *LabelSelector `json:"selector,omitempty"`
|
||||
|
||||
// ManualSelector controls generation of pod labels and pod selectors.
|
||||
// Leave `manualSelector` unset unless you are certain what you are doing.
|
||||
// When false or unset, the system pick labels unique to this job
|
||||
// and appends those labels to the pod template. When true,
|
||||
// the user is responsible for picking unique labels and specifying
|
||||
// the selector. Failure to pick a unique label may cause this
|
||||
// and other jobs to not function correctly. However, You may see
|
||||
// `manualSelector=true` in jobs that were created with the old `extensions/v1beta1`
|
||||
// API.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/design/selector-generation.md
|
||||
ManualSelector *bool `json:"manualSelector,omitempty"`
|
||||
|
||||
// Template is the object that describes the pod that will be created when
|
||||
// executing a job.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md
|
||||
|
@ -65,9 +65,10 @@ func (JobList) SwaggerDoc() map[string]string {
|
||||
var map_JobSpec = map[string]string{
|
||||
"": "JobSpec describes how the job execution will look like.",
|
||||
"parallelism": "Parallelism specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
|
||||
"completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job.",
|
||||
"completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
|
||||
"activeDeadlineSeconds": "Optional duration in seconds relative to the startTime that the job may be active before the system tries to terminate it; value must be positive integer",
|
||||
"selector": "Selector is a label query over pods that should match the pod count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
|
||||
"selector": "Selector is a label query over pods that should match the pod count. Normally, the system sets this field for you. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
|
||||
"manualSelector": "ManualSelector controls generation of pod labels and pod selectors. Leave `manualSelector` unset unless you are certain what you are doing. When false or unset, the system pick labels unique to this job and appends those labels to the pod template. When true, the user is responsible for picking unique labels and specifying the selector. Failure to pick a unique label may cause this and other jobs to not function correctly. However, You may see `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` API. More info: http://releases.k8s.io/HEAD/docs/design/selector-generation.md",
|
||||
"template": "Template is the object that describes the pod that will be created when executing a job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
|
||||
}
|
||||
|
||||
|
@ -9925,16 +9925,17 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [5]bool
|
||||
var yyq2 [6]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[0] = x.Parallelism != nil
|
||||
yyq2[1] = x.Completions != nil
|
||||
yyq2[2] = x.ActiveDeadlineSeconds != nil
|
||||
yyq2[3] = x.Selector != nil
|
||||
yyq2[4] = x.ManualSelector != nil
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(5)
|
||||
r.EncodeArrayStart(6)
|
||||
} else {
|
||||
yynn2 = 1
|
||||
for _, b := range yyq2 {
|
||||
@ -10087,14 +10088,49 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yy22 := &x.Template
|
||||
yy22.CodecEncodeSelf(e)
|
||||
if yyq2[4] {
|
||||
if x.ManualSelector == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yy22 := *x.ManualSelector
|
||||
yym23 := z.EncBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeBool(bool(yy22))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.EncodeNil()
|
||||
}
|
||||
} else {
|
||||
if yyq2[4] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("manualSelector"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
if x.ManualSelector == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yy24 := *x.ManualSelector
|
||||
yym25 := z.EncBinary()
|
||||
_ = yym25
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeBool(bool(yy24))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yy27 := &x.Template
|
||||
yy27.CodecEncodeSelf(e)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("template"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yy24 := &x.Template
|
||||
yy24.CodecEncodeSelf(e)
|
||||
yy29 := &x.Template
|
||||
yy29.CodecEncodeSelf(e)
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
@ -10222,12 +10258,28 @@ func (x *JobSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
z.DecFallback(x.Selector, false)
|
||||
}
|
||||
}
|
||||
case "manualSelector":
|
||||
if r.TryDecodeAsNil() {
|
||||
if x.ManualSelector != nil {
|
||||
x.ManualSelector = nil
|
||||
}
|
||||
} else {
|
||||
if x.ManualSelector == nil {
|
||||
x.ManualSelector = new(bool)
|
||||
}
|
||||
yym13 := z.DecBinary()
|
||||
_ = yym13
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(x.ManualSelector)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
case "template":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Template = pkg2_api.PodTemplateSpec{}
|
||||
} else {
|
||||
yyv12 := &x.Template
|
||||
yyv12.CodecDecodeSelf(d)
|
||||
yyv14 := &x.Template
|
||||
yyv14.CodecDecodeSelf(d)
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
@ -10240,16 +10292,16 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj13 int
|
||||
var yyb13 bool
|
||||
var yyhl13 bool = l >= 0
|
||||
yyj13++
|
||||
if yyhl13 {
|
||||
yyb13 = yyj13 > l
|
||||
var yyj15 int
|
||||
var yyb15 bool
|
||||
var yyhl15 bool = l >= 0
|
||||
yyj15++
|
||||
if yyhl15 {
|
||||
yyb15 = yyj15 > l
|
||||
} else {
|
||||
yyb13 = r.CheckBreak()
|
||||
yyb15 = r.CheckBreak()
|
||||
}
|
||||
if yyb13 {
|
||||
if yyb15 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10262,20 +10314,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.Parallelism == nil {
|
||||
x.Parallelism = new(int)
|
||||
}
|
||||
yym15 := z.DecBinary()
|
||||
_ = yym15
|
||||
yym17 := z.DecBinary()
|
||||
_ = yym17
|
||||
if false {
|
||||
} else {
|
||||
*((*int)(x.Parallelism)) = int(r.DecodeInt(codecSelferBitsize1234))
|
||||
}
|
||||
}
|
||||
yyj13++
|
||||
if yyhl13 {
|
||||
yyb13 = yyj13 > l
|
||||
yyj15++
|
||||
if yyhl15 {
|
||||
yyb15 = yyj15 > l
|
||||
} else {
|
||||
yyb13 = r.CheckBreak()
|
||||
yyb15 = r.CheckBreak()
|
||||
}
|
||||
if yyb13 {
|
||||
if yyb15 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10288,20 +10340,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.Completions == nil {
|
||||
x.Completions = new(int)
|
||||
}
|
||||
yym17 := z.DecBinary()
|
||||
_ = yym17
|
||||
yym19 := z.DecBinary()
|
||||
_ = yym19
|
||||
if false {
|
||||
} else {
|
||||
*((*int)(x.Completions)) = int(r.DecodeInt(codecSelferBitsize1234))
|
||||
}
|
||||
}
|
||||
yyj13++
|
||||
if yyhl13 {
|
||||
yyb13 = yyj13 > l
|
||||
yyj15++
|
||||
if yyhl15 {
|
||||
yyb15 = yyj15 > l
|
||||
} else {
|
||||
yyb13 = r.CheckBreak()
|
||||
yyb15 = r.CheckBreak()
|
||||
}
|
||||
if yyb13 {
|
||||
if yyb15 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10314,20 +10366,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.ActiveDeadlineSeconds == nil {
|
||||
x.ActiveDeadlineSeconds = new(int64)
|
||||
}
|
||||
yym19 := z.DecBinary()
|
||||
_ = yym19
|
||||
yym21 := z.DecBinary()
|
||||
_ = yym21
|
||||
if false {
|
||||
} else {
|
||||
*((*int64)(x.ActiveDeadlineSeconds)) = int64(r.DecodeInt(64))
|
||||
}
|
||||
}
|
||||
yyj13++
|
||||
if yyhl13 {
|
||||
yyb13 = yyj13 > l
|
||||
yyj15++
|
||||
if yyhl15 {
|
||||
yyb15 = yyj15 > l
|
||||
} else {
|
||||
yyb13 = r.CheckBreak()
|
||||
yyb15 = r.CheckBreak()
|
||||
}
|
||||
if yyb13 {
|
||||
if yyb15 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10340,21 +10392,47 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.Selector == nil {
|
||||
x.Selector = new(pkg1_unversioned.LabelSelector)
|
||||
}
|
||||
yym21 := z.DecBinary()
|
||||
_ = yym21
|
||||
yym23 := z.DecBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else if z.HasExtensions() && z.DecExt(x.Selector) {
|
||||
} else {
|
||||
z.DecFallback(x.Selector, false)
|
||||
}
|
||||
}
|
||||
yyj13++
|
||||
if yyhl13 {
|
||||
yyb13 = yyj13 > l
|
||||
yyj15++
|
||||
if yyhl15 {
|
||||
yyb15 = yyj15 > l
|
||||
} else {
|
||||
yyb13 = r.CheckBreak()
|
||||
yyb15 = r.CheckBreak()
|
||||
}
|
||||
if yyb13 {
|
||||
if yyb15 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
if x.ManualSelector != nil {
|
||||
x.ManualSelector = nil
|
||||
}
|
||||
} else {
|
||||
if x.ManualSelector == nil {
|
||||
x.ManualSelector = new(bool)
|
||||
}
|
||||
yym25 := z.DecBinary()
|
||||
_ = yym25
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(x.ManualSelector)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
yyj15++
|
||||
if yyhl15 {
|
||||
yyb15 = yyj15 > l
|
||||
} else {
|
||||
yyb15 = r.CheckBreak()
|
||||
}
|
||||
if yyb15 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10362,21 +10440,21 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Template = pkg2_api.PodTemplateSpec{}
|
||||
} else {
|
||||
yyv22 := &x.Template
|
||||
yyv22.CodecDecodeSelf(d)
|
||||
yyv26 := &x.Template
|
||||
yyv26.CodecDecodeSelf(d)
|
||||
}
|
||||
for {
|
||||
yyj13++
|
||||
if yyhl13 {
|
||||
yyb13 = yyj13 > l
|
||||
yyj15++
|
||||
if yyhl15 {
|
||||
yyb15 = yyj15 > l
|
||||
} else {
|
||||
yyb13 = r.CheckBreak()
|
||||
yyb15 = r.CheckBreak()
|
||||
}
|
||||
if yyb13 {
|
||||
if yyb15 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj13-1, "")
|
||||
z.DecStructFieldNotFound(yyj15-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
@ -19286,7 +19364,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) {
|
||||
|
||||
yyrg1 := len(yyv1) > 0
|
||||
yyv21 := yyv1
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 616)
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 624)
|
||||
if yyrt1 {
|
||||
if yyrl1 <= cap(yyv1) {
|
||||
yyv1 = yyv1[:yyrl1]
|
||||
|
@ -535,7 +535,10 @@ type JobSpec struct {
|
||||
Parallelism *int `json:"parallelism,omitempty"`
|
||||
|
||||
// Completions specifies the desired number of successfully finished pods the
|
||||
// job should be run with. When unset, any pod exiting signals the job to complete.
|
||||
// job should be run with. Setting to nil means that the success of any
|
||||
// pod signals the success of all pods, and allows parallelism to have any positive
|
||||
// value. Setting to 1 means that parallelism is limited to 1 and the success of that
|
||||
// pod signals the success of the job.
|
||||
Completions *int `json:"completions,omitempty"`
|
||||
|
||||
// Optional duration in seconds relative to the startTime that the job may be active
|
||||
@ -543,8 +546,20 @@ type JobSpec struct {
|
||||
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
|
||||
|
||||
// Selector is a label query over pods that should match the pod count.
|
||||
// Normally, the system sets this field for you.
|
||||
Selector *unversioned.LabelSelector `json:"selector,omitempty"`
|
||||
|
||||
// ManualSelector controls generation of pod labels and pod selectors.
|
||||
// Leave `manualSelector` unset unless you are certain what you are doing.
|
||||
// When false or unset, the system pick labels unique to this job
|
||||
// and appends those labels to the pod template. When true,
|
||||
// the user is responsible for picking unique labels and specifying
|
||||
// the selector. Failure to pick a unique label may cause this
|
||||
// and other jobs to not function correctly. However, You may see
|
||||
// `manualSelector=true` in jobs that were created with the old `extensions/v1beta1`
|
||||
// API.
|
||||
ManualSelector *bool `json:"manualSelector,omitempty"`
|
||||
|
||||
// Template is the object that describes the pod that will be created when
|
||||
// executing a job.
|
||||
Template api.PodTemplateSpec `json:"template"`
|
||||
|
@ -42,6 +42,8 @@ func addConversionFuncs(scheme *runtime.Scheme) {
|
||||
Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment,
|
||||
Convert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec,
|
||||
Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec,
|
||||
Convert_extensions_JobSpec_To_v1beta1_JobSpec,
|
||||
Convert_v1beta1_JobSpec_To_extensions_JobSpec,
|
||||
)
|
||||
if err != nil {
|
||||
// If one of the conversion functions is malformed, detect it immediately.
|
||||
@ -277,3 +279,107 @@ func Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *ReplicaSetS
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, out *JobSpec, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*extensions.JobSpec))(in)
|
||||
}
|
||||
if in.Parallelism != nil {
|
||||
out.Parallelism = new(int32)
|
||||
*out.Parallelism = int32(*in.Parallelism)
|
||||
} else {
|
||||
out.Parallelism = nil
|
||||
}
|
||||
if in.Completions != nil {
|
||||
out.Completions = new(int32)
|
||||
*out.Completions = int32(*in.Completions)
|
||||
} else {
|
||||
out.Completions = nil
|
||||
}
|
||||
if in.ActiveDeadlineSeconds != nil {
|
||||
out.ActiveDeadlineSeconds = new(int64)
|
||||
*out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds
|
||||
} else {
|
||||
out.ActiveDeadlineSeconds = nil
|
||||
}
|
||||
// unable to generate simple pointer conversion for unversioned.LabelSelector -> v1beta1.LabelSelector
|
||||
if in.Selector != nil {
|
||||
out.Selector = new(LabelSelector)
|
||||
if err := Convert_unversioned_LabelSelector_To_v1beta1_LabelSelector(in.Selector, out.Selector, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
|
||||
// BEGIN non-standard conversion
|
||||
// autoSelector has opposite meaning as manualSelector.
|
||||
// in both cases, unset means false, and unset is always preferred to false.
|
||||
// unset vs set-false distinction is not preserved.
|
||||
manualSelector := in.ManualSelector != nil && *in.ManualSelector
|
||||
autoSelector := !manualSelector
|
||||
if autoSelector {
|
||||
out.AutoSelector = new(bool)
|
||||
*out.AutoSelector = true
|
||||
} else {
|
||||
out.AutoSelector = nil
|
||||
}
|
||||
// END non-standard conversion
|
||||
|
||||
if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.JobSpec, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*JobSpec))(in)
|
||||
}
|
||||
if in.Parallelism != nil {
|
||||
out.Parallelism = new(int)
|
||||
*out.Parallelism = int(*in.Parallelism)
|
||||
} else {
|
||||
out.Parallelism = nil
|
||||
}
|
||||
if in.Completions != nil {
|
||||
out.Completions = new(int)
|
||||
*out.Completions = int(*in.Completions)
|
||||
} else {
|
||||
out.Completions = nil
|
||||
}
|
||||
if in.ActiveDeadlineSeconds != nil {
|
||||
out.ActiveDeadlineSeconds = new(int64)
|
||||
*out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds
|
||||
} else {
|
||||
out.ActiveDeadlineSeconds = nil
|
||||
}
|
||||
// unable to generate simple pointer conversion for v1beta1.LabelSelector -> unversioned.LabelSelector
|
||||
if in.Selector != nil {
|
||||
out.Selector = new(unversioned.LabelSelector)
|
||||
if err := Convert_v1beta1_LabelSelector_To_unversioned_LabelSelector(in.Selector, out.Selector, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
|
||||
// BEGIN non-standard conversion
|
||||
// autoSelector has opposite meaning as manualSelector.
|
||||
// in both cases, unset means false, and unset is always preferred to false.
|
||||
// unset vs set-false distinction is not preserved.
|
||||
autoSelector := bool(in.AutoSelector != nil && *in.AutoSelector)
|
||||
manualSelector := !autoSelector
|
||||
if manualSelector {
|
||||
out.ManualSelector = new(bool)
|
||||
*out.ManualSelector = true
|
||||
} else {
|
||||
out.ManualSelector = nil
|
||||
}
|
||||
// END non-standard conversion
|
||||
|
||||
if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3460,16 +3460,13 @@ func autoConvert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, o
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
// in.ManualSelector has no peer in out
|
||||
if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, out *JobSpec, s conversion.Scope) error {
|
||||
return autoConvert_extensions_JobSpec_To_v1beta1_JobSpec(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_extensions_JobStatus_To_v1beta1_JobStatus(in *extensions.JobStatus, out *JobStatus, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*extensions.JobStatus))(in)
|
||||
@ -4730,16 +4727,13 @@ func autoConvert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensi
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
// in.AutoSelector has no peer in out
|
||||
if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.JobSpec, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_JobSpec_To_extensions_JobSpec(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_JobStatus_To_extensions_JobStatus(in *JobStatus, out *extensions.JobStatus, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*JobStatus))(in)
|
||||
|
83
pkg/apis/extensions/v1beta1/conversion_test.go
Normal file
83
pkg/apis/extensions/v1beta1/conversion_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2016 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 v1beta1_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
versioned "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
)
|
||||
|
||||
// TestJobSpecConversion tests that ManualSelector and AutoSelector
|
||||
// are handled correctly.
|
||||
func TestJobSpecConversion(t *testing.T) {
|
||||
pTrue := new(bool)
|
||||
*pTrue = true
|
||||
pFalse := new(bool)
|
||||
*pFalse = false
|
||||
|
||||
// False or nil convert to true.
|
||||
// True converts to nil.
|
||||
tests := []struct {
|
||||
in *bool
|
||||
expectOut *bool
|
||||
}{
|
||||
{
|
||||
in: nil,
|
||||
expectOut: pTrue,
|
||||
},
|
||||
{
|
||||
in: pFalse,
|
||||
expectOut: pTrue,
|
||||
},
|
||||
{
|
||||
in: pTrue,
|
||||
expectOut: nil,
|
||||
},
|
||||
}
|
||||
|
||||
// Test internal -> v1beta1.
|
||||
for _, test := range tests {
|
||||
i := &extensions.JobSpec{
|
||||
ManualSelector: test.in,
|
||||
}
|
||||
v := versioned.JobSpec{}
|
||||
if err := api.Scheme.Convert(i, &v); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expectOut, v.AutoSelector) {
|
||||
t.Fatalf("want v1beta1.AutoSelector %v, got %v", test.expectOut, v.AutoSelector)
|
||||
}
|
||||
}
|
||||
|
||||
// Test v1beta1 -> internal.
|
||||
for _, test := range tests {
|
||||
i := &versioned.JobSpec{
|
||||
AutoSelector: test.in,
|
||||
}
|
||||
e := extensions.JobSpec{}
|
||||
if err := api.Scheme.Convert(i, &e); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expectOut, e.ManualSelector) {
|
||||
t.Fatalf("want extensions.ManualSelector %v, got %v", test.expectOut, e.ManualSelector)
|
||||
}
|
||||
}
|
||||
}
|
@ -1565,6 +1565,12 @@ func deepCopy_v1beta1_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) er
|
||||
} else {
|
||||
out.Selector = nil
|
||||
}
|
||||
if in.AutoSelector != nil {
|
||||
out.AutoSelector = new(bool)
|
||||
*out.AutoSelector = *in.AutoSelector
|
||||
} else {
|
||||
out.AutoSelector = nil
|
||||
}
|
||||
if err := deepCopy_v1_PodTemplateSpec(in.Template, &out.Template, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -9935,16 +9935,17 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [5]bool
|
||||
var yyq2 [6]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[0] = x.Parallelism != nil
|
||||
yyq2[1] = x.Completions != nil
|
||||
yyq2[2] = x.ActiveDeadlineSeconds != nil
|
||||
yyq2[3] = x.Selector != nil
|
||||
yyq2[4] = x.AutoSelector != nil
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(5)
|
||||
r.EncodeArrayStart(6)
|
||||
} else {
|
||||
yynn2 = 1
|
||||
for _, b := range yyq2 {
|
||||
@ -10085,14 +10086,49 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yy22 := &x.Template
|
||||
yy22.CodecEncodeSelf(e)
|
||||
if yyq2[4] {
|
||||
if x.AutoSelector == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yy22 := *x.AutoSelector
|
||||
yym23 := z.EncBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeBool(bool(yy22))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.EncodeNil()
|
||||
}
|
||||
} else {
|
||||
if yyq2[4] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("autoSelector"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
if x.AutoSelector == nil {
|
||||
r.EncodeNil()
|
||||
} else {
|
||||
yy24 := *x.AutoSelector
|
||||
yym25 := z.EncBinary()
|
||||
_ = yym25
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeBool(bool(yy24))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
yy27 := &x.Template
|
||||
yy27.CodecEncodeSelf(e)
|
||||
} else {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("template"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yy24 := &x.Template
|
||||
yy24.CodecEncodeSelf(e)
|
||||
yy29 := &x.Template
|
||||
yy29.CodecEncodeSelf(e)
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
@ -10214,12 +10250,28 @@ func (x *JobSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
}
|
||||
x.Selector.CodecDecodeSelf(d)
|
||||
}
|
||||
case "autoSelector":
|
||||
if r.TryDecodeAsNil() {
|
||||
if x.AutoSelector != nil {
|
||||
x.AutoSelector = nil
|
||||
}
|
||||
} else {
|
||||
if x.AutoSelector == nil {
|
||||
x.AutoSelector = new(bool)
|
||||
}
|
||||
yym12 := z.DecBinary()
|
||||
_ = yym12
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(x.AutoSelector)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
case "template":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Template = pkg2_v1.PodTemplateSpec{}
|
||||
} else {
|
||||
yyv11 := &x.Template
|
||||
yyv11.CodecDecodeSelf(d)
|
||||
yyv13 := &x.Template
|
||||
yyv13.CodecDecodeSelf(d)
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
@ -10232,16 +10284,16 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj12 int
|
||||
var yyb12 bool
|
||||
var yyhl12 bool = l >= 0
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
var yyj14 int
|
||||
var yyb14 bool
|
||||
var yyhl14 bool = l >= 0
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10254,20 +10306,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.Parallelism == nil {
|
||||
x.Parallelism = new(int32)
|
||||
}
|
||||
yym14 := z.DecBinary()
|
||||
_ = yym14
|
||||
yym16 := z.DecBinary()
|
||||
_ = yym16
|
||||
if false {
|
||||
} else {
|
||||
*((*int32)(x.Parallelism)) = int32(r.DecodeInt(32))
|
||||
}
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10280,20 +10332,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.Completions == nil {
|
||||
x.Completions = new(int32)
|
||||
}
|
||||
yym16 := z.DecBinary()
|
||||
_ = yym16
|
||||
yym18 := z.DecBinary()
|
||||
_ = yym18
|
||||
if false {
|
||||
} else {
|
||||
*((*int32)(x.Completions)) = int32(r.DecodeInt(32))
|
||||
}
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10306,20 +10358,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if x.ActiveDeadlineSeconds == nil {
|
||||
x.ActiveDeadlineSeconds = new(int64)
|
||||
}
|
||||
yym18 := z.DecBinary()
|
||||
_ = yym18
|
||||
yym20 := z.DecBinary()
|
||||
_ = yym20
|
||||
if false {
|
||||
} else {
|
||||
*((*int64)(x.ActiveDeadlineSeconds)) = int64(r.DecodeInt(64))
|
||||
}
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10334,13 +10386,39 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
}
|
||||
x.Selector.CodecDecodeSelf(d)
|
||||
}
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
if x.AutoSelector != nil {
|
||||
x.AutoSelector = nil
|
||||
}
|
||||
} else {
|
||||
if x.AutoSelector == nil {
|
||||
x.AutoSelector = new(bool)
|
||||
}
|
||||
yym23 := z.DecBinary()
|
||||
_ = yym23
|
||||
if false {
|
||||
} else {
|
||||
*((*bool)(x.AutoSelector)) = r.DecodeBool()
|
||||
}
|
||||
}
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb14 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -10348,21 +10426,21 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
if r.TryDecodeAsNil() {
|
||||
x.Template = pkg2_v1.PodTemplateSpec{}
|
||||
} else {
|
||||
yyv20 := &x.Template
|
||||
yyv20.CodecDecodeSelf(d)
|
||||
yyv24 := &x.Template
|
||||
yyv24.CodecDecodeSelf(d)
|
||||
}
|
||||
for {
|
||||
yyj12++
|
||||
if yyhl12 {
|
||||
yyb12 = yyj12 > l
|
||||
yyj14++
|
||||
if yyhl14 {
|
||||
yyb14 = yyj14 > l
|
||||
} else {
|
||||
yyb12 = r.CheckBreak()
|
||||
yyb14 = r.CheckBreak()
|
||||
}
|
||||
if yyb12 {
|
||||
if yyb14 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj12-1, "")
|
||||
z.DecStructFieldNotFound(yyj14-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
@ -20615,7 +20693,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) {
|
||||
|
||||
yyrg1 := len(yyv1) > 0
|
||||
yyv21 := yyv1
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 632)
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 640)
|
||||
if yyrt1 {
|
||||
if yyrl1 <= cap(yyv1) {
|
||||
yyv1 = yyv1[:yyrl1]
|
||||
|
@ -543,6 +543,7 @@ type JobSpec struct {
|
||||
// pod signals the success of all pods, and allows parallelism to have any positive
|
||||
// value. Setting to 1 means that parallelism is limited to 1 and the success of that
|
||||
// pod signals the success of the job.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md
|
||||
Completions *int32 `json:"completions,omitempty"`
|
||||
|
||||
// Optional duration in seconds relative to the startTime that the job may be active
|
||||
@ -550,9 +551,17 @@ type JobSpec struct {
|
||||
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
|
||||
|
||||
// Selector is a label query over pods that should match the pod count.
|
||||
// Normally, the system sets this field for you.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors
|
||||
Selector *LabelSelector `json:"selector,omitempty"`
|
||||
|
||||
// AutoSelector controls generation of pod labels and pod selectors.
|
||||
// It was not present in the original extensions/v1beta1 Job definition, but exists
|
||||
// to allow conversion from batch/v1 Jobs, where it corresponds to, but has the opposite
|
||||
// meaning as, ManualSelector.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/design/selector-generation.md
|
||||
AutoSelector *bool `json:"autoSelector,omitempty"`
|
||||
|
||||
// Template is the object that describes the pod that will be created when
|
||||
// executing a job.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md
|
||||
|
@ -417,9 +417,10 @@ func (JobList) SwaggerDoc() map[string]string {
|
||||
var map_JobSpec = map[string]string{
|
||||
"": "JobSpec describes how the job execution will look like.",
|
||||
"parallelism": "Parallelism specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
|
||||
"completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job.",
|
||||
"completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
|
||||
"activeDeadlineSeconds": "Optional duration in seconds relative to the startTime that the job may be active before the system tries to terminate it; value must be positive integer",
|
||||
"selector": "Selector is a label query over pods that should match the pod count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
|
||||
"selector": "Selector is a label query over pods that should match the pod count. Normally, the system sets this field for you. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
|
||||
"autoSelector": "AutoSelector controls generation of pod labels and pod selectors. It was not present in the original extensions/v1beta1 Job definition, but exists to allow conversion from batch/v1 Jobs, where it corresponds to, but has the opposite meaning as, ManualSelector. More info: http://releases.k8s.io/HEAD/docs/design/selector-generation.md",
|
||||
"template": "Template is the object that describes the pod that will be created when executing a job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
|
||||
}
|
||||
|
||||
|
@ -379,9 +379,62 @@ func ValidateThirdPartyResourceData(obj *extensions.ThirdPartyResourceData) fiel
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// TODO: generalize for other controller objects that will follow the same pattern, such as ReplicaSet and DaemonSet, and
|
||||
// move to new location. Replace extensions.Job with an interface.
|
||||
//
|
||||
// ValidateGeneratedSelector validates that the generated selector on a controller object match the controller object
|
||||
// metadata, and the labels on the pod template are as generated.
|
||||
func ValidateGeneratedSelector(obj *extensions.Job) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if obj.Spec.ManualSelector != nil && *obj.Spec.ManualSelector {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if obj.Spec.Selector == nil {
|
||||
return allErrs // This case should already have been checked in caller. No need for more errors.
|
||||
}
|
||||
|
||||
// If somehow uid was unset then we would get "controller-uid=" as the selector
|
||||
// which is bad.
|
||||
if obj.ObjectMeta.UID == "" {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("metadata").Child("uid"), ""))
|
||||
}
|
||||
|
||||
// If somehow uid was unset then we would get "controller-uid=" as the selector
|
||||
// which is bad.
|
||||
if obj.ObjectMeta.UID == "" {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("metadata").Child("uid"), ""))
|
||||
}
|
||||
|
||||
// If selector generation was requested, then expected labels must be
|
||||
// present on pod template, and much match job's uid and name. The
|
||||
// generated (not-manual) selectors/labels ensure no overlap with other
|
||||
// controllers. The manual mode allows orphaning, adoption,
|
||||
// backward-compatibility, and experimentation with new
|
||||
// labeling/selection schemes. Automatic selector generation should
|
||||
// have placed certain labels on the pod, but this could have failed if
|
||||
// the user added coflicting labels. Validate that the expected
|
||||
// generated ones are there.
|
||||
|
||||
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "controller-uid", string(obj.UID))...)
|
||||
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "job-name", string(obj.Name))...)
|
||||
expectedLabels := make(map[string]string)
|
||||
expectedLabels["controller-uid"] = string(obj.UID)
|
||||
expectedLabels["job-name"] = string(obj.Name)
|
||||
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
|
||||
if selector, err := unversioned.LabelSelectorAsSelector(obj.Spec.Selector); err == nil {
|
||||
if !selector.Matches(labels.Set(expectedLabels)) {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("selector"), obj.Spec.Selector, "`selector` not auto-generated"))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateJob(job *extensions.Job) field.ErrorList {
|
||||
// Jobs and rcs have the same name validation
|
||||
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidateGeneratedSelector(job)...)
|
||||
allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"))...)
|
||||
return allErrs
|
||||
}
|
||||
@ -404,6 +457,7 @@ func ValidateJobSpec(spec *extensions.JobSpec, fldPath *field.Path) field.ErrorL
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
|
||||
}
|
||||
|
||||
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
|
||||
if selector, err := unversioned.LabelSelectorAsSelector(spec.Selector); err == nil {
|
||||
labels := labels.Set(spec.Template.Labels)
|
||||
if !selector.Matches(labels) {
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/controller/podautoscaler"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
)
|
||||
|
||||
@ -975,12 +976,15 @@ func TestValidateDeploymentRollback(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateJob(t *testing.T) {
|
||||
validSelector := &unversioned.LabelSelector{
|
||||
validManualSelector := &unversioned.LabelSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
}
|
||||
validPodTemplateSpec := api.PodTemplateSpec{
|
||||
validGeneratedSelector := &unversioned.LabelSelector{
|
||||
MatchLabels: map[string]string{"collection-uid": "1a2b3c"},
|
||||
}
|
||||
validPodTemplateSpecForManual := api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector.MatchLabels,
|
||||
Labels: validManualSelector.MatchLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
@ -988,21 +992,45 @@ func TestValidateJob(t *testing.T) {
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}
|
||||
successCases := []extensions.Job{
|
||||
{
|
||||
validPodTemplateSpecForGenerated := api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validGeneratedSelector.MatchLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}
|
||||
successCases := map[string]extensions.Job{
|
||||
"manual selector": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: validSelector,
|
||||
Template: validPodTemplateSpec,
|
||||
Selector: validManualSelector,
|
||||
ManualSelector: newBool(true),
|
||||
Template: validPodTemplateSpecForManual,
|
||||
},
|
||||
},
|
||||
"generated selector": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: validGeneratedSelector,
|
||||
ManualSelector: newBool(true),
|
||||
Template: validPodTemplateSpecForGenerated,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
if errs := ValidateJob(&successCase); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
for k, v := range successCases {
|
||||
if errs := ValidateJob(&v); len(errs) != 0 {
|
||||
t.Errorf("expected success for %s: %v", k, errs)
|
||||
}
|
||||
}
|
||||
negative := -1
|
||||
@ -1012,51 +1040,59 @@ func TestValidateJob(t *testing.T) {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Parallelism: &negative,
|
||||
Selector: validSelector,
|
||||
Template: validPodTemplateSpec,
|
||||
Parallelism: &negative,
|
||||
ManualSelector: newBool(true),
|
||||
Template: validPodTemplateSpecForGenerated,
|
||||
},
|
||||
},
|
||||
"spec.completions:must be greater than or equal to 0": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Completions: &negative,
|
||||
Selector: validSelector,
|
||||
Template: validPodTemplateSpec,
|
||||
Completions: &negative,
|
||||
Selector: validManualSelector,
|
||||
ManualSelector: newBool(true),
|
||||
Template: validPodTemplateSpecForGenerated,
|
||||
},
|
||||
},
|
||||
"spec.activeDeadlineSeconds:must be greater than or equal to 0": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
ActiveDeadlineSeconds: &negative64,
|
||||
Selector: validSelector,
|
||||
Template: validPodTemplateSpec,
|
||||
Selector: validManualSelector,
|
||||
ManualSelector: newBool(true),
|
||||
Template: validPodTemplateSpecForGenerated,
|
||||
},
|
||||
},
|
||||
"spec.selector:Required value": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Template: validPodTemplateSpec,
|
||||
Template: validPodTemplateSpecForGenerated,
|
||||
},
|
||||
},
|
||||
"spec.template.metadata.labels: Invalid value: {\"y\":\"z\"}: `selector` does not match template `labels`": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: validSelector,
|
||||
Selector: validManualSelector,
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{"y": "z"},
|
||||
@ -1069,16 +1105,39 @@ func TestValidateJob(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
"spec.template.metadata.labels: Invalid value: {\"controller-uid\":\"4d5e6f\"}: `selector` does not match template `labels`": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: validManualSelector,
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{"controller-uid": "4d5e6f"},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"spec.template.spec.restartPolicy: Unsupported value": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: types.UID("1a2b3c"),
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: validSelector,
|
||||
Selector: validManualSelector,
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector.MatchLabels,
|
||||
Labels: validManualSelector.MatchLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
@ -2070,3 +2129,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
@ -269,6 +269,7 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object
|
||||
Selector: &unversioned.LabelSelector{
|
||||
MatchLabels: labels,
|
||||
},
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: labels,
|
||||
@ -607,3 +608,9 @@ func parseEnvs(envArray []string) ([]api.EnvVar, error) {
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
@ -749,6 +749,7 @@ func TestGenerateJob(t *testing.T) {
|
||||
Selector: &unversioned.LabelSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar", "baz": "blah"},
|
||||
},
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{"foo": "bar", "baz": "blah"},
|
||||
|
@ -53,6 +53,7 @@ func validNewJob() *extensions.Job {
|
||||
Selector: &unversioned.LabelSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
},
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{"a": "b"},
|
||||
@ -165,3 +166,9 @@ func TestWatch(t *testing.T) {
|
||||
}
|
||||
|
||||
// TODO: test update /status
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions/validation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
@ -60,9 +61,63 @@ func (jobStrategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
// Validate validates a new job.
|
||||
func (jobStrategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
|
||||
job := obj.(*extensions.Job)
|
||||
// TODO: move UID generation earlier and do this in defaulting logic?
|
||||
if job.Spec.ManualSelector == nil || *job.Spec.ManualSelector == false {
|
||||
generateSelector(job)
|
||||
}
|
||||
return validation.ValidateJob(job)
|
||||
}
|
||||
|
||||
// generateSelector adds a selector to a job and labels to its template
|
||||
// which can be used to uniquely identify the pods created by that job,
|
||||
// if the user has requested this behavior.
|
||||
func generateSelector(obj *extensions.Job) {
|
||||
if obj.Spec.Template.Labels == nil {
|
||||
obj.Spec.Template.Labels = make(map[string]string)
|
||||
}
|
||||
// The job-name label is unique except in cases that are expected to be
|
||||
// quite uncommon, and is more user friendly than uid. So, we add it as
|
||||
// a label.
|
||||
_, found := obj.Spec.Template.Labels["job-name"]
|
||||
if found {
|
||||
// User asked us to not automatically generate a selector and labels,
|
||||
// but set a possibly conflicting value. If there is a conflict,
|
||||
// we will reject in validation.
|
||||
} else {
|
||||
obj.Spec.Template.Labels["job-name"] = string(obj.ObjectMeta.Name)
|
||||
}
|
||||
// The controller-uid label makes the pods that belong to this job
|
||||
// only match this job.
|
||||
_, found = obj.Spec.Template.Labels["controller-uid"]
|
||||
if found {
|
||||
// User asked us to automatically generate a selector and labels,
|
||||
// but set a possibly conflicting value. If there is a conflict,
|
||||
// we will reject in validation.
|
||||
} else {
|
||||
obj.Spec.Template.Labels["controller-uid"] = string(obj.ObjectMeta.UID)
|
||||
}
|
||||
// Select the controller-uid label. This is sufficient for uniqueness.
|
||||
if obj.Spec.Selector == nil {
|
||||
obj.Spec.Selector = &unversioned.LabelSelector{}
|
||||
}
|
||||
if obj.Spec.Selector.MatchLabels == nil {
|
||||
obj.Spec.Selector.MatchLabels = make(map[string]string)
|
||||
}
|
||||
if _, found := obj.Spec.Selector.MatchLabels["controller-uid"]; !found {
|
||||
obj.Spec.Selector.MatchLabels["controller-uid"] = string(obj.ObjectMeta.UID)
|
||||
}
|
||||
// If the user specified matchLabel controller-uid=$WRONGUID, then it should fail
|
||||
// in validation, either because the selector does not match the pod template
|
||||
// (controller-uid=$WRONGUID does not match controller-uid=$UID, which we applied
|
||||
// above, or we will reject in validation because the template has the wrong
|
||||
// labels.
|
||||
}
|
||||
|
||||
// TODO: generalize generateSelector so it can work for other controller
|
||||
// objects such as ReplicaSet. Can use pkg/api/meta to generically get the
|
||||
// UID, but need some way to generically access the selector and pod labels
|
||||
// fields.
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (jobStrategy) Canonicalize(obj runtime.Object) {
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package job
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
@ -25,8 +26,15 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
func newBool(a bool) *bool {
|
||||
r := new(bool)
|
||||
*r = a
|
||||
return r
|
||||
}
|
||||
|
||||
func TestJobStrategy(t *testing.T) {
|
||||
ctx := api.NewDefaultContext()
|
||||
if !Strategy.NamespaceScoped() {
|
||||
@ -55,8 +63,9 @@ func TestJobStrategy(t *testing.T) {
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: validSelector,
|
||||
Template: validPodTemplateSpec,
|
||||
Selector: validSelector,
|
||||
Template: validPodTemplateSpec,
|
||||
ManualSelector: newBool(true),
|
||||
},
|
||||
Status: extensions.JobStatus{
|
||||
Active: 11,
|
||||
@ -93,6 +102,56 @@ func TestJobStrategy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobStrategyWithGeneration(t *testing.T) {
|
||||
ctx := api.NewDefaultContext()
|
||||
|
||||
theUID := types.UID("1a2b3c4d5e6f7g8h9i0k")
|
||||
|
||||
validPodTemplateSpec := api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}
|
||||
job := &extensions.Job{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob2",
|
||||
Namespace: api.NamespaceDefault,
|
||||
UID: theUID,
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: nil,
|
||||
Template: validPodTemplateSpec,
|
||||
},
|
||||
}
|
||||
|
||||
Strategy.PrepareForCreate(job)
|
||||
errs := Strategy.Validate(ctx, job)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected error validating %v", errs)
|
||||
}
|
||||
|
||||
// Validate the stuff that validation should have validated.
|
||||
if job.Spec.Selector == nil {
|
||||
t.Errorf("Selector not generated")
|
||||
}
|
||||
expectedLabels := make(map[string]string)
|
||||
expectedLabels["controller-uid"] = string(theUID)
|
||||
if !reflect.DeepEqual(job.Spec.Selector.MatchLabels, expectedLabels) {
|
||||
t.Errorf("Expected label selector not generated")
|
||||
}
|
||||
if job.Spec.Template.ObjectMeta.Labels == nil {
|
||||
t.Errorf("Expected template labels not generated")
|
||||
}
|
||||
if v, ok := job.Spec.Template.ObjectMeta.Labels["job-name"]; !ok || v != "myjob2" {
|
||||
t.Errorf("Expected template labels not present")
|
||||
}
|
||||
if v, ok := job.Spec.Template.ObjectMeta.Labels["controller-uid"]; !ok || v != string(theUID) {
|
||||
t.Errorf("Expected template labels not present: ok: %v, v: %v", ok, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobStatusStrategy(t *testing.T) {
|
||||
ctx := api.NewDefaultContext()
|
||||
if !StatusStrategy.NamespaceScoped() {
|
||||
|
@ -204,8 +204,9 @@ func newTestJob(behavior, name string, rPol api.RestartPolicy, parallelism, comp
|
||||
Name: name,
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Parallelism: ¶llelism,
|
||||
Completions: &completions,
|
||||
Parallelism: ¶llelism,
|
||||
Completions: &completions,
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{jobSelectorKey: name},
|
||||
@ -312,3 +313,9 @@ func waitForJobFail(c *client.Client, ns, jobName string) error {
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
@ -204,26 +204,15 @@ var jobV1 string = `
|
||||
"kind": "Job",
|
||||
"apiVersion": "batch/v1",
|
||||
"metadata": {
|
||||
"name": "pi",
|
||||
"labels": {
|
||||
"app": "pi"
|
||||
}
|
||||
"name": "pi"
|
||||
},
|
||||
"spec": {
|
||||
"parallelism": 1,
|
||||
"completions": 1,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "pi"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"name": "pi",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "pi"
|
||||
}
|
||||
"creationTimestamp": null
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
|
Loading…
Reference in New Issue
Block a user