mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Add Job.spec.completionMode and Job.status.completedIndexes
And IndexedJob feature gate, disabled by default. Update JobDescriber
This commit is contained in:
parent
6404eda8de
commit
a1a5868a5a
8
api/openapi-spec/swagger.json
generated
8
api/openapi-spec/swagger.json
generated
@ -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."
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
pkg/apis/batch/v1/zz_generated.conversion.go
generated
4
pkg/apis/batch/v1/zz_generated.conversion.go
generated
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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},
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
203
staging/src/k8s.io/api/batch/v1/generated.pb.go
generated
203
staging/src/k8s.io/api/batch/v1/generated.pb.go
generated
@ -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:])
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
BIN
staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb
vendored
BIN
staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb
vendored
Binary file not shown.
@ -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
|
||||
|
@ -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"
|
||||
|
Binary file not shown.
@ -31,7 +31,7 @@ metadata:
|
||||
uid: "7"
|
||||
spec:
|
||||
concurrencyPolicy: Hr鯹)晿<o,c鮽ort昍řČ扷5Ɨ
|
||||
failedJobsHistoryLimit: -1565042829
|
||||
failedJobsHistoryLimit: -406189540
|
||||
jobTemplate:
|
||||
metadata:
|
||||
annotations:
|
||||
@ -65,6 +65,7 @@ spec:
|
||||
spec:
|
||||
activeDeadlineSeconds: -1483125035702892746
|
||||
backoffLimit: -1822122846
|
||||
completionMode: 泐ɻvŰ`Ǧɝ憑ǖ菐u鸚Y髬.ʂmD
|
||||
completions: -106888179
|
||||
manualSelector: true
|
||||
parallelism: -856030588
|
||||
@ -1046,7 +1047,7 @@ spec:
|
||||
ttlSecondsAfterFinished: -339602975
|
||||
schedule: "19"
|
||||
startingDeadlineSeconds: -2555947251840004808
|
||||
successfulJobsHistoryLimit: 305459364
|
||||
successfulJobsHistoryLimit: 1380163777
|
||||
suspend: true
|
||||
status:
|
||||
active:
|
||||
@ -1056,4 +1057,4 @@ status:
|
||||
name: "481"
|
||||
namespace: "480"
|
||||
resourceVersion: "483"
|
||||
uid: vÐ仆dždĄ跞肞=ɴ
|
||||
uid: ɅĀ埰ʀ
|
||||
|
@ -1518,7 +1518,8 @@
|
||||
"setHostnameAsFQDN": true
|
||||
}
|
||||
},
|
||||
"ttlSecondsAfterFinished": -1766935785
|
||||
"ttlSecondsAfterFinished": -1766935785,
|
||||
"completionMode": "tS誖Śs垦"
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
@ -62,6 +62,7 @@ template:
|
||||
spec:
|
||||
activeDeadlineSeconds: -9086179100394185427
|
||||
backoffLimit: -1796008812
|
||||
completionMode: tS誖Śs垦
|
||||
completions: -1771909905
|
||||
manualSelector: false
|
||||
parallelism: -443114323
|
||||
|
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v1.Job.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v1.Job.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.CronJob.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.CronJob.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.JobTemplate.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v1beta1.JobTemplate.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.CronJob.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.CronJob.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.19.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v1.Job.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v1.Job.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.CronJob.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.CronJob.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.JobTemplate.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v1beta1.JobTemplate.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.CronJob.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.CronJob.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb
vendored
Normal file
BIN
staging/src/k8s.io/api/testdata/v1.20.0/batch.v2alpha1.JobTemplate.after_roundtrip.pb
vendored
Normal file
Binary file not shown.
@ -2160,6 +2160,9 @@ func describeJob(job *batchv1.Job, events *corev1.EventList) (string, error) {
|
||||
} else {
|
||||
w.Write(LEVEL_0, "Completions:\t<unset>\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 "<none>"
|
||||
}
|
||||
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
|
||||
|
@ -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: "<none>",
|
||||
},
|
||||
"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",
|
||||
|
Loading…
Reference in New Issue
Block a user