mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 10:51:29 +00:00
Merge pull request #114930 from kannon92/add-new-labels
Add batch.kubernetes.io to labels created in the Job controller.
This commit is contained in:
commit
f3aebc85b9
@ -22,6 +22,11 @@ import (
|
|||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Unprefixed labels are reserved for end-users
|
||||||
|
// so we will add a batch.kubernetes.io to designate these labels as official Kubernetes labels.
|
||||||
|
// See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#label-selector-and-annotation-conventions
|
||||||
|
labelPrefix = "batch.kubernetes.io/"
|
||||||
// JobTrackingFinalizer is a finalizer for Job's pods. It prevents them from
|
// JobTrackingFinalizer is a finalizer for Job's pods. It prevents them from
|
||||||
// being deleted before being accounted in the Job status.
|
// being deleted before being accounted in the Job status.
|
||||||
//
|
//
|
||||||
@ -31,7 +36,15 @@ import (
|
|||||||
// 1.27+, one release after JobTrackingWithFinalizers graduates to GA, the
|
// 1.27+, one release after JobTrackingWithFinalizers graduates to GA, the
|
||||||
// apiserver and job controller will ignore this annotation and they will
|
// apiserver and job controller will ignore this annotation and they will
|
||||||
// always track jobs using finalizers.
|
// always track jobs using finalizers.
|
||||||
const JobTrackingFinalizer = "batch.kubernetes.io/job-tracking"
|
JobTrackingFinalizer = labelPrefix + "job-tracking"
|
||||||
|
// LegacyJobName and LegacyControllerUid are legacy labels that were set using unprefixed labels.
|
||||||
|
LegacyJobNameLabel = "job-name"
|
||||||
|
LegacyControllerUidLabel = "controller-uid"
|
||||||
|
// JobName is a user friendly way to refer to jobs and is set in the labels for jobs.
|
||||||
|
JobNameLabel = labelPrefix + LegacyJobNameLabel
|
||||||
|
// Controller UID is used for selectors and labels for jobs
|
||||||
|
ControllerUidLabel = labelPrefix + LegacyControllerUidLabel
|
||||||
|
)
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
@ -70,12 +70,12 @@ var (
|
|||||||
string(v1.ConditionUnknown))
|
string(v1.ConditionUnknown))
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateGeneratedSelector validates that the generated selector on a controller object match the controller object
|
// 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.
|
// metadata, and the labels on the pod template are as generated.
|
||||||
//
|
//
|
||||||
// TODO: generalize for other controller objects that will follow the same pattern, such as ReplicaSet and DaemonSet, and
|
// TODO: generalize for other controller objects that will follow the same pattern, such as ReplicaSet and DaemonSet, and
|
||||||
// move to new location. Replace batch.Job with an interface.
|
// move to new location. Replace batch.Job with an interface.
|
||||||
func ValidateGeneratedSelector(obj *batch.Job) field.ErrorList {
|
func validateGeneratedSelector(obj *batch.Job, validateBatchLabels bool) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
if obj.Spec.ManualSelector != nil && *obj.Spec.ManualSelector {
|
if obj.Spec.ManualSelector != nil && *obj.Spec.ManualSelector {
|
||||||
return allErrs
|
return allErrs
|
||||||
@ -100,12 +100,20 @@ func ValidateGeneratedSelector(obj *batch.Job) field.ErrorList {
|
|||||||
// have placed certain labels on the pod, but this could have failed if
|
// have placed certain labels on the pod, but this could have failed if
|
||||||
// the user added coflicting labels. Validate that the expected
|
// the user added coflicting labels. Validate that the expected
|
||||||
// generated ones are there.
|
// generated ones are there.
|
||||||
|
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), batch.LegacyControllerUidLabel, string(obj.UID))...)
|
||||||
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"), batch.LegacyJobNameLabel, string(obj.Name))...)
|
||||||
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 := make(map[string]string)
|
||||||
expectedLabels["controller-uid"] = string(obj.UID)
|
if validateBatchLabels {
|
||||||
expectedLabels["job-name"] = string(obj.Name)
|
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), batch.ControllerUidLabel, string(obj.UID))...)
|
||||||
|
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), batch.JobNameLabel, string(obj.Name))...)
|
||||||
|
expectedLabels[batch.ControllerUidLabel] = string(obj.UID)
|
||||||
|
expectedLabels[batch.JobNameLabel] = string(obj.Name)
|
||||||
|
}
|
||||||
|
// Labels created by the Kubernetes project should have a Kubernetes prefix.
|
||||||
|
// These labels are set due to legacy reasons.
|
||||||
|
|
||||||
|
expectedLabels[batch.LegacyControllerUidLabel] = string(obj.UID)
|
||||||
|
expectedLabels[batch.LegacyJobNameLabel] = string(obj.Name)
|
||||||
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
|
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
|
||||||
if selector, err := metav1.LabelSelectorAsSelector(obj.Spec.Selector); err == nil {
|
if selector, err := metav1.LabelSelectorAsSelector(obj.Spec.Selector); err == nil {
|
||||||
if !selector.Matches(labels.Set(expectedLabels)) {
|
if !selector.Matches(labels.Set(expectedLabels)) {
|
||||||
@ -120,7 +128,7 @@ func ValidateGeneratedSelector(obj *batch.Job) field.ErrorList {
|
|||||||
func ValidateJob(job *batch.Job, opts JobValidationOptions) field.ErrorList {
|
func ValidateJob(job *batch.Job, opts JobValidationOptions) field.ErrorList {
|
||||||
// Jobs and rcs have the same name validation
|
// Jobs and rcs have the same name validation
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateGeneratedSelector(job)...)
|
allErrs = append(allErrs, validateGeneratedSelector(job, opts.RequirePrefixedLabels)...)
|
||||||
allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"), opts.PodValidationOptions)...)
|
allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"), opts.PodValidationOptions)...)
|
||||||
if !opts.AllowTrackingAnnotation && hasJobTrackingAnnotation(job) {
|
if !opts.AllowTrackingAnnotation && hasJobTrackingAnnotation(job) {
|
||||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("metadata").Child("annotations").Key(batch.JobTrackingFinalizer), "cannot add this annotation"))
|
allErrs = append(allErrs, field.Forbidden(field.NewPath("metadata").Child("annotations").Key(batch.JobTrackingFinalizer), "cannot add this annotation"))
|
||||||
@ -596,4 +604,6 @@ type JobValidationOptions struct {
|
|||||||
AllowMutableSchedulingDirectives bool
|
AllowMutableSchedulingDirectives bool
|
||||||
// Allow elastic indexed jobs
|
// Allow elastic indexed jobs
|
||||||
AllowElasticIndexedJobs bool
|
AllowElasticIndexedJobs bool
|
||||||
|
// Require Job to have the label on batch.kubernetes.io/job-name and batch.kubernetes.io/controller-uid
|
||||||
|
RequirePrefixedLabels bool
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func getValidPodTemplateSpecForManual(selector *metav1.LabelSelector) api.PodTem
|
|||||||
|
|
||||||
func getValidGeneratedSelector() *metav1.LabelSelector {
|
func getValidGeneratedSelector() *metav1.LabelSelector {
|
||||||
return &metav1.LabelSelector{
|
return &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{"controller-uid": "1a2b3c", "job-name": "myjob"},
|
MatchLabels: map[string]string{batch.ControllerUidLabel: "1a2b3c", batch.LegacyControllerUidLabel: "1a2b3c", batch.JobNameLabel: "myjob", batch.LegacyJobNameLabel: "myjob"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +105,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
job batch.Job
|
job batch.Job
|
||||||
}{
|
}{
|
||||||
"valid pod failure policy": {
|
"valid pod failure policy": {
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
@ -159,6 +160,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"valid manual selector": {
|
"valid manual selector": {
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
@ -174,6 +176,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"valid generated selector": {
|
"valid generated selector": {
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
@ -187,6 +190,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"valid NonIndexed completion mode": {
|
"valid NonIndexed completion mode": {
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
@ -201,6 +205,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"valid Indexed completion mode": {
|
"valid Indexed completion mode": {
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
@ -219,6 +224,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
"valid job tracking annotation": {
|
"valid job tracking annotation": {
|
||||||
opts: JobValidationOptions{
|
opts: JobValidationOptions{
|
||||||
AllowTrackingAnnotation: true,
|
AllowTrackingAnnotation: true,
|
||||||
|
RequirePrefixedLabels: true,
|
||||||
},
|
},
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -235,6 +241,58 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"valid batch labels": {
|
||||||
|
opts: JobValidationOptions{
|
||||||
|
AllowTrackingAnnotation: true,
|
||||||
|
RequirePrefixedLabels: true,
|
||||||
|
},
|
||||||
|
job: batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
UID: types.UID("1a2b3c"),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
batch.JobTrackingFinalizer: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: validGeneratedSelector,
|
||||||
|
Template: validPodTemplateSpecForGenerated,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"do not allow new batch labels": {
|
||||||
|
opts: JobValidationOptions{
|
||||||
|
AllowTrackingAnnotation: true,
|
||||||
|
RequirePrefixedLabels: false,
|
||||||
|
},
|
||||||
|
job: batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
UID: types.UID("1a2b3c"),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
batch.JobTrackingFinalizer: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{batch.LegacyControllerUidLabel: "1a2b3c"},
|
||||||
|
},
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{batch.LegacyControllerUidLabel: "1a2b3c", batch.LegacyJobNameLabel: "myjob"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
InitContainers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
@ -245,8 +303,12 @@ func TestValidateJob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
negative := int32(-1)
|
negative := int32(-1)
|
||||||
negative64 := int64(-1)
|
negative64 := int64(-1)
|
||||||
errorCases := map[string]batch.Job{
|
errorCases := map[string]struct {
|
||||||
|
opts JobValidationOptions
|
||||||
|
job batch.Job
|
||||||
|
}{
|
||||||
`spec.podFailurePolicy.rules[0]: Invalid value: specifying one of OnExitCodes and OnPodConditions is required`: {
|
`spec.podFailurePolicy.rules[0]: Invalid value: specifying one of OnExitCodes and OnPodConditions is required`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -260,7 +322,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.values[1]: Duplicate value: 11`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.values[1]: Duplicate value: 11`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -278,7 +343,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.values: Too many: 256: must have at most 255 items`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.values: Too many: 256: must have at most 255 items`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -302,7 +370,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules: Too many: 21: must have at most 20 items`: {
|
`spec.podFailurePolicy.rules: Too many: 21: must have at most 20 items`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -324,7 +395,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onPodConditions: Too many: 21: must have at most 20 items`: {
|
`spec.podFailurePolicy.rules[0].onPodConditions: Too many: 21: must have at most 20 items`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -348,7 +422,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.values[2]: Duplicate value: 13`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.values[2]: Duplicate value: 13`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -366,7 +443,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.values: Invalid value: []int32{19, 11}: must be ordered`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.values: Invalid value: []int32{19, 11}: must be ordered`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -384,7 +464,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.values: Invalid value: []int32{}: at least one value is required`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.values: Invalid value: []int32{}: at least one value is required`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -402,7 +485,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].action: Required value: valid values: ["Count" "FailJob" "Ignore"]`: {
|
`spec.podFailurePolicy.rules[0].action: Required value: valid values: ["Count" "FailJob" "Ignore"]`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -420,7 +506,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.operator: Required value: valid values: ["In" "NotIn"]`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.operator: Required value: valid values: ["In" "NotIn"]`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -438,7 +527,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0]: Invalid value: specifying both OnExitCodes and OnPodConditions is not supported`: {
|
`spec.podFailurePolicy.rules[0]: Invalid value: specifying both OnExitCodes and OnPodConditions is not supported`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -463,7 +555,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.values[1]: Invalid value: 0: must not be 0 for the In operator`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.values[1]: Invalid value: 0: must not be 0 for the In operator`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -481,7 +576,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[1].onExitCodes.containerName: Invalid value: "xyz": must be one of the container or initContainer names in the pod template`: {
|
`spec.podFailurePolicy.rules[1].onExitCodes.containerName: Invalid value: "xyz": must be one of the container or initContainer names in the pod template`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -508,7 +606,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].action: Unsupported value: "UnknownAction": supported values: "Count", "FailJob", "Ignore"`: {
|
`spec.podFailurePolicy.rules[0].action: Unsupported value: "UnknownAction": supported values: "Count", "FailJob", "Ignore"`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -527,7 +628,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onExitCodes.operator: Unsupported value: "UnknownOperator": supported values: "In", "NotIn"`: {
|
`spec.podFailurePolicy.rules[0].onExitCodes.operator: Unsupported value: "UnknownOperator": supported values: "In", "NotIn"`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -545,7 +649,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onPodConditions[0].status: Required value: valid values: ["False" "True" "Unknown"]`: {
|
`spec.podFailurePolicy.rules[0].onPodConditions[0].status: Required value: valid values: ["False" "True" "Unknown"]`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -564,7 +671,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onPodConditions[0].status: Unsupported value: "UnknownStatus": supported values: "False", "True", "Unknown"`: {
|
`spec.podFailurePolicy.rules[0].onPodConditions[0].status: Unsupported value: "UnknownStatus": supported values: "False", "True", "Unknown"`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -584,7 +694,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onPodConditions[0].type: Invalid value: "": name part must be non-empty`: {
|
`spec.podFailurePolicy.rules[0].onPodConditions[0].type: Invalid value: "": name part must be non-empty`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -603,7 +716,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.podFailurePolicy.rules[0].onPodConditions[0].type: Invalid value: "Invalid Condition Type": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`: {
|
`spec.podFailurePolicy.rules[0].onPodConditions[0].type: Invalid value: "Invalid Condition Type": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -623,7 +739,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
`spec.template.spec.restartPolicy: Invalid value: "OnFailure": only "Never" is supported when podFailurePolicy is specified`: {
|
`spec.template.spec.restartPolicy: Invalid value: "OnFailure": only "Never" is supported when podFailurePolicy is specified`: {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: validJobObjectMeta,
|
ObjectMeta: validJobObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
@ -642,7 +761,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.parallelism:must be greater than or equal to 0": {
|
"spec.parallelism:must be greater than or equal to 0": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -654,7 +776,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.backoffLimit:must be greater than or equal to 0": {
|
"spec.backoffLimit:must be greater than or equal to 0": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -666,8 +791,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.completions:must be greater than or equal to 0": {
|
"spec.completions:must be greater than or equal to 0": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -679,7 +806,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.activeDeadlineSeconds:must be greater than or equal to 0": {
|
"spec.activeDeadlineSeconds:must be greater than or equal to 0": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -691,7 +821,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.selector:Required value": {
|
"spec.selector:Required value": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -701,7 +834,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.template.metadata.labels: Invalid value: map[string]string{\"y\":\"z\"}: `selector` does not match template `labels`": {
|
"spec.template.metadata.labels: Invalid value: map[string]string{\"y\":\"z\"}: `selector` does not match template `labels`": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -709,7 +845,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validManualSelector,
|
Selector: validManualSelector,
|
||||||
ManualSelector: pointer.Bool(true),
|
ManualSelector: pointer.BoolPtr(true),
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{"y": "z"},
|
Labels: map[string]string{"y": "z"},
|
||||||
@ -722,7 +858,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.template.metadata.labels: Invalid value: map[string]string{\"controller-uid\":\"4d5e6f\"}: `selector` does not match template `labels`": {
|
"spec.template.metadata.labels: Invalid value: map[string]string{\"controller-uid\":\"4d5e6f\"}: `selector` does not match template `labels`": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -730,7 +869,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validManualSelector,
|
Selector: validManualSelector,
|
||||||
ManualSelector: pointer.Bool(true),
|
ManualSelector: pointer.BoolPtr(true),
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{"controller-uid": "4d5e6f"},
|
Labels: map[string]string{"controller-uid": "4d5e6f"},
|
||||||
@ -743,7 +882,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.template.spec.restartPolicy: Required value": {
|
"spec.template.spec.restartPolicy: Required value": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -751,7 +893,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validManualSelector,
|
Selector: validManualSelector,
|
||||||
ManualSelector: pointer.Bool(true),
|
ManualSelector: pointer.BoolPtr(true),
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: validManualSelector.MatchLabels,
|
Labels: validManualSelector.MatchLabels,
|
||||||
@ -764,7 +906,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.template.spec.restartPolicy: Unsupported value": {
|
"spec.template.spec.restartPolicy: Unsupported value": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -772,7 +917,7 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: validManualSelector,
|
Selector: validManualSelector,
|
||||||
ManualSelector: pointer.Bool(true),
|
ManualSelector: pointer.BoolPtr(true),
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: validManualSelector.MatchLabels,
|
Labels: validManualSelector.MatchLabels,
|
||||||
@ -785,7 +930,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.ttlSecondsAfterFinished: must be greater than or equal to 0": {
|
"spec.ttlSecondsAfterFinished: must be greater than or equal to 0": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -797,7 +945,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.completions: Required value: when completion mode is Indexed": {
|
"spec.completions: Required value: when completion mode is Indexed": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -809,7 +960,10 @@ func TestValidateJob(t *testing.T) {
|
|||||||
CompletionMode: completionModePtr(batch.IndexedCompletion),
|
CompletionMode: completionModePtr(batch.IndexedCompletion),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"spec.parallelism: must be less than or equal to 100000 when completion mode is Indexed": {
|
"spec.parallelism: must be less than or equal to 100000 when completion mode is Indexed": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -819,11 +973,14 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Selector: validGeneratedSelector,
|
Selector: validGeneratedSelector,
|
||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
CompletionMode: completionModePtr(batch.IndexedCompletion),
|
CompletionMode: completionModePtr(batch.IndexedCompletion),
|
||||||
Completions: pointer.Int32(2),
|
Completions: pointer.Int32Ptr(2),
|
||||||
Parallelism: pointer.Int32(100001),
|
Parallelism: pointer.Int32Ptr(100001),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
"metadata.annotations[batch.kubernetes.io/job-tracking]: cannot add this annotation": {
|
"metadata.annotations[batch.kubernetes.io/job-tracking]: cannot add this annotation": {
|
||||||
|
job: batch.Job{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "myjob",
|
Name: "myjob",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -837,11 +994,106 @@ func TestValidateJob(t *testing.T) {
|
|||||||
Template: validPodTemplateSpecForGenerated,
|
Template: validPodTemplateSpecForGenerated,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
|
"spec.template.metadata.labels[controller-uid]: Required value: must be '1a2b3c'": {
|
||||||
|
job: batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
UID: types.UID("1a2b3c"),
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{batch.LegacyControllerUidLabel: "1a2b3c"},
|
||||||
|
},
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{batch.LegacyJobNameLabel: "myjob"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
InitContainers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
opts: JobValidationOptions{},
|
||||||
|
},
|
||||||
|
"metadata.uid: Required value": {
|
||||||
|
job: batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{batch.LegacyControllerUidLabel: "test"},
|
||||||
|
},
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{batch.LegacyJobNameLabel: "myjob"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
InitContainers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
opts: JobValidationOptions{},
|
||||||
|
},
|
||||||
|
"spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{\"a\":\"b\"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: `selector` not auto-generated": {
|
||||||
|
job: batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
UID: types.UID("1a2b3c"),
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{"a": "b"},
|
||||||
|
},
|
||||||
|
Template: validPodTemplateSpecForGenerated,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
|
"spec.template.metadata.labels[batch.kubernetes.io/controller-uid]: Required value: must be '1a2b3c'": {
|
||||||
|
job: batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
UID: types.UID("1a2b3c"),
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{batch.ControllerUidLabel: "1a2b3c"},
|
||||||
|
},
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{batch.JobNameLabel: "myjob", batch.LegacyControllerUidLabel: "1a2b3c", batch.LegacyJobNameLabel: "myjob"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
InitContainers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
errs := ValidateJob(&v, JobValidationOptions{})
|
errs := ValidateJob(&v.job, v.opts)
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %s", k)
|
t.Errorf("expected failure for %s", k)
|
||||||
} else {
|
} else {
|
||||||
|
@ -170,6 +170,7 @@ func validationOptionsForJob(newJob, oldJob *batch.Job) batchvalidation.JobValid
|
|||||||
PodValidationOptions: pod.GetValidationOptionsFromPodTemplate(newPodTemplate, oldPodTemplate),
|
PodValidationOptions: pod.GetValidationOptionsFromPodTemplate(newPodTemplate, oldPodTemplate),
|
||||||
AllowTrackingAnnotation: true,
|
AllowTrackingAnnotation: true,
|
||||||
AllowElasticIndexedJobs: utilfeature.DefaultFeatureGate.Enabled(features.ElasticIndexedJob),
|
AllowElasticIndexedJobs: utilfeature.DefaultFeatureGate.Enabled(features.ElasticIndexedJob),
|
||||||
|
RequirePrefixedLabels: true,
|
||||||
}
|
}
|
||||||
if oldJob != nil {
|
if oldJob != nil {
|
||||||
opts.AllowInvalidLabelValueInSelector = opts.AllowInvalidLabelValueInSelector || metav1validation.LabelSelectorHasInvalidLabelValue(oldJob.Spec.Selector)
|
opts.AllowInvalidLabelValueInSelector = opts.AllowInvalidLabelValueInSelector || metav1validation.LabelSelectorHasInvalidLabelValue(oldJob.Spec.Selector)
|
||||||
@ -184,6 +185,12 @@ func validationOptionsForJob(newJob, oldJob *batch.Job) batchvalidation.JobValid
|
|||||||
suspended := oldJob.Spec.Suspend != nil && *oldJob.Spec.Suspend
|
suspended := oldJob.Spec.Suspend != nil && *oldJob.Spec.Suspend
|
||||||
notStarted := oldJob.Status.StartTime == nil
|
notStarted := oldJob.Status.StartTime == nil
|
||||||
opts.AllowMutableSchedulingDirectives = suspended && notStarted
|
opts.AllowMutableSchedulingDirectives = suspended && notStarted
|
||||||
|
|
||||||
|
// Validation should not fail jobs if they don't have the new labels.
|
||||||
|
// This can be removed once we have high confidence that both labels exist (1.30 at least)
|
||||||
|
_, hadJobName := oldJob.Spec.Template.Labels[batch.JobNameLabel]
|
||||||
|
_, hadControllerUid := oldJob.Spec.Template.Labels[batch.ControllerUidLabel]
|
||||||
|
opts.RequirePrefixedLabels = hadJobName && hadControllerUid
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
@ -209,21 +216,28 @@ func generateSelector(obj *batch.Job) {
|
|||||||
// The job-name label is unique except in cases that are expected to be
|
// 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
|
// quite uncommon, and is more user friendly than uid. So, we add it as
|
||||||
// a label.
|
// a label.
|
||||||
_, found := obj.Spec.Template.Labels["job-name"]
|
jobNameLabels := []string{batch.LegacyJobNameLabel, batch.JobNameLabel}
|
||||||
|
for _, value := range jobNameLabels {
|
||||||
|
_, found := obj.Spec.Template.Labels[value]
|
||||||
if found {
|
if found {
|
||||||
// User asked us to automatically generate a selector, but set manual labels.
|
// User asked us to automatically generate a selector, but set manual labels.
|
||||||
// If there is a conflict, we will reject in validation.
|
// If there is a conflict, we will reject in validation.
|
||||||
} else {
|
} else {
|
||||||
obj.Spec.Template.Labels["job-name"] = string(obj.ObjectMeta.Name)
|
obj.Spec.Template.Labels[value] = string(obj.ObjectMeta.Name)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The controller-uid label makes the pods that belong to this job
|
// The controller-uid label makes the pods that belong to this job
|
||||||
// only match this job.
|
// only match this job.
|
||||||
_, found = obj.Spec.Template.Labels["controller-uid"]
|
controllerUidLabels := []string{batch.LegacyControllerUidLabel, batch.ControllerUidLabel}
|
||||||
|
for _, value := range controllerUidLabels {
|
||||||
|
_, found := obj.Spec.Template.Labels[value]
|
||||||
if found {
|
if found {
|
||||||
// User asked us to automatically generate a selector, but set manual labels.
|
// User asked us to automatically generate a selector, but set manual labels.
|
||||||
// If there is a conflict, we will reject in validation.
|
// If there is a conflict, we will reject in validation.
|
||||||
} else {
|
} else {
|
||||||
obj.Spec.Template.Labels["controller-uid"] = string(obj.ObjectMeta.UID)
|
obj.Spec.Template.Labels[value] = string(obj.ObjectMeta.UID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Select the controller-uid label. This is sufficient for uniqueness.
|
// Select the controller-uid label. This is sufficient for uniqueness.
|
||||||
if obj.Spec.Selector == nil {
|
if obj.Spec.Selector == nil {
|
||||||
@ -232,8 +246,9 @@ func generateSelector(obj *batch.Job) {
|
|||||||
if obj.Spec.Selector.MatchLabels == nil {
|
if obj.Spec.Selector.MatchLabels == nil {
|
||||||
obj.Spec.Selector.MatchLabels = make(map[string]string)
|
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 _, found := obj.Spec.Selector.MatchLabels[batch.ControllerUidLabel]; !found {
|
||||||
|
obj.Spec.Selector.MatchLabels[batch.ControllerUidLabel] = string(obj.ObjectMeta.UID)
|
||||||
}
|
}
|
||||||
// If the user specified matchLabel controller-uid=$WRONGUID, then it should fail
|
// If the user specified matchLabel controller-uid=$WRONGUID, then it should fail
|
||||||
// in validation, either because the selector does not match the pod template
|
// in validation, either because the selector does not match the pod template
|
||||||
|
@ -670,6 +670,66 @@ func TestJobStrategy_ValidateUpdate(t *testing.T) {
|
|||||||
job.Annotations["hello"] = "world"
|
job.Annotations["hello"] = "world"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"old job has no batch.kubernetes.io labels": {
|
||||||
|
job: &batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
UID: "test",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
ResourceVersion: "10",
|
||||||
|
Annotations: map[string]string{"hello": "world"},
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{batch.LegacyControllerUidLabel: "test"},
|
||||||
|
},
|
||||||
|
Parallelism: pointer.Int32(4),
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{batch.LegacyJobNameLabel: "myjob", batch.LegacyControllerUidLabel: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: func(job *batch.Job) {
|
||||||
|
job.Annotations["hello"] = "world"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"old job has all labels": {
|
||||||
|
job: &batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
UID: "test",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
ResourceVersion: "10",
|
||||||
|
Annotations: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{batch.ControllerUidLabel: "test"},
|
||||||
|
},
|
||||||
|
Parallelism: pointer.Int32(4),
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{batch.LegacyJobNameLabel: "myjob", batch.JobNameLabel: "myjob", batch.LegacyControllerUidLabel: "test", batch.ControllerUidLabel: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: func(job *batch.Job) {
|
||||||
|
job.Annotations["hello"] = "world"
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
@ -872,7 +932,8 @@ func TestJobStrategy_Validate(t *testing.T) {
|
|||||||
validSelector := &metav1.LabelSelector{
|
validSelector := &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{"a": "b"},
|
MatchLabels: map[string]string{"a": "b"},
|
||||||
}
|
}
|
||||||
|
validLabels := map[string]string{batch.LegacyJobNameLabel: "myjob2", batch.JobNameLabel: "myjob2", batch.LegacyControllerUidLabel: string(theUID), batch.ControllerUidLabel: string(theUID)}
|
||||||
|
labelsWithNonBatch := map[string]string{"a": "b", batch.LegacyJobNameLabel: "myjob2", batch.JobNameLabel: "myjob2", batch.LegacyControllerUidLabel: string(theUID), batch.ControllerUidLabel: string(theUID)}
|
||||||
validPodSpec := api.PodSpec{
|
validPodSpec := api.PodSpec{
|
||||||
RestartPolicy: api.RestartPolicyOnFailure,
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
DNSPolicy: api.DNSClusterFirst,
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
@ -903,7 +964,7 @@ func TestJobStrategy_Validate(t *testing.T) {
|
|||||||
wantJob: &batch.Job{
|
wantJob: &batch.Job{
|
||||||
ObjectMeta: validObjectMeta,
|
ObjectMeta: validObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}},
|
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{batch.ControllerUidLabel: string(theUID)}},
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: validSelector.MatchLabels,
|
Labels: validSelector.MatchLabels,
|
||||||
@ -924,10 +985,10 @@ func TestJobStrategy_Validate(t *testing.T) {
|
|||||||
wantJob: &batch.Job{
|
wantJob: &batch.Job{
|
||||||
ObjectMeta: validObjectMeta,
|
ObjectMeta: validObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}},
|
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{batch.ControllerUidLabel: string(theUID)}},
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{"job-name": "myjob2", "controller-uid": string(theUID)},
|
Labels: validLabels,
|
||||||
},
|
},
|
||||||
Spec: validPodSpec,
|
Spec: validPodSpec,
|
||||||
}},
|
}},
|
||||||
@ -940,7 +1001,7 @@ func TestJobStrategy_Validate(t *testing.T) {
|
|||||||
Selector: nil,
|
Selector: nil,
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)},
|
Labels: labelsWithNonBatch,
|
||||||
},
|
},
|
||||||
Spec: validPodSpec,
|
Spec: validPodSpec,
|
||||||
}},
|
}},
|
||||||
@ -948,10 +1009,10 @@ func TestJobStrategy_Validate(t *testing.T) {
|
|||||||
wantJob: &batch.Job{
|
wantJob: &batch.Job{
|
||||||
ObjectMeta: validObjectMeta,
|
ObjectMeta: validObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}},
|
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{batch.ControllerUidLabel: string(theUID)}},
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)},
|
Labels: labelsWithNonBatch,
|
||||||
},
|
},
|
||||||
Spec: validPodSpec,
|
Spec: validPodSpec,
|
||||||
}},
|
}},
|
||||||
@ -1007,10 +1068,10 @@ func TestJobStrategy_Validate(t *testing.T) {
|
|||||||
wantJob: &batch.Job{
|
wantJob: &batch.Job{
|
||||||
ObjectMeta: validObjectMeta,
|
ObjectMeta: validObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}},
|
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{batch.ControllerUidLabel: string(theUID)}},
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)},
|
Labels: labelsWithNonBatch,
|
||||||
},
|
},
|
||||||
Spec: validPodSpec,
|
Spec: validPodSpec,
|
||||||
},
|
},
|
||||||
@ -1042,10 +1103,10 @@ func TestJobStrategy_Validate(t *testing.T) {
|
|||||||
wantJob: &batch.Job{
|
wantJob: &batch.Job{
|
||||||
ObjectMeta: validObjectMeta,
|
ObjectMeta: validObjectMeta,
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}},
|
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{batch.ControllerUidLabel: string(theUID)}},
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)},
|
Labels: labelsWithNonBatch,
|
||||||
},
|
},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
RestartPolicy: api.RestartPolicyOnFailure,
|
RestartPolicy: api.RestartPolicyOnFailure,
|
||||||
|
@ -23,8 +23,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
JobCompletionIndexAnnotation = "batch.kubernetes.io/job-completion-index"
|
// All Kubernetes labels need to be prefixed with Kubernetes to distinguish them from end-user labels
|
||||||
|
// More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#label-selector-and-annotation-conventions
|
||||||
|
labelPrefix = "batch.kubernetes.io/"
|
||||||
|
|
||||||
|
JobCompletionIndexAnnotation = labelPrefix + "job-completion-index"
|
||||||
// JobTrackingFinalizer is a finalizer for Job's pods. It prevents them from
|
// JobTrackingFinalizer is a finalizer for Job's pods. It prevents them from
|
||||||
// being deleted before being accounted in the Job status.
|
// being deleted before being accounted in the Job status.
|
||||||
//
|
//
|
||||||
@ -34,7 +37,14 @@ const (
|
|||||||
// 1.27+, one release after JobTrackingWithFinalizers graduates to GA, the
|
// 1.27+, one release after JobTrackingWithFinalizers graduates to GA, the
|
||||||
// apiserver and job controller will ignore this annotation and they will
|
// apiserver and job controller will ignore this annotation and they will
|
||||||
// always track jobs using finalizers.
|
// always track jobs using finalizers.
|
||||||
JobTrackingFinalizer = "batch.kubernetes.io/job-tracking"
|
JobTrackingFinalizer = labelPrefix + "job-tracking"
|
||||||
|
// The Job labels will use batch.kubernetes.io as a prefix for all labels
|
||||||
|
// Historically the job controller uses unprefixed labels for job-name and controller-uid and
|
||||||
|
// Kubernetes continutes to recognize those unprefixed labels for consistency.
|
||||||
|
JobNameLabel = labelPrefix + "job-name"
|
||||||
|
// ControllerUid is used to programatically get pods corresponding to a Job.
|
||||||
|
// There is a corresponding label without the batch.kubernetes.io that we support for legacy reasons.
|
||||||
|
ControllerUidLabel = labelPrefix + "controller-uid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// +genclient
|
// +genclient
|
||||||
|
Loading…
Reference in New Issue
Block a user