From 3af483b7e4f66f26adcc5fd1e19c13d4b2bd45de 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 Kubernetes-commit: a1a5868a5ae8c18df39b386d2424ff04c89826fb --- pkg/describe/describe.go | 22 ++++++++++ pkg/describe/describe_test.go | 82 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/pkg/describe/describe.go b/pkg/describe/describe.go index ec7b6e91..5e8cc8f8 100644 --- a/pkg/describe/describe.go +++ b/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/pkg/describe/describe_test.go b/pkg/describe/describe_test.go index d1e6f169..9ec40285 100644 --- a/pkg/describe/describe_test.go +++ b/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",