Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

printers: Add Job status to jobs table #123226

Merged
merged 1 commit into from Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 25 additions & 1 deletion pkg/printers/internalversion/printers.go
Expand Up @@ -167,6 +167,7 @@ func AddHandlers(h printers.PrintHandler) {

jobColumnDefinitions := []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "Status", Type: "string", Description: "Status of the job."},
{Name: "Completions", Type: "string", Description: batchv1.JobStatus{}.SwaggerDoc()["succeeded"]},
{Name: "Duration", Type: "string", Description: "Time required to complete the job."},
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
Expand Down Expand Up @@ -1013,6 +1014,15 @@ func hasPodReadyCondition(conditions []api.PodCondition) bool {
return false
}

func hasJobCondition(conditions []batch.JobCondition, conditionType batch.JobConditionType) bool {
for _, condition := range conditions {
if condition.Type == conditionType {
return condition.Status == api.ConditionTrue
}
}
return false
}

func printPodTemplate(obj *api.PodTemplate, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},
Expand Down Expand Up @@ -1155,8 +1165,22 @@ func printJob(obj *batch.Job, options printers.GenerateOptions) ([]metav1.TableR
default:
jobDuration = duration.HumanDuration(obj.Status.CompletionTime.Sub(obj.Status.StartTime.Time))
}
var status string
if hasJobCondition(obj.Status.Conditions, batch.JobComplete) {
status = "Complete"
} else if hasJobCondition(obj.Status.Conditions, batch.JobFailed) {
status = "Failed"
} else if obj.ObjectMeta.DeletionTimestamp != nil {
status = "Terminating"
} else if hasJobCondition(obj.Status.Conditions, batch.JobSuspended) {
status = "Suspended"
} else if hasJobCondition(obj.Status.Conditions, batch.JobFailureTarget) {
Copy link
Contributor

@kannon92 kannon92 Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mimowo do you think JobFailureTarget is useful for a user?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on a fence here. Terminating pods in reaction to FailJob action might take ~30s between SIGTERM and SIGKILL. Knowing that the job is doomed to be failed might be useful I guess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I'm happy with it then.

status = "FailureTarget"
} else {
status = "Running"
}

row.Cells = append(row.Cells, obj.Name, completions, jobDuration, translateTimestampSince(obj.CreationTimestamp))
row.Cells = append(row.Cells, obj.Name, status, completions, jobDuration, translateTimestampSince(obj.CreationTimestamp))
if options.Wide {
names, images := layoutContainerCells(obj.Spec.Template.Spec.Containers)
row.Cells = append(row.Cells, names, images, metav1.FormatLabelSelector(obj.Spec.Selector))
Expand Down
133 changes: 120 additions & 13 deletions pkg/printers/internalversion/printers_test.go
Expand Up @@ -2527,8 +2527,8 @@ func TestPrintJob(t *testing.T) {
},
},
options: printers.GenerateOptions{},
// Columns: Name, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job1", "1/2", "", "0s"}}},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job1", "Running", "1/2", "", "0s"}}},
},
// Generate table rows for Job with generate options "Wide".
{
Expand Down Expand Up @@ -2560,10 +2560,10 @@ func TestPrintJob(t *testing.T) {
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, Completions, Duration, Age, Containers, Images, Selectors
// Columns: Name, Status, Completions, Duration, Age, Containers, Images, Selectors
expected: []metav1.TableRow{
{
Cells: []interface{}{"job1", "1/2", "", "0s", "fake-job-container1,fake-job-container2", "fake-job-image1,fake-job-image2", "job-label=job-label-value"},
Cells: []interface{}{"job1", "Running", "1/2", "", "0s", "fake-job-container1,fake-job-container2", "fake-job-image1,fake-job-image2", "job-label=job-label-value"},
},
},
},
Expand All @@ -2582,8 +2582,8 @@ func TestPrintJob(t *testing.T) {
},
},
options: printers.GenerateOptions{},
// Columns: Name, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job2", "0/1", "", "10y"}}},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job2", "Running", "0/1", "", "10y"}}},
},
// Job with duration.
{
Expand All @@ -2602,8 +2602,8 @@ func TestPrintJob(t *testing.T) {
},
},
options: printers.GenerateOptions{},
// Columns: Name, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job3", "0/1", "30m", "10y"}}},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job3", "Running", "0/1", "30m", "10y"}}},
},
{
job: batch.Job{
Expand All @@ -2620,8 +2620,115 @@ func TestPrintJob(t *testing.T) {
},
},
options: printers.GenerateOptions{},
// Columns: Name, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job4", "0/1", "20m", "10y"}}},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job4", "Running", "0/1", "20m", "10y"}}},
},
{
job: batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job5",
CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
},
Spec: batch.JobSpec{
Completions: nil,
},
Status: batch.JobStatus{
Succeeded: 0,
Conditions: []batch.JobCondition{
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably include Failed or Complete. Not both. It is a bit confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Type: batch.JobComplete,
Status: api.ConditionTrue,
},
},
},
},
options: printers.GenerateOptions{},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job5", "Complete", "0/1", "", "0s"}}},
},
{
job: batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job6",
CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
},
Spec: batch.JobSpec{
Completions: nil,
},
Status: batch.JobStatus{
Succeeded: 0,
Conditions: []batch.JobCondition{
{
Type: batch.JobFailed,
Status: api.ConditionTrue,
},
},
},
},
options: printers.GenerateOptions{},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job6", "Failed", "0/1", "", "0s"}}},
},
{
job: batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job7",
CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
},
Spec: batch.JobSpec{
Completions: nil,
},
Status: batch.JobStatus{
Succeeded: 0,
Conditions: []batch.JobCondition{
{
Type: batch.JobSuspended,
Status: api.ConditionTrue,
},
},
},
},
options: printers.GenerateOptions{},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job7", "Suspended", "0/1", "", "0s"}}},
},
{
job: batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job8",
CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
},
Spec: batch.JobSpec{
Completions: nil,
},
Status: batch.JobStatus{
Succeeded: 0,
Conditions: []batch.JobCondition{
{
Type: batch.JobFailureTarget,
Status: api.ConditionTrue,
},
},
},
},
options: printers.GenerateOptions{},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job8", "FailureTarget", "0/1", "", "0s"}}},
},
{
job: batch.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job9",
CreationTimestamp: metav1.Time{Time: time.Now().Add(1.9e9)},
DeletionTimestamp: &metav1.Time{Time: time.Now().Add(1.9e9)},
},
Spec: batch.JobSpec{
Completions: nil,
},
},
options: printers.GenerateOptions{},
// Columns: Name, Status, Completions, Duration, Age
expected: []metav1.TableRow{{Cells: []interface{}{"job9", "Terminating", "0/1", "", "0s"}}},
},
}

Expand Down Expand Up @@ -2701,10 +2808,10 @@ func TestPrintJobList(t *testing.T) {
},
}

// Columns: Name, Completions, Duration, Age
// Columns: Name, Status, Completions, Duration, Age
expectedRows := []metav1.TableRow{
{Cells: []interface{}{"job1", "1/2", "", "0s"}},
{Cells: []interface{}{"job2", "2/2", "20m", "0s"}},
{Cells: []interface{}{"job1", "Running", "1/2", "", "0s"}},
{Cells: []interface{}{"job2", "Running", "2/2", "20m", "0s"}},
}

rows, err := printJobList(&jobList, printers.GenerateOptions{})
Expand Down