From a1a5868a5ae8c18df39b386d2424ff04c89826fb Mon Sep 17 00:00:00 2001 From: Aldo Culquicondor Date: Wed, 30 Dec 2020 11:42:01 -0500 Subject: [PATCH] Add Job.spec.completionMode and Job.status.completedIndexes And IndexedJob feature gate, disabled by default. Update JobDescriber --- api/openapi-spec/swagger.json | 8 + pkg/apis/batch/fuzzer/fuzzer.go | 6 +- pkg/apis/batch/types.go | 48 +++++ pkg/apis/batch/v1/defaults.go | 3 + pkg/apis/batch/v1/defaults_test.go | 116 +++++----- pkg/apis/batch/v1/zz_generated.conversion.go | 4 + pkg/apis/batch/validation/validation.go | 20 ++ pkg/apis/batch/validation/validation_test.go | 82 ++++++- pkg/features/kube_features.go | 7 + pkg/registry/batch/job/strategy.go | 8 + pkg/registry/batch/job/strategy_test.go | 38 +++- .../src/k8s.io/api/batch/v1/generated.pb.go | 203 ++++++++++++------ .../src/k8s.io/api/batch/v1/generated.proto | 32 +++ staging/src/k8s.io/api/batch/v1/types.go | 50 +++++ .../batch/v1/types_swagger_doc_generated.go | 16 +- .../api/testdata/HEAD/batch.v1.Job.json | 18 +- .../k8s.io/api/testdata/HEAD/batch.v1.Job.pb | Bin 6669 -> 6710 bytes .../api/testdata/HEAD/batch.v1.Job.yaml | 16 +- .../testdata/HEAD/batch.v1beta1.CronJob.json | 9 +- .../testdata/HEAD/batch.v1beta1.CronJob.pb | Bin 7369 -> 7394 bytes .../testdata/HEAD/batch.v1beta1.CronJob.yaml | 7 +- .../HEAD/batch.v1beta1.JobTemplate.json | 3 +- .../HEAD/batch.v1beta1.JobTemplate.pb | Bin 7315 -> 7328 bytes .../HEAD/batch.v1beta1.JobTemplate.yaml | 1 + .../v1.19.0/batch.v1.Job.after_roundtrip.pb | Bin 0 -> 6673 bytes .../batch.v1beta1.CronJob.after_roundtrip.pb | Bin 0 -> 7371 bytes ...tch.v1beta1.JobTemplate.after_roundtrip.pb | Bin 0 -> 7317 bytes .../batch.v2alpha1.CronJob.after_roundtrip.pb | Bin 0 -> 7372 bytes ...ch.v2alpha1.JobTemplate.after_roundtrip.pb | Bin 0 -> 7318 bytes .../v1.20.0/batch.v1.Job.after_roundtrip.pb | Bin 0 -> 6673 bytes .../batch.v1beta1.CronJob.after_roundtrip.pb | Bin 0 -> 7371 bytes ...tch.v1beta1.JobTemplate.after_roundtrip.pb | Bin 0 -> 7317 bytes .../batch.v2alpha1.CronJob.after_roundtrip.pb | Bin 0 -> 7372 bytes ...ch.v2alpha1.JobTemplate.after_roundtrip.pb | Bin 0 -> 7318 bytes .../k8s.io/kubectl/pkg/describe/describe.go | 22 ++ .../kubectl/pkg/describe/describe_test.go | 82 +++++++ 36 files changed, 642 insertions(+), 157 deletions(-) create mode 100644 staging/src/k8s.io/api/testdata/v1.19.0/batch.v1.Job.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.CronJob.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.JobTemplate.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.CronJob.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.20.0/batch.v1.Job.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.CronJob.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.JobTemplate.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.CronJob.after_roundtrip.pb create mode 100644 staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index fd107f2ea009..a7b3901e5cae 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -4247,6 +4247,10 @@ "format": "int32", "type": "integer" }, + "completionMode": { + "description": "CompletionMode specifies how Pod completions are tracked. It can be `NonIndexed` (default) or `Indexed`.\n\n`NonIndexed` means that the Job is considered complete when there have been .spec.completions successfully completed Pods. Each Pod completion is homologous to each other.\n\n`Indexed` means that the Pods of a Job get an associated completion index from 0 to (.spec.completions - 1), available in the annotation batch.alpha.kubernetes.io/job-completion-index. The Job is considered complete when there is one successfully completed Pod for each index. When value is `Indexed`, .spec.completions must be specified and `.spec.parallelism` must be less than or equal to 10^5.\n\nThis field is alpha-level and is only honored by servers that enable the IndexedJob feature gate. More completion modes can be added in the future. If the Job controller observes a mode that it doesn't recognize, the controller skips updates for the Job.", + "type": "string" + }, "completions": { "description": "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: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", "format": "int32", @@ -4288,6 +4292,10 @@ "format": "int32", "type": "integer" }, + "completedIndexes": { + "description": "CompletedIndexes holds the completed indexes when .spec.completionMode = \"Indexed\" in a text format. The indexes are represented as decimal integers separated by commas. The numbers are listed in increasing order. Three or more consecutive numbers are compressed and represented by the first and last element of the series, separated by a hyphen. For example, if the completed indexes are 1, 3, 4, 5 and 7, they are represented as \"1,3-5,7\".", + "type": "string" + }, "completionTime": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully." diff --git a/pkg/apis/batch/fuzzer/fuzzer.go b/pkg/apis/batch/fuzzer/fuzzer.go index b6f82a2b1e30..a9c947d92cd4 100644 --- a/pkg/apis/batch/fuzzer/fuzzer.go +++ b/pkg/apis/batch/fuzzer/fuzzer.go @@ -18,7 +18,6 @@ package fuzzer import ( fuzz "github.com/google/gofuzz" - runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/apis/batch" ) @@ -53,6 +52,11 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { } else { j.ManualSelector = nil } + if c.Rand.Int31()%2 == 0 { + j.CompletionMode = batch.NonIndexedCompletion + } else { + j.CompletionMode = batch.IndexedCompletion + } }, func(sj *batch.CronJobSpec, c fuzz.Continue) { c.FuzzNoCustom(sj) diff --git a/pkg/apis/batch/types.go b/pkg/apis/batch/types.go index 396f6429a94c..570ebfd97f1c 100644 --- a/pkg/apis/batch/types.go +++ b/pkg/apis/batch/types.go @@ -85,6 +85,22 @@ type JobTemplateSpec struct { Spec JobSpec } +// CompletionMode specifies how Pod completions of a Job are tracked. +type CompletionMode string + +const ( + // NonIndexedCompletion is a Job completion mode. In this mode, the Job is + // considered complete when there have been .spec.completions + // successfully completed Pods. Pod completions are homologous to each other. + NonIndexedCompletion CompletionMode = "NonIndexed" + + // IndexedCompletion is a Job completion mode. In this mode, the Pods of a + // Job get an associated completion index from 0 to (.spec.completions - 1). + // The Job is considered complete when a Pod completes for each completion + // index. + IndexedCompletion CompletionMode = "Indexed" +) + // JobSpec describes how the job execution will look like. type JobSpec struct { @@ -149,6 +165,28 @@ type JobSpec struct { // TTLAfterFinished feature. // +optional TTLSecondsAfterFinished *int32 + + // CompletionMode specifies how Pod completions are tracked. It can be + // `NonIndexed` (default) or `Indexed`. + // + // `NonIndexed` means that the Job is considered complete when there have + // been .spec.completions successfully completed Pods. Each Pod completion is + // homologous to each other. + // + // `Indexed` means that the Pods of a + // Job get an associated completion index from 0 to (.spec.completions - 1), + // available in the annotation batch.alpha.kubernetes.io/job-completion-index. + // The Job is considered complete when there is one successfully completed Pod + // for each index. + // When value is `Indexed`, .spec.completions must be specified and + // `.spec.parallelism` must be less than or equal to 10^5. + // + // This field is alpha-level and is only honored by servers that enable the + // IndexedJob feature gate. More completion modes can be added in the future. + // If the Job controller observes a mode that it doesn't recognize, the + // controller skips updates for the Job. + // +optional + CompletionMode CompletionMode } // JobStatus represents the current state of a Job. @@ -183,6 +221,16 @@ type JobStatus struct { // The number of pods which reached phase Failed. // +optional Failed int32 + + // CompletedIndexes holds the completed indexes when .spec.completionMode = + // "Indexed" in a text format. The indexes are represented as decimal integers + // separated by commas. The numbers are listed in increasing order. Three or + // more consecutive numbers are compressed and represented by the first and + // last element of the series, separated by a hyphen. + // For example, if the completed indexes are 1, 3, 4, 5 and 7, they are + // represented as "1,3-5,7". + // +optional + CompletedIndexes string } // JobConditionType is a valid value for JobCondition.Type diff --git a/pkg/apis/batch/v1/defaults.go b/pkg/apis/batch/v1/defaults.go index 72e7e2a9d5bb..f25ef7d6f17e 100644 --- a/pkg/apis/batch/v1/defaults.go +++ b/pkg/apis/batch/v1/defaults.go @@ -46,4 +46,7 @@ func SetDefaults_Job(obj *batchv1.Job) { if labels != nil && len(obj.Labels) == 0 { obj.Labels = labels } + if len(obj.Spec.CompletionMode) == 0 { + obj.Spec.CompletionMode = batchv1.NonIndexedCompletion + } } diff --git a/pkg/apis/batch/v1/defaults_test.go b/pkg/apis/batch/v1/defaults_test.go index 0f9c8c5c1797..e7c7adead3c0 100644 --- a/pkg/apis/batch/v1/defaults_test.go +++ b/pkg/apis/batch/v1/defaults_test.go @@ -21,15 +21,15 @@ import ( "testing" batchv1 "k8s.io/api/batch/v1" - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/api/legacyscheme" _ "k8s.io/kubernetes/pkg/apis/batch/install" - . "k8s.io/kubernetes/pkg/apis/batch/v1" _ "k8s.io/kubernetes/pkg/apis/core/install" utilpointer "k8s.io/utils/pointer" + + . "k8s.io/kubernetes/pkg/apis/batch/v1" ) func TestSetDefaultJob(t *testing.T) { @@ -49,9 +49,10 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(1), - Parallelism: utilpointer.Int32Ptr(1), - BackoffLimit: utilpointer.Int32Ptr(6), + Completions: utilpointer.Int32Ptr(1), + Parallelism: utilpointer.Int32Ptr(1), + BackoffLimit: utilpointer.Int32Ptr(6), + CompletionMode: batchv1.NonIndexedCompletion, }, }, expectLabels: true, @@ -69,9 +70,10 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(1), - Parallelism: utilpointer.Int32Ptr(1), - BackoffLimit: utilpointer.Int32Ptr(6), + Completions: utilpointer.Int32Ptr(1), + Parallelism: utilpointer.Int32Ptr(1), + BackoffLimit: utilpointer.Int32Ptr(6), + CompletionMode: batchv1.NonIndexedCompletion, }, }, }, @@ -86,8 +88,9 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Parallelism: utilpointer.Int32Ptr(0), - BackoffLimit: utilpointer.Int32Ptr(6), + Parallelism: utilpointer.Int32Ptr(0), + BackoffLimit: utilpointer.Int32Ptr(6), + CompletionMode: batchv1.NonIndexedCompletion, }, }, expectLabels: true, @@ -103,8 +106,9 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Parallelism: utilpointer.Int32Ptr(2), - BackoffLimit: utilpointer.Int32Ptr(6), + Parallelism: utilpointer.Int32Ptr(2), + BackoffLimit: utilpointer.Int32Ptr(6), + CompletionMode: batchv1.NonIndexedCompletion, }, }, expectLabels: true, @@ -120,9 +124,10 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(2), - Parallelism: utilpointer.Int32Ptr(1), - BackoffLimit: utilpointer.Int32Ptr(6), + Completions: utilpointer.Int32Ptr(2), + Parallelism: utilpointer.Int32Ptr(1), + BackoffLimit: utilpointer.Int32Ptr(6), + CompletionMode: batchv1.NonIndexedCompletion, }, }, expectLabels: true, @@ -138,9 +143,10 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(1), - Parallelism: utilpointer.Int32Ptr(1), - BackoffLimit: utilpointer.Int32Ptr(5), + Completions: utilpointer.Int32Ptr(1), + Parallelism: utilpointer.Int32Ptr(1), + BackoffLimit: utilpointer.Int32Ptr(5), + CompletionMode: batchv1.NonIndexedCompletion, }, }, expectLabels: true, @@ -148,9 +154,10 @@ func TestSetDefaultJob(t *testing.T) { "All set -> no change": { original: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(8), - Parallelism: utilpointer.Int32Ptr(9), - BackoffLimit: utilpointer.Int32Ptr(10), + Completions: utilpointer.Int32Ptr(8), + Parallelism: utilpointer.Int32Ptr(9), + BackoffLimit: utilpointer.Int32Ptr(10), + CompletionMode: batchv1.NonIndexedCompletion, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, }, @@ -158,9 +165,10 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(8), - Parallelism: utilpointer.Int32Ptr(9), - BackoffLimit: utilpointer.Int32Ptr(10), + Completions: utilpointer.Int32Ptr(8), + Parallelism: utilpointer.Int32Ptr(9), + BackoffLimit: utilpointer.Int32Ptr(10), + CompletionMode: batchv1.NonIndexedCompletion, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, }, @@ -171,9 +179,10 @@ func TestSetDefaultJob(t *testing.T) { "All set, flipped -> no change": { original: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(11), - Parallelism: utilpointer.Int32Ptr(10), - BackoffLimit: utilpointer.Int32Ptr(9), + Completions: utilpointer.Int32Ptr(11), + Parallelism: utilpointer.Int32Ptr(10), + BackoffLimit: utilpointer.Int32Ptr(9), + CompletionMode: batchv1.IndexedCompletion, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, }, @@ -181,9 +190,10 @@ func TestSetDefaultJob(t *testing.T) { }, expected: &batchv1.Job{ Spec: batchv1.JobSpec{ - Completions: utilpointer.Int32Ptr(11), - Parallelism: utilpointer.Int32Ptr(10), - BackoffLimit: utilpointer.Int32Ptr(9), + Completions: utilpointer.Int32Ptr(11), + Parallelism: utilpointer.Int32Ptr(10), + BackoffLimit: utilpointer.Int32Ptr(9), + CompletionMode: batchv1.IndexedCompletion, }, }, expectLabels: true, @@ -191,37 +201,41 @@ func TestSetDefaultJob(t *testing.T) { } for name, test := range tests { - original := test.original - expected := test.expected - obj2 := roundTrip(t, runtime.Object(original)) - actual, ok := obj2.(*batchv1.Job) - if !ok { - t.Errorf("%s: unexpected object: %v", name, actual) - t.FailNow() - } + t.Run(name, func(t *testing.T) { - validateDefaultInt32(t, name, "Completions", actual.Spec.Completions, expected.Spec.Completions) - validateDefaultInt32(t, name, "Parallelism", actual.Spec.Parallelism, expected.Spec.Parallelism) - validateDefaultInt32(t, name, "BackoffLimit", actual.Spec.BackoffLimit, expected.Spec.BackoffLimit) - - if test.expectLabels != reflect.DeepEqual(actual.Labels, actual.Spec.Template.Labels) { - if test.expectLabels { - t.Errorf("%s: expected: %v, got: %v", name, actual.Spec.Template.Labels, actual.Labels) - } else { - t.Errorf("%s: unexpected equality: %v", name, actual.Labels) + original := test.original + expected := test.expected + obj2 := roundTrip(t, runtime.Object(original)) + actual, ok := obj2.(*batchv1.Job) + if !ok { + t.Fatalf("Unexpected object: %v", actual) } - } + validateDefaultInt32(t, "Completions", actual.Spec.Completions, expected.Spec.Completions) + validateDefaultInt32(t, "Parallelism", actual.Spec.Parallelism, expected.Spec.Parallelism) + validateDefaultInt32(t, "BackoffLimit", actual.Spec.BackoffLimit, expected.Spec.BackoffLimit) + + if test.expectLabels != reflect.DeepEqual(actual.Labels, actual.Spec.Template.Labels) { + if test.expectLabels { + t.Errorf("Expected labels: %v, got: %v", actual.Spec.Template.Labels, actual.Labels) + } else { + t.Errorf("Unexpected equality: %v", actual.Labels) + } + } + if actual.Spec.CompletionMode != expected.Spec.CompletionMode { + t.Errorf("Got CompletionMode: %v, want: %v", actual.Spec.CompletionMode, expected.Spec.CompletionMode) + } + }) } } -func validateDefaultInt32(t *testing.T, name string, field string, actual *int32, expected *int32) { +func validateDefaultInt32(t *testing.T, field string, actual *int32, expected *int32) { if (actual == nil) != (expected == nil) { - t.Errorf("%s: got different *%s than expected: %v %v", name, field, actual, expected) + t.Errorf("Got different *%s than expected: %v %v", field, actual, expected) } if actual != nil && expected != nil { if *actual != *expected { - t.Errorf("%s: got different %s than expected: %d %d", name, field, *actual, *expected) + t.Errorf("Got different %s than expected: %d %d", field, *actual, *expected) } } } diff --git a/pkg/apis/batch/v1/zz_generated.conversion.go b/pkg/apis/batch/v1/zz_generated.conversion.go index 5b66fd3d6354..26eeacebabb8 100644 --- a/pkg/apis/batch/v1/zz_generated.conversion.go +++ b/pkg/apis/batch/v1/zz_generated.conversion.go @@ -208,6 +208,7 @@ func autoConvert_v1_JobSpec_To_batch_JobSpec(in *v1.JobSpec, out *batch.JobSpec, return err } out.TTLSecondsAfterFinished = (*int32)(unsafe.Pointer(in.TTLSecondsAfterFinished)) + out.CompletionMode = batch.CompletionMode(in.CompletionMode) return nil } @@ -222,6 +223,7 @@ func autoConvert_batch_JobSpec_To_v1_JobSpec(in *batch.JobSpec, out *v1.JobSpec, return err } out.TTLSecondsAfterFinished = (*int32)(unsafe.Pointer(in.TTLSecondsAfterFinished)) + out.CompletionMode = v1.CompletionMode(in.CompletionMode) return nil } @@ -232,6 +234,7 @@ func autoConvert_v1_JobStatus_To_batch_JobStatus(in *v1.JobStatus, out *batch.Jo out.Active = in.Active out.Succeeded = in.Succeeded out.Failed = in.Failed + out.CompletedIndexes = in.CompletedIndexes return nil } @@ -247,6 +250,7 @@ func autoConvert_batch_JobStatus_To_v1_JobStatus(in *batch.JobStatus, out *v1.Jo out.Active = in.Active out.Succeeded = in.Succeeded out.Failed = in.Failed + out.CompletedIndexes = in.CompletedIndexes return nil } diff --git a/pkg/apis/batch/validation/validation.go b/pkg/apis/batch/validation/validation.go index 8466b92e0388..b0b534cc52e3 100644 --- a/pkg/apis/batch/validation/validation.go +++ b/pkg/apis/batch/validation/validation.go @@ -31,6 +31,11 @@ import ( apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" ) +// maxParallelismForIndexJob is the maximum parallelism that an Indexed Job +// is allowed to have. This threshold allows to cap the length of +// .status.completedIndexes. +const maxParallelismForIndexedJob = 100000 + // 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. // @@ -124,6 +129,20 @@ func validateJobSpec(spec *batch.JobSpec, fldPath *field.Path, opts apivalidatio if spec.TTLSecondsAfterFinished != nil { allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.TTLSecondsAfterFinished), fldPath.Child("ttlSecondsAfterFinished"))...) } + // CompletionMode might be empty when IndexedJob feature gate is disabled. + if spec.CompletionMode != "" { + if spec.CompletionMode != batch.NonIndexedCompletion && spec.CompletionMode != batch.IndexedCompletion { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("completionMode"), spec.CompletionMode, []string{string(batch.NonIndexedCompletion), string(batch.IndexedCompletion)})) + } + if spec.CompletionMode == batch.IndexedCompletion { + if spec.Completions == nil { + allErrs = append(allErrs, field.Required(fldPath.Child("completions"), fmt.Sprintf("when completion mode is %s", batch.IndexedCompletion))) + } + if spec.Parallelism != nil && *spec.Parallelism > maxParallelismForIndexedJob { + allErrs = append(allErrs, field.Invalid(fldPath.Child("parallelism"), *spec.Parallelism, fmt.Sprintf("must be less than or equal to %d when completion mode is %s", maxParallelismForIndexedJob, batch.IndexedCompletion))) + } + } + } allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"), opts)...) @@ -170,6 +189,7 @@ func ValidateJobSpecUpdate(spec, oldSpec batch.JobSpec, fldPath *field.Path, opt allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Completions, oldSpec.Completions, fldPath.Child("completions"))...) allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Selector, oldSpec.Selector, fldPath.Child("selector"))...) allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Template, oldSpec.Template, fldPath.Child("template"))...) + allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.CompletionMode, oldSpec.CompletionMode, fldPath.Child("completionMode"))...) return allErrs } diff --git a/pkg/apis/batch/validation/validation_test.go b/pkg/apis/batch/validation/validation_test.go index d16b4e0ffd49..4d40ac85a3d5 100644 --- a/pkg/apis/batch/validation/validation_test.go +++ b/pkg/apis/batch/validation/validation_test.go @@ -78,7 +78,7 @@ func TestValidateJob(t *testing.T) { validPodTemplateSpecForGenerated := getValidPodTemplateSpecForGenerated(validGeneratedSelector) successCases := map[string]batch.Job{ - "manual selector": { + "valid manual selector": { ObjectMeta: metav1.ObjectMeta{ Name: "myjob", Namespace: metav1.NamespaceDefault, @@ -90,7 +90,7 @@ func TestValidateJob(t *testing.T) { Template: validPodTemplateSpecForManual, }, }, - "generated selector": { + "valid generated selector": { ObjectMeta: metav1.ObjectMeta{ Name: "myjob", Namespace: metav1.NamespaceDefault, @@ -101,6 +101,32 @@ func TestValidateJob(t *testing.T) { Template: validPodTemplateSpecForGenerated, }, }, + "valid NonIndexed completion mode": { + ObjectMeta: metav1.ObjectMeta{ + Name: "myjob", + Namespace: metav1.NamespaceDefault, + UID: types.UID("1a2b3c"), + }, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + CompletionMode: batch.NonIndexedCompletion, + }, + }, + "valid Indexed completion mode": { + ObjectMeta: metav1.ObjectMeta{ + Name: "myjob", + Namespace: metav1.NamespaceDefault, + UID: types.UID("1a2b3c"), + }, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + CompletionMode: batch.IndexedCompletion, + Completions: pointer.Int32Ptr(2), + Parallelism: pointer.Int32Ptr(100000), + }, + }, } for k, v := range successCases { t.Run(k, func(t *testing.T) { @@ -158,7 +184,7 @@ func TestValidateJob(t *testing.T) { Template: validPodTemplateSpecForGenerated, }, }, - "spec.template.metadata.labels: Invalid value: {\"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`": { ObjectMeta: metav1.ObjectMeta{ Name: "myjob", Namespace: metav1.NamespaceDefault, @@ -179,7 +205,7 @@ func TestValidateJob(t *testing.T) { }, }, }, - "spec.template.metadata.labels: Invalid value: {\"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`": { ObjectMeta: metav1.ObjectMeta{ Name: "myjob", Namespace: metav1.NamespaceDefault, @@ -242,7 +268,7 @@ func TestValidateJob(t *testing.T) { }, }, }, - "spec.ttlSecondsAfterFinished:must be greater than or equal to 0": { + "spec.ttlSecondsAfterFinished: must be greater than or equal to 0": { ObjectMeta: metav1.ObjectMeta{ Name: "myjob", Namespace: metav1.NamespaceDefault, @@ -254,6 +280,32 @@ func TestValidateJob(t *testing.T) { Template: validPodTemplateSpecForGenerated, }, }, + "spec.completions: Required value: when completion mode is Indexed": { + ObjectMeta: metav1.ObjectMeta{ + Name: "myjob", + Namespace: metav1.NamespaceDefault, + UID: types.UID("1a2b3c"), + }, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + CompletionMode: batch.IndexedCompletion, + }, + }, + "spec.parallelism: must be less than or equal to 100000 when completion mode is Indexed": { + ObjectMeta: metav1.ObjectMeta{ + Name: "myjob", + Namespace: metav1.NamespaceDefault, + UID: types.UID("1a2b3c"), + }, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + CompletionMode: batch.IndexedCompletion, + Completions: pointer.Int32Ptr(2), + Parallelism: pointer.Int32Ptr(100001), + }, + }, } for k, v := range errorCases { @@ -262,7 +314,7 @@ func TestValidateJob(t *testing.T) { if len(errs) == 0 { t.Errorf("expected failure for %s", k) } else { - s := strings.Split(k, ":") + s := strings.SplitN(k, ":", 2) err := errs[0] if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { t.Errorf("unexpected error: %v, expected: %s", err, k) @@ -346,6 +398,24 @@ func TestValidateJobUpdate(t *testing.T) { Field: "spec.template", }, }, + "immutable completion mode": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + CompletionMode: batch.IndexedCompletion, + Completions: pointer.Int32Ptr(2), + }, + }, + update: func(job *batch.Job) { + job.Spec.CompletionMode = batch.NonIndexedCompletion + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.completionMode", + }, + }, } ignoreValueAndDetail := cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail") for k, tc := range cases { diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 8fb22fe5c6de..fb3cdfd4cd7c 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -311,6 +311,12 @@ const ( // Allow TTL controller to clean up Pods and Jobs after they finish. TTLAfterFinished featuregate.Feature = "TTLAfterFinished" + // owner: @alculquicondor + // alpha: v1.21 + // + // Allows Job controller to manage Pod completions per completion index. + IndexedJob featuregate.Feature = "IndexedJob" + // owner: @dashpole // alpha: v1.13 // beta: v1.15 @@ -733,6 +739,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS VolumeSnapshotDataSource: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21 ProcMountType: {Default: false, PreRelease: featuregate.Alpha}, TTLAfterFinished: {Default: true, PreRelease: featuregate.Beta}, + IndexedJob: {Default: false, PreRelease: featuregate.Alpha}, KubeletPodResources: {Default: true, PreRelease: featuregate.Beta}, LocalStorageCapacityIsolationFSQuotaMonitoring: {Default: false, PreRelease: featuregate.Alpha}, NonPreemptingPriority: {Default: true, PreRelease: featuregate.Beta}, diff --git a/pkg/registry/batch/job/strategy.go b/pkg/registry/batch/job/strategy.go index 906ca95c4a88..8df719df45bb 100644 --- a/pkg/registry/batch/job/strategy.go +++ b/pkg/registry/batch/job/strategy.go @@ -80,6 +80,10 @@ func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { job.Spec.TTLSecondsAfterFinished = nil } + if !utilfeature.DefaultFeatureGate.Enabled(features.IndexedJob) { + job.Spec.CompletionMode = batch.NonIndexedCompletion + } + pod.DropDisabledTemplateFields(&job.Spec.Template, nil) } @@ -93,6 +97,10 @@ func (jobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object newJob.Spec.TTLSecondsAfterFinished = nil } + if !utilfeature.DefaultFeatureGate.Enabled(features.IndexedJob) && oldJob.Spec.CompletionMode == batch.NonIndexedCompletion { + newJob.Spec.CompletionMode = batch.NonIndexedCompletion + } + pod.DropDisabledTemplateFields(&newJob.Spec.Template, &oldJob.Spec.Template) } diff --git a/pkg/registry/batch/job/strategy_test.go b/pkg/registry/batch/job/strategy_test.go index 4cc78d44f2c9..b76f9d7ff6ac 100644 --- a/pkg/registry/batch/job/strategy_test.go +++ b/pkg/registry/batch/job/strategy_test.go @@ -43,16 +43,21 @@ func newInt32(i int32) *int32 { func TestJobStrategy(t *testing.T) { cases := map[string]struct { - ttlEnabled bool + ttlEnabled bool + indexedJobEnabled bool }{ "features disabled": {}, "ttl enabled": { ttlEnabled: true, }, + "indexed job enabled": { + indexedJobEnabled: true, + }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TTLAfterFinished, tc.ttlEnabled)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IndexedJob, tc.indexedJobEnabled)() testJobStrategy(t) }) } @@ -60,6 +65,7 @@ func TestJobStrategy(t *testing.T) { func testJobStrategy(t *testing.T) { ttlEnabled := utilfeature.DefaultFeatureGate.Enabled(features.TTLAfterFinished) + indexedJobEnabled := utilfeature.DefaultFeatureGate.Enabled(features.IndexedJob) ctx := genericapirequest.NewDefaultContext() if !Strategy.NamespaceScoped() { t.Errorf("Job must be namespace scoped") @@ -87,10 +93,13 @@ func testJobStrategy(t *testing.T) { Namespace: metav1.NamespaceDefault, }, Spec: batch.JobSpec{ - Selector: validSelector, - Template: validPodTemplateSpec, - TTLSecondsAfterFinished: newInt32(0), // Set TTL - ManualSelector: newBool(true), + Selector: validSelector, + Template: validPodTemplateSpec, + ManualSelector: newBool(true), + Completions: newInt32(2), + // Set gated values. + TTLSecondsAfterFinished: newInt32(0), + CompletionMode: batch.IndexedCompletion, }, Status: batch.JobStatus{ Active: 11, @@ -106,15 +115,21 @@ func testJobStrategy(t *testing.T) { t.Errorf("Unexpected error validating %v", errs) } if ttlEnabled != (job.Spec.TTLSecondsAfterFinished != nil) { - t.Errorf("Job should allow setting .spec.ttlSecondsAfterFinished when %v feature is enabled", features.TTLAfterFinished) + t.Errorf("Job should allow setting .spec.ttlSecondsAfterFinished only when %v feature is enabled", features.TTLAfterFinished) + } + if indexedJobEnabled != (job.Spec.CompletionMode != batch.NonIndexedCompletion) { + t.Errorf("Job should allow setting .spec.completionMode=Indexed only when %v feature is enabled", features.IndexedJob) } parallelism := int32(10) updatedJob := &batch.Job{ ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"}, Spec: batch.JobSpec{ - Parallelism: ¶llelism, - TTLSecondsAfterFinished: newInt32(1), // Update TTL + Parallelism: ¶llelism, + Completions: newInt32(2), + // Update gated features. + TTLSecondsAfterFinished: newInt32(1), + CompletionMode: batch.IndexedCompletion, // No change because field is immutable. }, Status: batch.JobStatus{ Active: 11, @@ -135,13 +150,18 @@ func testJobStrategy(t *testing.T) { t.Errorf("Expected a validation error") } - // Existing TTLSecondsAfterFinished should be preserved + // Existing gated fields should be preserved job.Spec.TTLSecondsAfterFinished = newInt32(1) + job.Spec.CompletionMode = batch.IndexedCompletion updatedJob.Spec.TTLSecondsAfterFinished = newInt32(2) + updatedJob.Spec.CompletionMode = batch.IndexedCompletion Strategy.PrepareForUpdate(ctx, updatedJob, job) if job.Spec.TTLSecondsAfterFinished == nil || updatedJob.Spec.TTLSecondsAfterFinished == nil { t.Errorf("existing TTLSecondsAfterFinished should be preserved") } + if job.Spec.CompletionMode == "" || updatedJob.Spec.CompletionMode == "" { + t.Errorf("existing completionMode should be preserved") + } // Make sure we correctly implement the interface. // Otherwise a typo could silently change the default. diff --git a/staging/src/k8s.io/api/batch/v1/generated.pb.go b/staging/src/k8s.io/api/batch/v1/generated.pb.go index dc43514c6ae2..af7f36c4ce72 100644 --- a/staging/src/k8s.io/api/batch/v1/generated.pb.go +++ b/staging/src/k8s.io/api/batch/v1/generated.pb.go @@ -198,66 +198,69 @@ func init() { } var fileDescriptor_3b52da57c93de713 = []byte{ - // 929 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x5d, 0x6f, 0xe3, 0x44, - 0x14, 0xad, 0x9b, 0xa6, 0x4d, 0xa6, 0x1f, 0x5b, 0x06, 0x55, 0x1b, 0x0a, 0xb2, 0x97, 0x20, 0xa1, - 0x82, 0x84, 0x4d, 0x4b, 0x85, 0x10, 0x02, 0xa4, 0x75, 0x51, 0x25, 0xaa, 0x54, 0x5b, 0x26, 0x59, - 0x21, 0x21, 0x90, 0x18, 0xdb, 0x37, 0x89, 0x89, 0xed, 0xb1, 0x3c, 0x93, 0x48, 0x7d, 0xe3, 0x27, - 0xf0, 0x23, 0x10, 0x7f, 0x82, 0x77, 0xd4, 0xc7, 0x7d, 0xdc, 0x27, 0x8b, 0x9a, 0x1f, 0xc0, 0xfb, - 0x3e, 0xa1, 0x19, 0x3b, 0xb6, 0xd3, 0x26, 0xa2, 0xcb, 0x5b, 0xe6, 0xcc, 0x39, 0xe7, 0x5e, 0xcf, - 0x3d, 0xb9, 0xe8, 0x8b, 0xc9, 0x67, 0xdc, 0xf4, 0x99, 0x35, 0x99, 0x3a, 0x90, 0x44, 0x20, 0x80, - 0x5b, 0x33, 0x88, 0x3c, 0x96, 0x58, 0xc5, 0x05, 0x8d, 0x7d, 0xcb, 0xa1, 0xc2, 0x1d, 0x5b, 0xb3, - 0x63, 0x6b, 0x04, 0x11, 0x24, 0x54, 0x80, 0x67, 0xc6, 0x09, 0x13, 0x0c, 0xbf, 0x99, 0x93, 0x4c, - 0x1a, 0xfb, 0xa6, 0x22, 0x99, 0xb3, 0xe3, 0xc3, 0x8f, 0x46, 0xbe, 0x18, 0x4f, 0x1d, 0xd3, 0x65, - 0xa1, 0x35, 0x62, 0x23, 0x66, 0x29, 0xae, 0x33, 0x1d, 0xaa, 0x93, 0x3a, 0xa8, 0x5f, 0xb9, 0xc7, - 0x61, 0xb7, 0x56, 0xc8, 0x65, 0x09, 0x2c, 0xa9, 0x73, 0x78, 0x5a, 0x71, 0x42, 0xea, 0x8e, 0xfd, - 0x08, 0x92, 0x6b, 0x2b, 0x9e, 0x8c, 0x24, 0xc0, 0xad, 0x10, 0x04, 0x5d, 0xa6, 0xb2, 0x56, 0xa9, - 0x92, 0x69, 0x24, 0xfc, 0x10, 0xee, 0x09, 0x3e, 0xfd, 0x2f, 0x01, 0x77, 0xc7, 0x10, 0xd2, 0xbb, - 0xba, 0xee, 0x3f, 0x1a, 0x6a, 0x5c, 0x30, 0x07, 0xff, 0x84, 0x5a, 0xb2, 0x17, 0x8f, 0x0a, 0xda, - 0xd1, 0x9e, 0x68, 0x47, 0xdb, 0x27, 0x1f, 0x9b, 0xd5, 0x0b, 0x95, 0x96, 0x66, 0x3c, 0x19, 0x49, - 0x80, 0x9b, 0x92, 0x6d, 0xce, 0x8e, 0xcd, 0x67, 0xce, 0xcf, 0xe0, 0x8a, 0x4b, 0x10, 0xd4, 0xc6, - 0x37, 0xa9, 0xb1, 0x96, 0xa5, 0x06, 0xaa, 0x30, 0x52, 0xba, 0xe2, 0xaf, 0xd0, 0x06, 0x8f, 0xc1, - 0xed, 0xac, 0x2b, 0xf7, 0x77, 0xcc, 0x25, 0xef, 0x6f, 0x5e, 0x30, 0xa7, 0x1f, 0x83, 0x6b, 0xef, - 0x14, 0x4e, 0x1b, 0xf2, 0x44, 0x94, 0x0e, 0x9f, 0xa3, 0x4d, 0x2e, 0xa8, 0x98, 0xf2, 0x4e, 0x43, - 0x39, 0xe8, 0x2b, 0x1d, 0x14, 0xcb, 0xde, 0x2b, 0x3c, 0x36, 0xf3, 0x33, 0x29, 0xd4, 0xdd, 0x3f, - 0x1b, 0x68, 0xe7, 0x82, 0x39, 0x67, 0x2c, 0xf2, 0x7c, 0xe1, 0xb3, 0x08, 0x9f, 0xa2, 0x0d, 0x71, - 0x1d, 0x83, 0xfa, 0xec, 0xb6, 0xfd, 0x64, 0x5e, 0x7a, 0x70, 0x1d, 0xc3, 0xab, 0xd4, 0xd8, 0xaf, - 0x73, 0x25, 0x46, 0x14, 0x1b, 0xf7, 0xca, 0x76, 0xd6, 0x95, 0xee, 0x74, 0xb1, 0xdc, 0xab, 0xd4, - 0x58, 0x92, 0x0e, 0xb3, 0x74, 0x5a, 0x6c, 0x0a, 0x8f, 0xd0, 0x6e, 0x40, 0xb9, 0xb8, 0x4a, 0x98, - 0x03, 0x03, 0x3f, 0x84, 0xe2, 0x1b, 0x3f, 0x7c, 0xd8, 0x0c, 0xa4, 0xc2, 0x3e, 0x28, 0x1a, 0xd8, - 0xed, 0xd5, 0x8d, 0xc8, 0xa2, 0x2f, 0x9e, 0x21, 0x2c, 0x81, 0x41, 0x42, 0x23, 0x9e, 0x7f, 0x92, - 0xac, 0xb6, 0xf1, 0xda, 0xd5, 0x0e, 0x8b, 0x6a, 0xb8, 0x77, 0xcf, 0x8d, 0x2c, 0xa9, 0x80, 0xdf, - 0x47, 0x9b, 0x09, 0x50, 0xce, 0xa2, 0x4e, 0x53, 0x3d, 0x57, 0x39, 0x1d, 0xa2, 0x50, 0x52, 0xdc, - 0xe2, 0x0f, 0xd0, 0x56, 0x08, 0x9c, 0xd3, 0x11, 0x74, 0x36, 0x15, 0xf1, 0x51, 0x41, 0xdc, 0xba, - 0xcc, 0x61, 0x32, 0xbf, 0xef, 0xfe, 0xae, 0xa1, 0xad, 0x0b, 0xe6, 0xf4, 0x7c, 0x2e, 0xf0, 0x0f, - 0xf7, 0xe2, 0x6b, 0x3e, 0xec, 0x63, 0xa4, 0x5a, 0x85, 0x77, 0xbf, 0xa8, 0xd3, 0x9a, 0x23, 0xb5, - 0xe8, 0x7e, 0x89, 0x9a, 0xbe, 0x80, 0x50, 0x8e, 0xba, 0x71, 0xb4, 0x7d, 0xd2, 0x59, 0x95, 0x3c, - 0x7b, 0xb7, 0x30, 0x69, 0x7e, 0x23, 0xe9, 0x24, 0x57, 0x75, 0xff, 0xd8, 0x50, 0x8d, 0xca, 0x2c, - 0xe3, 0x63, 0xb4, 0x1d, 0xd3, 0x84, 0x06, 0x01, 0x04, 0x3e, 0x0f, 0x55, 0xaf, 0x4d, 0xfb, 0x51, - 0x96, 0x1a, 0xdb, 0x57, 0x15, 0x4c, 0xea, 0x1c, 0x29, 0x71, 0x59, 0x18, 0x07, 0x20, 0x1f, 0x33, - 0x8f, 0x5b, 0x21, 0x39, 0xab, 0x60, 0x52, 0xe7, 0xe0, 0x67, 0xe8, 0x80, 0xba, 0xc2, 0x9f, 0xc1, - 0xd7, 0x40, 0xbd, 0xc0, 0x8f, 0xa0, 0x0f, 0x2e, 0x8b, 0xbc, 0xfc, 0xaf, 0xd3, 0xb0, 0xdf, 0xca, - 0x52, 0xe3, 0xe0, 0xe9, 0x32, 0x02, 0x59, 0xae, 0xc3, 0xa7, 0x68, 0xc7, 0xa1, 0xee, 0x84, 0x0d, - 0x87, 0x3d, 0x3f, 0xf4, 0x45, 0x67, 0x4b, 0x35, 0xb1, 0x9f, 0xa5, 0xc6, 0x8e, 0x5d, 0xc3, 0xc9, - 0x02, 0x0b, 0xff, 0x88, 0x5a, 0x1c, 0x02, 0x70, 0x05, 0x4b, 0x8a, 0x88, 0x7d, 0xf2, 0xc0, 0xa9, - 0x50, 0x07, 0x82, 0x7e, 0x21, 0xb5, 0x77, 0xe4, 0x58, 0xe6, 0x27, 0x52, 0x5a, 0xe2, 0xcf, 0xd1, - 0x5e, 0x48, 0xa3, 0x29, 0x2d, 0x99, 0x2a, 0x5b, 0x2d, 0x1b, 0x67, 0xa9, 0xb1, 0x77, 0xb9, 0x70, - 0x43, 0xee, 0x30, 0xf1, 0xb7, 0xa8, 0x25, 0x20, 0x8c, 0x03, 0x2a, 0xf2, 0xa0, 0x6d, 0x9f, 0xbc, - 0x57, 0x9f, 0xaa, 0xfc, 0xbf, 0xca, 0x46, 0xae, 0x98, 0x37, 0x28, 0x68, 0x6a, 0x31, 0x95, 0x29, - 0x99, 0xa3, 0xa4, 0xb4, 0xc1, 0xcf, 0xd1, 0x63, 0x21, 0x82, 0xe2, 0xc5, 0x9e, 0x0e, 0x05, 0x24, - 0xe7, 0x7e, 0xe4, 0xf3, 0x31, 0x78, 0x9d, 0x96, 0x7a, 0xae, 0xb7, 0xb3, 0xd4, 0x78, 0x3c, 0x18, - 0xf4, 0x96, 0x51, 0xc8, 0x2a, 0x6d, 0xf7, 0xb7, 0x06, 0x6a, 0x97, 0x5b, 0x0d, 0x3f, 0x47, 0xc8, - 0x9d, 0xef, 0x10, 0xde, 0xd1, 0x54, 0x1e, 0xdf, 0x5d, 0x95, 0xc7, 0x72, 0xdb, 0x54, 0xab, 0xb9, - 0x84, 0x38, 0xa9, 0x19, 0xe1, 0xef, 0x50, 0x9b, 0x0b, 0x9a, 0x08, 0xb5, 0x0d, 0xd6, 0x5f, 0x7b, - 0x1b, 0xec, 0x66, 0xa9, 0xd1, 0xee, 0xcf, 0x0d, 0x48, 0xe5, 0x85, 0x87, 0x68, 0xaf, 0x0a, 0xe6, - 0xff, 0xdc, 0x6c, 0x6a, 0x9e, 0x67, 0x0b, 0x2e, 0xe4, 0x8e, 0xab, 0xdc, 0x2f, 0x79, 0x72, 0x55, - 0xd0, 0x9a, 0xd5, 0x7e, 0xc9, 0x63, 0x4e, 0x8a, 0x5b, 0x6c, 0xa1, 0x36, 0x9f, 0xba, 0x2e, 0x80, - 0x07, 0x9e, 0x8a, 0x4b, 0xd3, 0x7e, 0xa3, 0xa0, 0xb6, 0xfb, 0xf3, 0x0b, 0x52, 0x71, 0xa4, 0xf1, - 0x90, 0xfa, 0x01, 0x78, 0x2a, 0x26, 0x35, 0xe3, 0x73, 0x85, 0x92, 0xe2, 0xd6, 0x3e, 0xba, 0xb9, - 0xd5, 0xd7, 0x5e, 0xdc, 0xea, 0x6b, 0x2f, 0x6f, 0xf5, 0xb5, 0x5f, 0x32, 0x5d, 0xbb, 0xc9, 0x74, - 0xed, 0x45, 0xa6, 0x6b, 0x2f, 0x33, 0x5d, 0xfb, 0x2b, 0xd3, 0xb5, 0x5f, 0xff, 0xd6, 0xd7, 0xbe, - 0x5f, 0x9f, 0x1d, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xc8, 0x73, 0xe7, 0x7a, 0xb8, 0x08, 0x00, - 0x00, + // 979 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x5f, 0x6f, 0xe3, 0xc4, + 0x17, 0x6d, 0x9a, 0xa6, 0x4d, 0xa6, 0x7f, 0xb6, 0xbf, 0xf9, 0xa9, 0x5a, 0x53, 0x50, 0xbc, 0x04, + 0x09, 0x15, 0x24, 0x6c, 0x5a, 0x2a, 0x84, 0x10, 0x20, 0xad, 0xbb, 0xaa, 0xb4, 0x55, 0xaa, 0x2d, + 0xd3, 0xac, 0x90, 0x10, 0x48, 0x8c, 0xed, 0x9b, 0xd4, 0xd4, 0xf6, 0x58, 0x9e, 0x49, 0x44, 0xdf, + 0xf8, 0x02, 0x48, 0x7c, 0x0a, 0x3e, 0x0a, 0xea, 0xe3, 0x3e, 0xee, 0x93, 0x45, 0xcd, 0x1b, 0x2f, + 0xbc, 0xf7, 0x09, 0xcd, 0x78, 0x62, 0x3b, 0x6d, 0x22, 0xba, 0xbc, 0x79, 0xee, 0x3d, 0xe7, 0xdc, + 0xeb, 0x3b, 0x67, 0x2e, 0xfa, 0xe2, 0xf2, 0x33, 0x6e, 0x05, 0xcc, 0xbe, 0x1c, 0xbb, 0x90, 0xc6, + 0x20, 0x80, 0xdb, 0x13, 0x88, 0x7d, 0x96, 0xda, 0x3a, 0x41, 0x93, 0xc0, 0x76, 0xa9, 0xf0, 0x2e, + 0xec, 0xc9, 0xbe, 0x3d, 0x82, 0x18, 0x52, 0x2a, 0xc0, 0xb7, 0x92, 0x94, 0x09, 0x86, 0xff, 0x5f, + 0x80, 0x2c, 0x9a, 0x04, 0x96, 0x02, 0x59, 0x93, 0xfd, 0xdd, 0x8f, 0x46, 0x81, 0xb8, 0x18, 0xbb, + 0x96, 0xc7, 0x22, 0x7b, 0xc4, 0x46, 0xcc, 0x56, 0x58, 0x77, 0x3c, 0x54, 0x27, 0x75, 0x50, 0x5f, + 0x85, 0xc6, 0x6e, 0xaf, 0x56, 0xc8, 0x63, 0x29, 0xcc, 0xa9, 0xb3, 0x7b, 0x58, 0x61, 0x22, 0xea, + 0x5d, 0x04, 0x31, 0xa4, 0x57, 0x76, 0x72, 0x39, 0x92, 0x01, 0x6e, 0x47, 0x20, 0xe8, 0x3c, 0x96, + 0xbd, 0x88, 0x95, 0x8e, 0x63, 0x11, 0x44, 0x70, 0x8f, 0xf0, 0xe9, 0xbf, 0x11, 0xb8, 0x77, 0x01, + 0x11, 0xbd, 0xcb, 0xeb, 0xfd, 0xdd, 0x40, 0xcd, 0x13, 0xe6, 0xe2, 0x1f, 0x50, 0x5b, 0xf6, 0xe2, + 0x53, 0x41, 0x8d, 0xc6, 0x93, 0xc6, 0xde, 0xfa, 0xc1, 0xc7, 0x56, 0x35, 0xa1, 0x52, 0xd2, 0x4a, + 0x2e, 0x47, 0x32, 0xc0, 0x2d, 0x89, 0xb6, 0x26, 0xfb, 0xd6, 0x0b, 0xf7, 0x47, 0xf0, 0xc4, 0x29, + 0x08, 0xea, 0xe0, 0xeb, 0xcc, 0x5c, 0xca, 0x33, 0x13, 0x55, 0x31, 0x52, 0xaa, 0xe2, 0xaf, 0xd0, + 0x0a, 0x4f, 0xc0, 0x33, 0x96, 0x95, 0xfa, 0x3b, 0xd6, 0x9c, 0xf9, 0x5b, 0x27, 0xcc, 0x3d, 0x4f, + 0xc0, 0x73, 0x36, 0xb4, 0xd2, 0x8a, 0x3c, 0x11, 0xc5, 0xc3, 0xc7, 0x68, 0x95, 0x0b, 0x2a, 0xc6, + 0xdc, 0x68, 0x2a, 0x85, 0xee, 0x42, 0x05, 0x85, 0x72, 0xb6, 0xb4, 0xc6, 0x6a, 0x71, 0x26, 0x9a, + 0xdd, 0xfb, 0xbd, 0x89, 0x36, 0x4e, 0x98, 0x7b, 0xc4, 0x62, 0x3f, 0x10, 0x01, 0x8b, 0xf1, 0x21, + 0x5a, 0x11, 0x57, 0x09, 0xa8, 0xdf, 0xee, 0x38, 0x4f, 0xa6, 0xa5, 0x07, 0x57, 0x09, 0xdc, 0x66, + 0xe6, 0x76, 0x1d, 0x2b, 0x63, 0x44, 0xa1, 0x71, 0xbf, 0x6c, 0x67, 0x59, 0xf1, 0x0e, 0x67, 0xcb, + 0xdd, 0x66, 0xe6, 0x1c, 0x77, 0x58, 0xa5, 0xd2, 0x6c, 0x53, 0x78, 0x84, 0x36, 0x43, 0xca, 0xc5, + 0x59, 0xca, 0x5c, 0x18, 0x04, 0x11, 0xe8, 0x7f, 0xfc, 0xf0, 0x61, 0x77, 0x20, 0x19, 0xce, 0x8e, + 0x6e, 0x60, 0xb3, 0x5f, 0x17, 0x22, 0xb3, 0xba, 0x78, 0x82, 0xb0, 0x0c, 0x0c, 0x52, 0x1a, 0xf3, + 0xe2, 0x97, 0x64, 0xb5, 0x95, 0x37, 0xae, 0xb6, 0xab, 0xab, 0xe1, 0xfe, 0x3d, 0x35, 0x32, 0xa7, + 0x02, 0x7e, 0x1f, 0xad, 0xa6, 0x40, 0x39, 0x8b, 0x8d, 0x96, 0x1a, 0x57, 0x79, 0x3b, 0x44, 0x45, + 0x89, 0xce, 0xe2, 0x0f, 0xd0, 0x5a, 0x04, 0x9c, 0xd3, 0x11, 0x18, 0xab, 0x0a, 0xf8, 0x48, 0x03, + 0xd7, 0x4e, 0x8b, 0x30, 0x99, 0xe6, 0x7b, 0xbf, 0x35, 0xd0, 0xda, 0x09, 0x73, 0xfb, 0x01, 0x17, + 0xf8, 0xbb, 0x7b, 0xf6, 0xb5, 0x1e, 0xf6, 0x33, 0x92, 0xad, 0xcc, 0xbb, 0xad, 0xeb, 0xb4, 0xa7, + 0x91, 0x9a, 0x75, 0xbf, 0x44, 0xad, 0x40, 0x40, 0x24, 0xaf, 0xba, 0xb9, 0xb7, 0x7e, 0x60, 0x2c, + 0x72, 0x9e, 0xb3, 0xa9, 0x45, 0x5a, 0xcf, 0x25, 0x9c, 0x14, 0xac, 0xde, 0x2f, 0x2d, 0xd5, 0xa8, + 0xf4, 0x32, 0xde, 0x47, 0xeb, 0x09, 0x4d, 0x69, 0x18, 0x42, 0x18, 0xf0, 0x48, 0xf5, 0xda, 0x72, + 0x1e, 0xe5, 0x99, 0xb9, 0x7e, 0x56, 0x85, 0x49, 0x1d, 0x23, 0x29, 0x1e, 0x8b, 0x92, 0x10, 0xe4, + 0x30, 0x0b, 0xbb, 0x69, 0xca, 0x51, 0x15, 0x26, 0x75, 0x0c, 0x7e, 0x81, 0x76, 0xa8, 0x27, 0x82, + 0x09, 0x3c, 0x03, 0xea, 0x87, 0x41, 0x0c, 0xe7, 0xe0, 0xb1, 0xd8, 0x2f, 0x9e, 0x4e, 0xd3, 0x79, + 0x2b, 0xcf, 0xcc, 0x9d, 0xa7, 0xf3, 0x00, 0x64, 0x3e, 0x0f, 0x1f, 0xa2, 0x0d, 0x97, 0x7a, 0x97, + 0x6c, 0x38, 0xec, 0x07, 0x51, 0x20, 0x8c, 0x35, 0xd5, 0xc4, 0x76, 0x9e, 0x99, 0x1b, 0x4e, 0x2d, + 0x4e, 0x66, 0x50, 0xf8, 0x7b, 0xd4, 0xe6, 0x10, 0x82, 0x27, 0x58, 0xaa, 0x2d, 0xf6, 0xc9, 0x03, + 0x6f, 0x85, 0xba, 0x10, 0x9e, 0x6b, 0xaa, 0xb3, 0x21, 0xaf, 0x65, 0x7a, 0x22, 0xa5, 0x24, 0xfe, + 0x1c, 0x6d, 0x45, 0x34, 0x1e, 0xd3, 0x12, 0xa9, 0xbc, 0xd5, 0x76, 0x70, 0x9e, 0x99, 0x5b, 0xa7, + 0x33, 0x19, 0x72, 0x07, 0x89, 0xbf, 0x46, 0x6d, 0x01, 0x51, 0x12, 0x52, 0x51, 0x18, 0x6d, 0xfd, + 0xe0, 0xbd, 0xfa, 0xad, 0xca, 0xf7, 0x2a, 0x1b, 0x39, 0x63, 0xfe, 0x40, 0xc3, 0xd4, 0x62, 0x2a, + 0x5d, 0x32, 0x8d, 0x92, 0x52, 0x06, 0xbf, 0x44, 0x8f, 0x85, 0x08, 0xf5, 0xc4, 0x9e, 0x0e, 0x05, + 0xa4, 0xc7, 0x41, 0x1c, 0xf0, 0x0b, 0xf0, 0x8d, 0xb6, 0x1a, 0xd7, 0xdb, 0x79, 0x66, 0x3e, 0x1e, + 0x0c, 0xfa, 0xf3, 0x20, 0x64, 0x11, 0x17, 0x9f, 0xa1, 0xad, 0xea, 0x6a, 0x4f, 0x99, 0x0f, 0x46, + 0x47, 0x3d, 0x8c, 0x3d, 0xdd, 0xca, 0xd6, 0xd1, 0x4c, 0xf6, 0xf6, 0x5e, 0x84, 0xdc, 0xe1, 0xf7, + 0xfe, 0x6a, 0xa2, 0x4e, 0xb9, 0x27, 0xf1, 0x4b, 0x84, 0xbc, 0xe9, 0x56, 0xe2, 0x46, 0x43, 0x39, + 0xfc, 0xdd, 0x45, 0x0e, 0x2f, 0xf7, 0x57, 0xb5, 0xec, 0xcb, 0x10, 0x27, 0x35, 0x21, 0xfc, 0x0d, + 0xea, 0x70, 0x41, 0x53, 0xa1, 0xf6, 0xcb, 0xf2, 0x1b, 0xef, 0x97, 0xcd, 0x3c, 0x33, 0x3b, 0xe7, + 0x53, 0x01, 0x52, 0x69, 0xe1, 0x61, 0x7d, 0x1e, 0xff, 0x71, 0x57, 0xe2, 0xd9, 0xb9, 0xa9, 0x12, + 0x77, 0x54, 0xe5, 0xc6, 0x2a, 0xde, 0x82, 0xb2, 0x6e, 0xab, 0xda, 0x58, 0xc5, 0xc3, 0x21, 0x3a, + 0x8b, 0x6d, 0xd4, 0xe1, 0x63, 0xcf, 0x03, 0xf0, 0xc1, 0x57, 0x06, 0x6c, 0x39, 0xff, 0xd3, 0xd0, + 0xce, 0xf9, 0x34, 0x41, 0x2a, 0x8c, 0x14, 0x1e, 0xd2, 0x20, 0x04, 0x5f, 0x19, 0xaf, 0x26, 0x7c, + 0xac, 0xa2, 0x44, 0x67, 0xf1, 0x33, 0xb4, 0xad, 0x5b, 0x02, 0xff, 0x79, 0xec, 0xc3, 0x4f, 0xc0, + 0xd5, 0xbb, 0xeb, 0x38, 0x86, 0x66, 0x6c, 0x1f, 0xdd, 0xc9, 0x93, 0x7b, 0x0c, 0x67, 0xef, 0xfa, + 0xa6, 0xbb, 0xf4, 0xea, 0xa6, 0xbb, 0xf4, 0xfa, 0xa6, 0xbb, 0xf4, 0x73, 0xde, 0x6d, 0x5c, 0xe7, + 0xdd, 0xc6, 0xab, 0xbc, 0xdb, 0x78, 0x9d, 0x77, 0x1b, 0x7f, 0xe4, 0xdd, 0xc6, 0xaf, 0x7f, 0x76, + 0x97, 0xbe, 0x5d, 0x9e, 0xec, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x79, 0x3b, 0xba, 0x50, + 0x09, 0x00, 0x00, } func (m *Job) Marshal() (dAtA []byte, err error) { @@ -443,6 +446,11 @@ func (m *JobSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.CompletionMode) + copy(dAtA[i:], m.CompletionMode) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.CompletionMode))) + i-- + dAtA[i] = 0x4a if m.TTLSecondsAfterFinished != nil { i = encodeVarintGenerated(dAtA, i, uint64(*m.TTLSecondsAfterFinished)) i-- @@ -523,6 +531,11 @@ func (m *JobStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.CompletedIndexes) + copy(dAtA[i:], m.CompletedIndexes) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.CompletedIndexes))) + i-- + dAtA[i] = 0x3a i = encodeVarintGenerated(dAtA, i, uint64(m.Failed)) i-- dAtA[i] = 0x30 @@ -667,6 +680,8 @@ func (m *JobSpec) Size() (n int) { if m.TTLSecondsAfterFinished != nil { n += 1 + sovGenerated(uint64(*m.TTLSecondsAfterFinished)) } + l = len(m.CompletionMode) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -693,6 +708,8 @@ func (m *JobStatus) Size() (n int) { n += 1 + sovGenerated(uint64(m.Active)) n += 1 + sovGenerated(uint64(m.Succeeded)) n += 1 + sovGenerated(uint64(m.Failed)) + l = len(m.CompletedIndexes) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -758,6 +775,7 @@ func (this *JobSpec) String() string { `Template:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Template), "PodTemplateSpec", "v11.PodTemplateSpec", 1), `&`, ``, 1) + `,`, `BackoffLimit:` + valueToStringGenerated(this.BackoffLimit) + `,`, `TTLSecondsAfterFinished:` + valueToStringGenerated(this.TTLSecondsAfterFinished) + `,`, + `CompletionMode:` + fmt.Sprintf("%v", this.CompletionMode) + `,`, `}`, }, "") return s @@ -778,6 +796,7 @@ func (this *JobStatus) String() string { `Active:` + fmt.Sprintf("%v", this.Active) + `,`, `Succeeded:` + fmt.Sprintf("%v", this.Succeeded) + `,`, `Failed:` + fmt.Sprintf("%v", this.Failed) + `,`, + `CompletedIndexes:` + fmt.Sprintf("%v", this.CompletedIndexes) + `,`, `}`, }, "") return s @@ -1519,6 +1538,38 @@ func (m *JobSpec) Unmarshal(dAtA []byte) error { } } m.TTLSecondsAfterFinished = &v + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletionMode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CompletionMode = CompletionMode(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -1732,6 +1783,38 @@ func (m *JobStatus) Unmarshal(dAtA []byte) error { break } } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletedIndexes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CompletedIndexes = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/batch/v1/generated.proto b/staging/src/k8s.io/api/batch/v1/generated.proto index 7548c04dc190..74d437640dac 100644 --- a/staging/src/k8s.io/api/batch/v1/generated.proto +++ b/staging/src/k8s.io/api/batch/v1/generated.proto @@ -146,6 +146,28 @@ message JobSpec { // TTLAfterFinished feature. // +optional optional int32 ttlSecondsAfterFinished = 8; + + // CompletionMode specifies how Pod completions are tracked. It can be + // `NonIndexed` (default) or `Indexed`. + // + // `NonIndexed` means that the Job is considered complete when there have + // been .spec.completions successfully completed Pods. Each Pod completion is + // homologous to each other. + // + // `Indexed` means that the Pods of a + // Job get an associated completion index from 0 to (.spec.completions - 1), + // available in the annotation batch.alpha.kubernetes.io/job-completion-index. + // The Job is considered complete when there is one successfully completed Pod + // for each index. + // When value is `Indexed`, .spec.completions must be specified and + // `.spec.parallelism` must be less than or equal to 10^5. + // + // This field is alpha-level and is only honored by servers that enable the + // IndexedJob feature gate. More completion modes can be added in the future. + // If the Job controller observes a mode that it doesn't recognize, the + // controller skips updates for the Job. + // +optional + optional string completionMode = 9; } // JobStatus represents the current state of a Job. @@ -182,5 +204,15 @@ message JobStatus { // The number of pods which reached phase Failed. // +optional optional int32 failed = 6; + + // CompletedIndexes holds the completed indexes when .spec.completionMode = + // "Indexed" in a text format. The indexes are represented as decimal integers + // separated by commas. The numbers are listed in increasing order. Three or + // more consecutive numbers are compressed and represented by the first and + // last element of the series, separated by a hyphen. + // For example, if the completed indexes are 1, 3, 4, 5 and 7, they are + // represented as "1,3-5,7". + // +optional + optional string completedIndexes = 7; } diff --git a/staging/src/k8s.io/api/batch/v1/types.go b/staging/src/k8s.io/api/batch/v1/types.go index fd478874a13d..c148d77b6e3f 100644 --- a/staging/src/k8s.io/api/batch/v1/types.go +++ b/staging/src/k8s.io/api/batch/v1/types.go @@ -21,6 +21,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const JobCompletionIndexAnnotationAlpha = "batch.alpha.kubernetes.io/job-completion-index" + // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -57,6 +59,22 @@ type JobList struct { Items []Job `json:"items" protobuf:"bytes,2,rep,name=items"` } +// CompletionMode specifies how Pod completions of a Job are tracked. +type CompletionMode string + +const ( + // NonIndexedCompletion is a Job completion mode. In this mode, the Job is + // considered complete when there have been .spec.completions + // successfully completed Pods. Pod completions are homologous to each other. + NonIndexedCompletion CompletionMode = "NonIndexed" + + // IndexedCompletion is a Job completion mode. In this mode, the Pods of a + // Job get an associated completion index from 0 to (.spec.completions - 1). + // The Job is considered complete when a Pod completes for each completion + // index. + IndexedCompletion CompletionMode = "Indexed" +) + // JobSpec describes how the job execution will look like. type JobSpec struct { @@ -126,6 +144,28 @@ type JobSpec struct { // TTLAfterFinished feature. // +optional TTLSecondsAfterFinished *int32 `json:"ttlSecondsAfterFinished,omitempty" protobuf:"varint,8,opt,name=ttlSecondsAfterFinished"` + + // CompletionMode specifies how Pod completions are tracked. It can be + // `NonIndexed` (default) or `Indexed`. + // + // `NonIndexed` means that the Job is considered complete when there have + // been .spec.completions successfully completed Pods. Each Pod completion is + // homologous to each other. + // + // `Indexed` means that the Pods of a + // Job get an associated completion index from 0 to (.spec.completions - 1), + // available in the annotation batch.alpha.kubernetes.io/job-completion-index. + // The Job is considered complete when there is one successfully completed Pod + // for each index. + // When value is `Indexed`, .spec.completions must be specified and + // `.spec.parallelism` must be less than or equal to 10^5. + // + // This field is alpha-level and is only honored by servers that enable the + // IndexedJob feature gate. More completion modes can be added in the future. + // If the Job controller observes a mode that it doesn't recognize, the + // controller skips updates for the Job. + // +optional + CompletionMode CompletionMode `json:"completionMode,omitempty" protobuf:"bytes,9,opt,name=completionMode,casttype=CompletionMode"` } // JobStatus represents the current state of a Job. @@ -162,6 +202,16 @@ type JobStatus struct { // The number of pods which reached phase Failed. // +optional Failed int32 `json:"failed,omitempty" protobuf:"varint,6,opt,name=failed"` + + // CompletedIndexes holds the completed indexes when .spec.completionMode = + // "Indexed" in a text format. The indexes are represented as decimal integers + // separated by commas. The numbers are listed in increasing order. Three or + // more consecutive numbers are compressed and represented by the first and + // last element of the series, separated by a hyphen. + // For example, if the completed indexes are 1, 3, 4, 5 and 7, they are + // represented as "1,3-5,7". + // +optional + CompletedIndexes string `json:"completedIndexes,omitempty" protobuf:"bytes,7,opt,name=completedIndexes"` } type JobConditionType string diff --git a/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go index 0d8003a727fb..8a7eb17ab1b9 100644 --- a/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go @@ -72,6 +72,7 @@ var map_JobSpec = map[string]string{ "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: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector", "template": "Describes the pod that will be created when executing a job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", "ttlSecondsAfterFinished": "ttlSecondsAfterFinished limits the lifetime of a Job that has finished execution (either Complete or Failed). If this field is set, ttlSecondsAfterFinished after the Job finishes, it is eligible to be automatically deleted. When the Job is being deleted, its lifecycle guarantees (e.g. finalizers) will be honored. If this field is unset, the Job won't be automatically deleted. If this field is set to zero, the Job becomes eligible to be deleted immediately after it finishes. This field is alpha-level and is only honored by servers that enable the TTLAfterFinished feature.", + "completionMode": "CompletionMode specifies how Pod completions are tracked. It can be `NonIndexed` (default) or `Indexed`.\n\n`NonIndexed` means that the Job is considered complete when there have been .spec.completions successfully completed Pods. Each Pod completion is homologous to each other.\n\n`Indexed` means that the Pods of a Job get an associated completion index from 0 to (.spec.completions - 1), available in the annotation batch.alpha.kubernetes.io/job-completion-index. The Job is considered complete when there is one successfully completed Pod for each index. When value is `Indexed`, .spec.completions must be specified and `.spec.parallelism` must be less than or equal to 10^5.\n\nThis field is alpha-level and is only honored by servers that enable the IndexedJob feature gate. More completion modes can be added in the future. If the Job controller observes a mode that it doesn't recognize, the controller skips updates for the Job.", } func (JobSpec) SwaggerDoc() map[string]string { @@ -79,13 +80,14 @@ func (JobSpec) SwaggerDoc() map[string]string { } var map_JobStatus = map[string]string{ - "": "JobStatus represents the current state of a Job.", - "conditions": "The latest available observations of an object's current state. When a job fails, one of the conditions will have type == \"Failed\". More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", - "startTime": "Represents time when the job was acknowledged by the job controller. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", - "completionTime": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully.", - "active": "The number of actively running pods.", - "succeeded": "The number of pods which reached phase Succeeded.", - "failed": "The number of pods which reached phase Failed.", + "": "JobStatus represents the current state of a Job.", + "conditions": "The latest available observations of an object's current state. When a job fails, one of the conditions will have type == \"Failed\". More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "startTime": "Represents time when the job was acknowledged by the job controller. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + "completionTime": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully.", + "active": "The number of actively running pods.", + "succeeded": "The number of pods which reached phase Succeeded.", + "failed": "The number of pods which reached phase Failed.", + "completedIndexes": "CompletedIndexes holds the completed indexes when .spec.completionMode = \"Indexed\" in a text format. The indexes are represented as decimal integers separated by commas. The numbers are listed in increasing order. Three or more consecutive numbers are compressed and represented by the first and last element of the series, separated by a hyphen. For example, if the completed indexes are 1, 3, 4, 5 and 7, they are represented as \"1,3-5,7\".", } func (JobStatus) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json index 869198a9b9c4..662f1df7654a 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json @@ -1474,21 +1474,23 @@ "setHostnameAsFQDN": false } }, - "ttlSecondsAfterFinished": 1192652907 + "ttlSecondsAfterFinished": 1192652907, + "completionMode": "莾簏ì淵歔ųd," }, "status": { "conditions": [ { - "type": "", - "status": "簏ì淵歔ųd,4", - "lastProbeTime": "2813-03-11T20:08:42Z", - "lastTransitionTime": "2793-11-20T00:30:11Z", + "type": ";蛡媈U", + "status": "Oa2ƒƈɈ达iʍjʒu+,妧縖%Á", + "lastProbeTime": "2823-10-04T11:14:04Z", + "lastTransitionTime": "2882-02-07T11:38:45Z", "reason": "464", "message": "465" } ], - "active": -1983720493, - "succeeded": -2026748262, - "failed": 1049326966 + "active": -1993578228, + "succeeded": 1971731732, + "failed": 165851549, + "completedIndexes": "466" } } \ No newline at end of file diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb index eb3570f162b16e5c2b87ddc68d45caebcbfde21e..26eb05c55cc9ec9456ac3cb1349977f67d08222f 100644 GIT binary patch delta 147 zcmV;E0Brw_G`2L576O_ykr=H4gfg+m@ECte66lV;=dh2%tme11=B<>)vt%q9WeP|N z2s`MTq2;QGRT3UgVKT;&#)!#?=)S&b%8hEul65OA<))|SxRxcuff@)1{P*{+WDo!% z2nfZX_MB@F04f7CHZ(E=G&VILjMA~m`2YX^{{bkJ`k1T-FrDLyPdWoMHZ~dnA^?=6 BL>mAA delta 106 zcmdmH(rYq7jOnBCM2WRb9}G7h{~!@26~JZ0#ULc`d_(`?HP5zheYSSW(akA3CQ=+6 zr*8i4PZnTM;^3IPVaMA90R}B*6EhPdAYrO-d3pD_AOC@XQDau`$(<0U!M9gyzc5QN HC@}y4mC-PC diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml index 8ddb75e7efa6..6a22b9de09ed 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml @@ -32,6 +32,7 @@ metadata: spec: activeDeadlineSeconds: -5584804243908071872 backoffLimit: -783752440 + completionMode: 莾簏ì淵歔ųd, completions: 1305381319 manualSelector: true parallelism: 896585016 @@ -1009,13 +1010,14 @@ spec: volumePath: "101" ttlSecondsAfterFinished: 1192652907 status: - active: -1983720493 + active: -1993578228 + completedIndexes: "466" conditions: - - lastProbeTime: "2813-03-11T20:08:42Z" - lastTransitionTime: "2793-11-20T00:30:11Z" + - lastProbeTime: "2823-10-04T11:14:04Z" + lastTransitionTime: "2882-02-07T11:38:45Z" message: "465" reason: "464" - status: 簏ì淵歔ųd,4 - type: "" - failed: 1049326966 - succeeded: -2026748262 + status: Oa2ƒƈɈ达iʍjʒu+,妧縖%Á + type: ;蛡媈U + failed: 165851549 + succeeded: 1971731732 diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json index c9df7e54c1ef..82b06266a42c 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json @@ -1522,11 +1522,12 @@ "setHostnameAsFQDN": false } }, - "ttlSecondsAfterFinished": -339602975 + "ttlSecondsAfterFinished": -339602975, + "completionMode": "泐ɻvŰ`Ǧɝ憑ǖ菐u鸚Y髬.ʂmD" } }, - "successfulJobsHistoryLimit": 305459364, - "failedJobsHistoryLimit": -1565042829 + "successfulJobsHistoryLimit": 1380163777, + "failedJobsHistoryLimit": -406189540 }, "status": { "active": [ @@ -1534,7 +1535,7 @@ "kind": "479", "namespace": "480", "name": "481", - "uid": "vÐ仆dždĄ跞肞=ɴ", + "uid": "ɅĀ埰ʀ", "apiVersion": "482", "resourceVersion": "483", "fieldPath": "484" diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb index 68aaf6d2a79acd6bae2854703568dfef488497f7..333cb33d8e0e84a7acc0b2905c924f7b7abd2c2b 100644 GIT binary patch delta 133 zcmX?U`N(pDJmcPp3hNjjPCReQG|PN48{>M$mz%dS9+OJ*QhK&|!pYrbM>ixKUv_ft zv$l!Hr@iQ(Q2KJmtjL$E*XW&U%5^a~c(890tHqotEB5{S4+M--+FY7k%qHfRLd+%> k22w!6P>J(o>yd`1^EaGo&;s&}jDUo(6_7BIVo+iL0Mr{fN&o-= delta 108 zcmaE4dD3!%JmZpy3hNm6PCReQ^v!HC8{>M$qno!e9+UDlSn}}lL`I9x>+Vkd^&bcr zrOdcYxR_1MErpm(EDWT8grSm1+2ILKcDEgGOF7c=V*9)oP4jF|ZqWit85scyV=EwG JBE_J@008cMD$)P| diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml index 6848d6eefd22..15ca59cac210 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml @@ -31,7 +31,7 @@ metadata: uid: "7" spec: concurrencyPolicy: Hr鯹)晿D(e_`PrSUCar)+RMhR&aDF!750Hz=bpa1{> diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.JobTemplate.yaml b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.JobTemplate.yaml index 9a1297b0c9b9..f72e3909151f 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.JobTemplate.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.JobTemplate.yaml @@ -62,6 +62,7 @@ template: spec: activeDeadlineSeconds: -9086179100394185427 backoffLimit: -1796008812 + completionMode: tS誖Śs垦 completions: -1771909905 manualSelector: false parallelism: -443114323 diff --git a/staging/src/k8s.io/api/testdata/v1.19.0/batch.v1.Job.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.19.0/batch.v1.Job.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..17b39b06efce3286d15f113067abb77b4daf4666 GIT binary patch literal 6673 zcmY*d33wD$w(ja6#1gSB#mTfjr)^PV@K&nstu-^F5&>dSW=g_3fh;B=A@TWr zAwYmEWFwG;ECfOlmH>f}4U!?M1;j%Gx+6{sNcO`(Ou*08R=o{ zE7_;mS({Q{(z8*jBq9MRnJ8iYf^;<>sS=Nb7nUzgN=)RJNZb^%nBU4pv)nRWB{7dj>z-HEB7R#mgM$Klj$zGM=FC{Jnz3p(V=ZR7r~jAk zqmE*KTjNtA=uD6WSro9QV1if8k~I^7 zGZQg|K4Ewwsk7iowwj4z=K`lny`4S&x&hCr@e)siXJ2%TnMh&P5w*Zfv=Zh~7_M_O z{pb4pN4osSyZs&KoPB|o>Okr6CaYNzuvrqhGqLP2%bF$3^|9;>cpzM4aJX;$xTAOr z_b@xtU(*+Cyy)v~a~$#?z2qO-^_sV_ox6jLP_byWz<7x>OLEk=my4RG+D!JDz)^ss zMzs%>ow@Nn{4q9D%yeLDz}Bs1S-_95W?AI!0}AJoNz|904S(}(XlT^m&)vLXwwdBU zBpFtg+60#jM~2n5+=a ztVmHz(o}esEle`FoC1p|s@1G$cC(_#m{rh#D#pP~6+g?QSm76Dsgjj)WRMy(ufiNv zO@)K1Ait_dne6aY6SAU!ikw*kH`a*VtVuDHFAJsun5DriO)X+D`xv}v9A$y$>w@(j z)~v%c9a}+#;On;iUtHZEVHpX2{>iP-!Ah8)!VqG4x2>)4yAa1})+G?etjpC5oJ@zc zbv249baAuuM<`I!JkGVY^FBme%MHWy9|zJ1w!mVq~pwk zlr2-X0*8VhH3o==f#^N3HkA&(ilX)I;J)^WmS4>Bo!{jdP+_QuqfOZCXdVQ4;Em8> z(kL?>Ua$r)7}PYu@bC&Q3`7hamS7tb18)Qak>Y^J@j#RWAnJ?EpP2&;yN6*PWJ8*f zISb5(s}Dqt+H-?EN|TWw5hGuef?*kC9GQhw0HPs*w@};qF@A}CnGw1 zlmsvb6;cvEWqw9~v4x?0Vy9xkjQ}EtM<~~sk0RLjBG}Um{iM=Q8Xc>H9%UisPG4i& zuO9O??~23sfjQv7vZ#v+oJWv=X#F1^Dtov2O3_R}hvOH&{`iu!=-Y^7x((bNAS5O9 zOt+NfR6FIXu)M6<@AlR!3DxPS$PyN8_$xZ!WBq6|%-N>c@U4a4TM39|5Nc803wZ$wDd(R#E-l~UHC zT!e(w)O?wTkb=+_K^61!)3Q~Rvsui{0FX^nM3oS<4dGlV3!#+Qb!jR$Tfo95v=rc3 z+qlpuFi_4al%y;ZRfE@_OJ8Ix<<}wJSdzE)`SeWg0e0$3>o8ifikm(+V}16LjGTGf zew&>Gicv-UF4FiR!?P-oV8gY7Mzc5_9*{!5zJ;XZ!Y^AMbQ^W8kSt4Gg zrKeV9>jUgPktU0yrtFO{l37iZuUOjq|JE1QTU66%bw`Bl^5-|M-V8xxbpOo@ABVpQ zZP%`SG$lEmog0=- z(BvxGGd{&ucEed~jy`_Y_Z1_wc^o5Qqo(X-*r!7q0=m-;b^ODII`|3JrO6F-fLyFA z5IS!wO9xQE0OQYx=j+^4Yy=U6+w+MaTHzhr?+o;Ht#Q@*N2;jgL;%<&g7o7;grYeS zlvE&U)@AlILAnr`|UhiUJ-3?TQ^83byvoUgp>tXxNbiH-(7ci$nws5K&6LpS{~T{A6%fNpQH+v2XM| z{UjGM%pS&6$v{AZkc1&IxHF^(nS$!%Be6gnGaaYl1}pRmC=j9`RwRc;Y7+Mhe9&Z$ zI!?_s_s-jnLoKb?A8=j_?CSEi4LYj5$F~Q{Pi@Tej+Fdo*#1=cn)ag>AvvSw5G=)S z?r0DDLe#{YUv`I=w7p;RzaK_K?>qWQ`J}t;*&Cc{o#{B{Z#?chS05;Ce`U+-j;cUw zk>il9&r>zncKPIqFPP}h>YL8A1>3GA;fTQT0Z4Bm2;>Do^!Ny(3kg8PBp`UD=@ru^ zQ&uO#Qak9>$hrjT)8Qr(U9q(N_3fM2tNJb_M=`d)4ekE)ukUo-oF6^vsTdt-{rJDW zWdz^5e{2t2sk=IlZX$sn0TJV=EvTar9ik7>^@M0oTYtUvgb^@9~xQSm9me?xt*wW0uobolA*q`0$;T z+-e%$CCED-b(Z=L@AVzt;XP0rXziKhY4i=Y1$G|_969Vb7%b^U{_|BZQxqXdhab#6 z`{h|XYHycq^!#mV1}e-W#wr|&PnItH6P{V#cIYn zUgI9hOZ#ntv<(@8A%iIDe3FTwa+#Ah7x8nr->|cv6A+Km3<0GfWm%@N1TEwFr6@j` z=Rp#F8Hy(cpCYPj*PzUhNV5r5k-SpO+JqL#`No`rrSU6xJ!773C~(qTQOyEX;gp5R z#v&>__?Lur>)R1*lbNkFvxgZr7n2m&Z3|uxu^nxebIBU`U0;AQ&{jTM6p@+5v$p+ZUSSs=9d7zSQqL!AkEyXa_bE$0d z2AqS^G@_$dvyckXp{$L$8_27u0LcZK0KSm}vg=?x$RYA#^8|zctzoR;m*VGPsYQqv z2%nb8BT-niIT0lpNjzv7NyfsLmi~Lb0V*PpWl2Qk&M}cUJL^w}BM8^duyqXhzimD; zjQAJ88vH6l#mE5fd`XoNzdr3{!w^v}ch~Z{8+atmMOplOZas^hmlkY6D+L3b*D$0- zJdYF&sdKg@83z0sXw_VlsDqRS&##&vvZ(+W5|Um*3qeYL5fH&JvgcBfjfH4F|Kf_A zr4nf2MM;ElJemWMS&{&SBuQ?78!jnr%x94_52E#hN|Q#F)&;6Yvnd@5@JSOaU!6JH zG#RizK2e!pudZ=ySR?2zN)lJWYn3zDksf~@Be_DQ!{VnM8)?mF)s>dSs@eAs>N+0fpAkr5cWE%&l?@LwuFy3B+!Dv?;)hR05s zqrR*h7@KDM`rY7>$z;lX_VN`*I9}4QHpRoR8$(-S%*}@_p^Tz2w)gVo_h_2*JuC(4 zz>@tx#!6I(LxMnW1coY;saCD;TFvb+P5CNd z6Bq|W0r?#L(QoHJm;fANBHC@SzRso%d8=J@u8Y_DyIhwt+^w#miMmyRb2~i8p+OYd z1zIIAnuJtsvV8D|x|q;uiIfU#IqPw6$C0OfyZ1llFYXJRX>kk(8c%!91j;YC_fBm0 zRTO*b#>)abhYR``R%Rb|wk{s496uU3*h=_Z6ddXB6?J=?y2snR11-*r+ z-;zxpN%jmokN8fPI%?>S5-DU%dl=?+Iwfk5hf=+YkjqY{Q=$enOB-Wz*DhWRy`yHd zb!^C2bYQ$o80%l{taG069V=TNJkb|Qw?x`mz=F`jyv8Pm3?xdN0@8@`4+ate0I;aq z?{f`I9LW!y-S@1g&_8_YsbI~}(y$BClugwC#r-io3l=ShI(Bv35!G2TaLsJ{uJ!Bs zi0B(V-}=G{w@_c*aV_lmos6l60hgDcfWFIBu8j72s$LmC_zQ|iQ0XQFdRu~h#je9! zWY3}Ke;m0rxNrLxA3hptPe>9RK$3+~z%5xEXU=z5eF43O2VgN;K|=@5O7kRIa!IoM z4rBV1nZZu?4Ic5A9`WrQ@r@K-r!$qmF{XbqmhqnPa#!hC;cS0Z#ro&c<`kKs>EUmx zr?{fYdWpG$iTtSJ`!$VgC8Pa&ys0+7F^zRVfl6pEg^1viL;Njh#dT;0H@iX4WGCC7e&?cBJiq$Y^6WaYD zaCHH+32pt5aO;OCVi>fc_d}UbnubA}5KucIw8{4f!VMu>jQ!eRMgCarwf^E@S zFi=m*VdqirCD7-Lp+Drd_YxY{h$P#mJ!e<8%@VVz#}epa0#SeTSOV=!;OLgY+N<8l zLjG#a=Qkp39S3$F3PW}Dcu8r=G-0H^qdJVF$=U3}(5|w;K_38mz9GPVW8#yOp*TWh zY-e-ljUOGW@V0bkhIW=|#xMVCStrx7PF5@j8k(Gw1<9!YR>c(C*yWM2Dbc0D!99}~ z##h>Rb!rN937-k=sz|gHCCU#sl_+piiK2!}Q9>&zqUe*6;rF)=4@Xd}Bv8*U4J`l_ zJ%I(F+c8E0PUQRyz5*c+c7y}=!-;0a9!2&4nyP*eO-aqHxQYAW!x ztcU$4PsQrq^W~cY7fwZcJ8QSj^R_kn%gWuowr%dtf>&8^oUz^w9P8K-D67dFukj3p z@sy}=i-`u2fM_C^Uco0G*EajRDNnyn;NyS zCMR?3UD2*~(J`>Wb!zMq{h)-3Pe4mksQ4rh-a=?eFl~QKKFSm=&1>>QcnCdqm?()o zOeQ-&Y+L9LnsbL*axtdWru?|xUY)?%_pM%J9^|Kb&kDJDWvBn(8h ez1dNEWzuyU$~^;<3CI6kKlOD)oXKjkoBki7Pkp`s literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.CronJob.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.CronJob.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..4b7cb17bf1b451fd694ee888a57f67435354f2e8 GIT binary patch literal 7371 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 literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.JobTemplate.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.JobTemplate.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..f742b1e2b4234d22ffbc7be8bdb2f6f5f25ca53a GIT binary patch literal 7317 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 KSQ8+q>3;!|^tHqQ literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.CronJob.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.CronJob.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..00c5f8fafca968a2dcba0657e5c4f99101c93cdb GIT binary patch literal 7372 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 literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..c8b633fdf9abc73fa9986da6c00ad42085692317 GIT binary patch literal 7318 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 KW2^}f)bzi+`?fIv literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.20.0/batch.v1.Job.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.20.0/batch.v1.Job.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..17b39b06efce3286d15f113067abb77b4daf4666 GIT binary patch literal 6673 zcmY*d33wD$w(ja6#1gSB#mTfjr)^PV@K&nstu-^F5&>dSW=g_3fh;B=A@TWr zAwYmEWFwG;ECfOlmH>f}4U!?M1;j%Gx+6{sNcO`(Ou*08R=o{ zE7_;mS({Q{(z8*jBq9MRnJ8iYf^;<>sS=Nb7nUzgN=)RJNZb^%nBU4pv)nRWB{7dj>z-HEB7R#mgM$Klj$zGM=FC{Jnz3p(V=ZR7r~jAk zqmE*KTjNtA=uD6WSro9QV1if8k~I^7 zGZQg|K4Ewwsk7iowwj4z=K`lny`4S&x&hCr@e)siXJ2%TnMh&P5w*Zfv=Zh~7_M_O z{pb4pN4osSyZs&KoPB|o>Okr6CaYNzuvrqhGqLP2%bF$3^|9;>cpzM4aJX;$xTAOr z_b@xtU(*+Cyy)v~a~$#?z2qO-^_sV_ox6jLP_byWz<7x>OLEk=my4RG+D!JDz)^ss zMzs%>ow@Nn{4q9D%yeLDz}Bs1S-_95W?AI!0}AJoNz|904S(}(XlT^m&)vLXwwdBU zBpFtg+60#jM~2n5+=a ztVmHz(o}esEle`FoC1p|s@1G$cC(_#m{rh#D#pP~6+g?QSm76Dsgjj)WRMy(ufiNv zO@)K1Ait_dne6aY6SAU!ikw*kH`a*VtVuDHFAJsun5DriO)X+D`xv}v9A$y$>w@(j z)~v%c9a}+#;On;iUtHZEVHpX2{>iP-!Ah8)!VqG4x2>)4yAa1})+G?etjpC5oJ@zc zbv249baAuuM<`I!JkGVY^FBme%MHWy9|zJ1w!mVq~pwk zlr2-X0*8VhH3o==f#^N3HkA&(ilX)I;J)^WmS4>Bo!{jdP+_QuqfOZCXdVQ4;Em8> z(kL?>Ua$r)7}PYu@bC&Q3`7hamS7tb18)Qak>Y^J@j#RWAnJ?EpP2&;yN6*PWJ8*f zISb5(s}Dqt+H-?EN|TWw5hGuef?*kC9GQhw0HPs*w@};qF@A}CnGw1 zlmsvb6;cvEWqw9~v4x?0Vy9xkjQ}EtM<~~sk0RLjBG}Um{iM=Q8Xc>H9%UisPG4i& zuO9O??~23sfjQv7vZ#v+oJWv=X#F1^Dtov2O3_R}hvOH&{`iu!=-Y^7x((bNAS5O9 zOt+NfR6FIXu)M6<@AlR!3DxPS$PyN8_$xZ!WBq6|%-N>c@U4a4TM39|5Nc803wZ$wDd(R#E-l~UHC zT!e(w)O?wTkb=+_K^61!)3Q~Rvsui{0FX^nM3oS<4dGlV3!#+Qb!jR$Tfo95v=rc3 z+qlpuFi_4al%y;ZRfE@_OJ8Ix<<}wJSdzE)`SeWg0e0$3>o8ifikm(+V}16LjGTGf zew&>Gicv-UF4FiR!?P-oV8gY7Mzc5_9*{!5zJ;XZ!Y^AMbQ^W8kSt4Gg zrKeV9>jUgPktU0yrtFO{l37iZuUOjq|JE1QTU66%bw`Bl^5-|M-V8xxbpOo@ABVpQ zZP%`SG$lEmog0=- z(BvxGGd{&ucEed~jy`_Y_Z1_wc^o5Qqo(X-*r!7q0=m-;b^ODII`|3JrO6F-fLyFA z5IS!wO9xQE0OQYx=j+^4Yy=U6+w+MaTHzhr?+o;Ht#Q@*N2;jgL;%<&g7o7;grYeS zlvE&U)@AlILAnr`|UhiUJ-3?TQ^83byvoUgp>tXxNbiH-(7ci$nws5K&6LpS{~T{A6%fNpQH+v2XM| z{UjGM%pS&6$v{AZkc1&IxHF^(nS$!%Be6gnGaaYl1}pRmC=j9`RwRc;Y7+Mhe9&Z$ zI!?_s_s-jnLoKb?A8=j_?CSEi4LYj5$F~Q{Pi@Tej+Fdo*#1=cn)ag>AvvSw5G=)S z?r0DDLe#{YUv`I=w7p;RzaK_K?>qWQ`J}t;*&Cc{o#{B{Z#?chS05;Ce`U+-j;cUw zk>il9&r>zncKPIqFPP}h>YL8A1>3GA;fTQT0Z4Bm2;>Do^!Ny(3kg8PBp`UD=@ru^ zQ&uO#Qak9>$hrjT)8Qr(U9q(N_3fM2tNJb_M=`d)4ekE)ukUo-oF6^vsTdt-{rJDW zWdz^5e{2t2sk=IlZX$sn0TJV=EvTar9ik7>^@M0oTYtUvgb^@9~xQSm9me?xt*wW0uobolA*q`0$;T z+-e%$CCED-b(Z=L@AVzt;XP0rXziKhY4i=Y1$G|_969Vb7%b^U{_|BZQxqXdhab#6 z`{h|XYHycq^!#mV1}e-W#wr|&PnItH6P{V#cIYn zUgI9hOZ#ntv<(@8A%iIDe3FTwa+#Ah7x8nr->|cv6A+Km3<0GfWm%@N1TEwFr6@j` z=Rp#F8Hy(cpCYPj*PzUhNV5r5k-SpO+JqL#`No`rrSU6xJ!773C~(qTQOyEX;gp5R z#v&>__?Lur>)R1*lbNkFvxgZr7n2m&Z3|uxu^nxebIBU`U0;AQ&{jTM6p@+5v$p+ZUSSs=9d7zSQqL!AkEyXa_bE$0d z2AqS^G@_$dvyckXp{$L$8_27u0LcZK0KSm}vg=?x$RYA#^8|zctzoR;m*VGPsYQqv z2%nb8BT-niIT0lpNjzv7NyfsLmi~Lb0V*PpWl2Qk&M}cUJL^w}BM8^duyqXhzimD; zjQAJ88vH6l#mE5fd`XoNzdr3{!w^v}ch~Z{8+atmMOplOZas^hmlkY6D+L3b*D$0- zJdYF&sdKg@83z0sXw_VlsDqRS&##&vvZ(+W5|Um*3qeYL5fH&JvgcBfjfH4F|Kf_A zr4nf2MM;ElJemWMS&{&SBuQ?78!jnr%x94_52E#hN|Q#F)&;6Yvnd@5@JSOaU!6JH zG#RizK2e!pudZ=ySR?2zN)lJWYn3zDksf~@Be_DQ!{VnM8)?mF)s>dSs@eAs>N+0fpAkr5cWE%&l?@LwuFy3B+!Dv?;)hR05s zqrR*h7@KDM`rY7>$z;lX_VN`*I9}4QHpRoR8$(-S%*}@_p^Tz2w)gVo_h_2*JuC(4 zz>@tx#!6I(LxMnW1coY;saCD;TFvb+P5CNd z6Bq|W0r?#L(QoHJm;fANBHC@SzRso%d8=J@u8Y_DyIhwt+^w#miMmyRb2~i8p+OYd z1zIIAnuJtsvV8D|x|q;uiIfU#IqPw6$C0OfyZ1llFYXJRX>kk(8c%!91j;YC_fBm0 zRTO*b#>)abhYR``R%Rb|wk{s496uU3*h=_Z6ddXB6?J=?y2snR11-*r+ z-;zxpN%jmokN8fPI%?>S5-DU%dl=?+Iwfk5hf=+YkjqY{Q=$enOB-Wz*DhWRy`yHd zb!^C2bYQ$o80%l{taG069V=TNJkb|Qw?x`mz=F`jyv8Pm3?xdN0@8@`4+ate0I;aq z?{f`I9LW!y-S@1g&_8_YsbI~}(y$BClugwC#r-io3l=ShI(Bv35!G2TaLsJ{uJ!Bs zi0B(V-}=G{w@_c*aV_lmos6l60hgDcfWFIBu8j72s$LmC_zQ|iQ0XQFdRu~h#je9! zWY3}Ke;m0rxNrLxA3hptPe>9RK$3+~z%5xEXU=z5eF43O2VgN;K|=@5O7kRIa!IoM z4rBV1nZZu?4Ic5A9`WrQ@r@K-r!$qmF{XbqmhqnPa#!hC;cS0Z#ro&c<`kKs>EUmx zr?{fYdWpG$iTtSJ`!$VgC8Pa&ys0+7F^zRVfl6pEg^1viL;Njh#dT;0H@iX4WGCC7e&?cBJiq$Y^6WaYD zaCHH+32pt5aO;OCVi>fc_d}UbnubA}5KucIw8{4f!VMu>jQ!eRMgCarwf^E@S zFi=m*VdqirCD7-Lp+Drd_YxY{h$P#mJ!e<8%@VVz#}epa0#SeTSOV=!;OLgY+N<8l zLjG#a=Qkp39S3$F3PW}Dcu8r=G-0H^qdJVF$=U3}(5|w;K_38mz9GPVW8#yOp*TWh zY-e-ljUOGW@V0bkhIW=|#xMVCStrx7PF5@j8k(Gw1<9!YR>c(C*yWM2Dbc0D!99}~ z##h>Rb!rN937-k=sz|gHCCU#sl_+piiK2!}Q9>&zqUe*6;rF)=4@Xd}Bv8*U4J`l_ zJ%I(F+c8E0PUQRyz5*c+c7y}=!-;0a9!2&4nyP*eO-aqHxQYAW!x ztcU$4PsQrq^W~cY7fwZcJ8QSj^R_kn%gWuowr%dtf>&8^oUz^w9P8K-D67dFukj3p z@sy}=i-`u2fM_C^Uco0G*EajRDNnyn;NyS zCMR?3UD2*~(J`>Wb!zMq{h)-3Pe4mksQ4rh-a=?eFl~QKKFSm=&1>>QcnCdqm?()o zOeQ-&Y+L9LnsbL*axtdWru?|xUY)?%_pM%J9^|Kb&kDJDWvBn(8h ez1dNEWzuyU$~^;<3CI6kKlOD)oXKjkoBki7Pkp`s literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.CronJob.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.CronJob.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..4b7cb17bf1b451fd694ee888a57f67435354f2e8 GIT binary patch literal 7371 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 literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.JobTemplate.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.JobTemplate.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..f742b1e2b4234d22ffbc7be8bdb2f6f5f25ca53a GIT binary patch literal 7317 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 KSQ8+q>3;!|^tHqQ literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.CronJob.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.CronJob.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..00c5f8fafca968a2dcba0657e5c4f99101c93cdb GIT binary patch literal 7372 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 literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb b/staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb new file mode 100644 index 0000000000000000000000000000000000000000..c8b633fdf9abc73fa9986da6c00ad42085692317 GIT binary patch literal 7318 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 KW2^}f)bzi+`?fIv literal 0 HcmV?d00001 diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go index ec7b6e91f320..5e8cc8f824d5 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go @@ -2160,6 +2160,9 @@ func describeJob(job *batchv1.Job, events *corev1.EventList) (string, error) { } else { w.Write(LEVEL_0, "Completions:\t\n") } + if job.Spec.CompletionMode != "" { + w.Write(LEVEL_0, "Completion Mode:\t%s\n", job.Spec.CompletionMode) + } if job.Status.StartTime != nil { w.Write(LEVEL_0, "Start Time:\t%s\n", job.Status.StartTime.Time.Format(time.RFC1123Z)) } @@ -2173,6 +2176,9 @@ func describeJob(job *batchv1.Job, events *corev1.EventList) (string, error) { w.Write(LEVEL_0, "Active Deadline Seconds:\t%ds\n", *job.Spec.ActiveDeadlineSeconds) } w.Write(LEVEL_0, "Pods Statuses:\t%d Running / %d Succeeded / %d Failed\n", job.Status.Active, job.Status.Succeeded, job.Status.Failed) + if job.Spec.CompletionMode == batchv1.IndexedCompletion { + w.Write(LEVEL_0, "Completed Indexes:\t%s\n", capIndexesListOrNone(job.Status.CompletedIndexes, 50)) + } DescribePodTemplate(&job.Spec.Template, w) if events != nil { DescribeEvents(events, w) @@ -2181,6 +2187,22 @@ func describeJob(job *batchv1.Job, events *corev1.EventList) (string, error) { }) } +func capIndexesListOrNone(indexes string, softLimit int) string { + if len(indexes) == 0 { + return "" + } + ix := softLimit + for ; ix < len(indexes); ix++ { + if indexes[ix] == ',' { + break + } + } + if ix >= len(indexes) { + return indexes + } + return indexes[:ix+1] + "..." +} + // CronJobDescriber generates information about a cron job and the jobs it has created. type CronJobDescriber struct { client clientset.Interface diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go index d1e6f169312a..9ec40285aadf 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go @@ -2062,6 +2062,88 @@ func TestDescribeDeployment(t *testing.T) { } } +func TestDescribeJob(t *testing.T) { + cases := map[string]struct { + job *batchv1.Job + wantCompletedIndexes string + }{ + "not indexed": { + job: &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: batchv1.JobSpec{ + CompletionMode: batchv1.NonIndexedCompletion, + }, + }, + }, + "no indexes": { + job: &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: batchv1.JobSpec{ + CompletionMode: batchv1.IndexedCompletion, + }, + }, + wantCompletedIndexes: "", + }, + "few completed indexes": { + job: &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: batchv1.JobSpec{ + CompletionMode: batchv1.IndexedCompletion, + }, + Status: batchv1.JobStatus{ + CompletedIndexes: "0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32", + }, + }, + wantCompletedIndexes: "0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32", + }, + "too many completed indexes": { + job: &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + Spec: batchv1.JobSpec{ + CompletionMode: batchv1.IndexedCompletion, + }, + Status: batchv1.JobStatus{ + CompletedIndexes: "0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32-34,36,37", + }, + }, + wantCompletedIndexes: "0-5,7,9,10,12,13,15,16,18,20,21,23,24,26,27,29,30,32-34,...", + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + client := &describeClient{ + T: t, + Namespace: tc.job.Namespace, + Interface: fake.NewSimpleClientset(tc.job), + } + describer := JobDescriber{Interface: client} + out, err := describer.Describe(tc.job.Namespace, tc.job.Name, DescriberSettings{ShowEvents: true}) + if err != nil { + t.Fatalf("Unexpected error describing object: %v", err) + } + if tc.wantCompletedIndexes != "" { + if !strings.Contains(out, fmt.Sprintf("Completed Indexes: %s\n", tc.wantCompletedIndexes)) { + t.Errorf("Output didn't contain wanted Completed Indexes:\n%s", out) + } + } else if strings.Contains(out, fmt.Sprintf("Completed Indexes:")) { + t.Errorf("Output contains unexpected completed indexes:\n%s", out) + } + }) + } +} + func TestDescribeIngress(t *testing.T) { backendV1beta1 := networkingv1beta1.IngressBackend{ ServiceName: "default-backend",