Add Job.spec.completionMode and Job.status.completedIndexes

And IndexedJob feature gate, disabled by default.
Update JobDescriber

Kubernetes-commit: a1a5868a5ae8c18df39b386d2424ff04c89826fb
This commit is contained in:
Aldo Culquicondor 2020-12-30 11:42:01 -05:00 committed by Kubernetes Publisher
parent e032aa6f92
commit 3af483b7e4
2 changed files with 104 additions and 0 deletions

View File

@ -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

View File

@ -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",