diff --git a/api/jobs.go b/api/jobs.go index a248f4519..cc303d523 100644 --- a/api/jobs.go +++ b/api/jobs.go @@ -159,6 +159,15 @@ func (j *Jobs) Plan(job *Job, diff bool, q *WriteOptions) (*JobPlanResponse, *Wr return &resp, wm, nil } +func (j *Jobs) Summary(jobID string, q *QueryOptions) (*JobSummary, *QueryMeta, error) { + var resp JobSummary + qm, err := j.client.query("/v1/job/"+jobID+"/summary", &resp, q) + if err != nil { + return nil, nil, err + } + return &resp, qm, nil +} + // periodicForceResponse is used to deserialize a force response type periodicForceResponse struct { EvalID string @@ -199,6 +208,27 @@ type Job struct { JobModifyIndex uint64 } +// JobSummary summarizes the state of the allocations of a job +type JobSummary struct { + JobID string + Summary map[string]TaskGroupSummary + + // Raft Indexes + CreateIndex uint64 + ModifyIndex uint64 +} + +// TaskGroup summarizes the state of all the allocations of a particular +// TaskGroup +type TaskGroupSummary struct { + Queued int + Complete int + Failed int + Running int + Starting int + Lost int +} + // JobListStub is used to return a subset of information about // jobs during list operations. type JobListStub struct { @@ -209,6 +239,7 @@ type JobListStub struct { Priority int Status string StatusDescription string + JobSummary *JobSummary CreateIndex uint64 ModifyIndex uint64 JobModifyIndex uint64 diff --git a/api/jobs_test.go b/api/jobs_test.go index 8bda1708b..fea8fc11e 100644 --- a/api/jobs_test.go +++ b/api/jobs_test.go @@ -488,6 +488,48 @@ func TestJobs_Plan(t *testing.T) { } } +func TestJobs_JobSummary(t *testing.T) { + c, s := makeClient(t, nil, nil) + defer s.Stop() + jobs := c.Jobs() + + // Trying to retrieve a job summary before the job exists + // returns an error + _, _, err := jobs.Summary("job1", nil) + if err == nil || !strings.Contains(err.Error(), "not found") { + t.Fatalf("expected not found error, got: %#v", err) + } + + // Register the job + job := testJob() + _, wm, err := jobs.Register(job, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + assertWriteMeta(t, wm) + + // Query the job again and ensure it exists + result, qm, err := jobs.Summary("job1", nil) + if err != nil { + t.Fatalf("err: %s", err) + } + assertQueryMeta(t, qm) + + expectedJobSummary := JobSummary{ + JobID: job.ID, + Summary: map[string]TaskGroupSummary{ + job.TaskGroups[0].Name: {}, + }, + CreateIndex: result.CreateIndex, + ModifyIndex: result.ModifyIndex, + } + + // Check that the result is what we expect + if !reflect.DeepEqual(&expectedJobSummary, result) { + t.Fatalf("expect: %#v, got: %#v", expectedJobSummary, result) + } +} + func TestJobs_NewBatchJob(t *testing.T) { job := NewBatchJob("job1", "myjob", "region1", 5) expect := &Job{ diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 2e7d65b31..3273261c2 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -1226,10 +1226,10 @@ type JobListStub struct { Priority int Status string StatusDescription string + JobSummary *JobSummary CreateIndex uint64 ModifyIndex uint64 JobModifyIndex uint64 - JobSummary *JobSummary } // UpdateStrategy is used to modify how updates are done