Merge pull request #1540 from novilabs/add-creation-time-to-job-status

Add creation time to job status
This commit is contained in:
Alex Dadgar
2016-08-15 21:04:14 -07:00
committed by GitHub
9 changed files with 90 additions and 72 deletions

View File

@@ -268,7 +268,7 @@ func (c *AllocStatusCommand) outputTaskStatus(state *api.TaskState) {
size := len(state.Events)
for i, event := range state.Events {
formatedTime := c.formatUnixNanoTime(event.Time)
formatedTime := formatUnixNanoTime(event.Time)
// Build up the description based on the event type.
var desc string
@@ -474,7 +474,7 @@ func (c *AllocStatusCommand) shortTaskStatus(alloc *api.Allocation) {
if l != 0 {
last := state.Events[l-1]
lastEvent = last.Type
lastTime = c.formatUnixNanoTime(last.Time)
lastTime = formatUnixNanoTime(last.Time)
}
tasks = append(tasks, fmt.Sprintf("%s|%s|%s|%s",
@@ -504,9 +504,3 @@ func (c *AllocStatusCommand) sortedTaskStateIterator(m map[string]*api.TaskState
close(output)
return output
}
// formatUnixNanoTime is a helper for formating time for output.
func (c *AllocStatusCommand) formatUnixNanoTime(nano int64) string {
t := time.Unix(0, nano)
return formatTime(t)
}

View File

@@ -51,6 +51,12 @@ func formatTime(t time.Time) string {
return t.Format("01/02/06 15:04:05 MST")
}
// formatUnixNanoTime is a helper for formatting time for output.
func formatUnixNanoTime(nano int64) string {
t := time.Unix(0, nano)
return formatTime(t)
}
// formatTimeDifference takes two times and determines their duration difference
// truncating to a passed unit.
// E.g. formatTimeDifference(first=1m22s33ms, second=1m28s55ms, time.Second) -> 6s

View File

@@ -20,8 +20,9 @@ const (
type StatusCommand struct {
Meta
length int
showEvals, verbose bool
length int
evals bool
verbose bool
}
func (c *StatusCommand) Help() string {
@@ -60,7 +61,7 @@ func (c *StatusCommand) Run(args []string) int {
flags := c.Meta.FlagSet("status", FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&short, "short", false, "")
flags.BoolVar(&c.showEvals, "evals", false, "")
flags.BoolVar(&c.evals, "evals", false, "")
flags.BoolVar(&c.verbose, "verbose", false, "")
if err := flags.Parse(args); err != nil {
@@ -95,22 +96,12 @@ func (c *StatusCommand) Run(args []string) int {
return 1
}
// No output if we have no jobs
if len(jobs) == 0 {
// No output if we have no jobs
c.Ui.Output("No running jobs")
return 0
} else {
c.Ui.Output(createStatusListOutput(jobs))
}
out := make([]string, len(jobs)+1)
out[0] = "ID|Type|Priority|Status"
for i, job := range jobs {
out[i+1] = fmt.Sprintf("%s|%s|%d|%s",
job.ID,
job.Type,
job.Priority,
job.Status)
}
c.Ui.Output(formatList(out))
return 0
}
@@ -126,16 +117,7 @@ func (c *StatusCommand) Run(args []string) int {
return 1
}
if len(jobs) > 1 && strings.TrimSpace(jobID) != jobs[0].ID {
out := make([]string, len(jobs)+1)
out[0] = "ID|Type|Priority|Status"
for i, job := range jobs {
out[i+1] = fmt.Sprintf("%s|%s|%d|%s",
job.ID,
job.Type,
job.Priority,
job.Status)
}
c.Ui.Output(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", formatList(out)))
c.Ui.Output(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs)))
return 0
}
// Prefix lookup matched a single job
@@ -306,7 +288,7 @@ func (c *StatusCommand) outputJobInfo(client *api.Client, job *api.Job) error {
}
}
if c.verbose || c.showEvals {
if c.verbose || c.evals {
c.Ui.Output(c.Colorize().Color("\n[bold]Evaluations[reset]"))
c.Ui.Output(formatList(evals))
}
@@ -319,15 +301,16 @@ func (c *StatusCommand) outputJobInfo(client *api.Client, job *api.Job) error {
c.Ui.Output(c.Colorize().Color("\n[bold]Allocations[reset]"))
if len(jobAllocs) > 0 {
allocs = make([]string, len(jobAllocs)+1)
allocs[0] = "ID|Eval ID|Node ID|Task Group|Desired|Status"
allocs[0] = "ID|Eval ID|Node ID|Task Group|Desired|Status|Created At"
for i, alloc := range jobAllocs {
allocs[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s",
allocs[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s|%s",
limit(alloc.ID, c.length),
limit(alloc.EvalID, c.length),
limit(alloc.NodeID, c.length),
alloc.TaskGroup,
alloc.DesiredStatus,
alloc.ClientStatus)
alloc.ClientStatus,
formatUnixNanoTime(alloc.CreateTime))
}
c.Ui.Output(formatList(allocs))
@@ -379,3 +362,17 @@ func convertApiJob(in *api.Job) (*structs.Job, error) {
}
return structJob, nil
}
// list general information about a list of jobs
func createStatusListOutput(jobs []*api.JobListStub) string {
out := make([]string, len(jobs)+1)
out[0] = "ID|Type|Priority|Status"
for i, job := range jobs {
out[i+1] = fmt.Sprintf("%s|%s|%d|%s",
job.ID,
job.Type,
job.Priority,
job.Status)
}
return formatList(out)
}

View File

@@ -4,6 +4,8 @@ import (
"strings"
"testing"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
)
@@ -12,7 +14,9 @@ func TestStatusCommand_Implements(t *testing.T) {
}
func TestStatusCommand_Run(t *testing.T) {
srv, client, url := testServer(t, nil)
srv, client, url := testServer(t, func(c *testutil.TestServerConfig) {
c.DevMode = true
})
defer srv.Stop()
ui := new(cli.MockUi)
@@ -33,14 +37,22 @@ func TestStatusCommand_Run(t *testing.T) {
// Register two jobs
job1 := testJob("job1_sfx")
evalId, _, err := client.Jobs().Register(job1, nil)
evalId1, _, err := client.Jobs().Register(job1, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if code := waitForSuccess(ui, client, fullId, t, evalId1); code != 0 {
t.Fatalf("status code non zero saw %d", code)
}
job2 := testJob("job2_sfx")
if _, _, err := client.Jobs().Register(job2, nil); err != nil {
evalId2, _, err := client.Jobs().Register(job2, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if code := waitForSuccess(ui, client, fullId, t, evalId2); code != 0 {
t.Fatalf("status code non zero saw %d", code)
}
// Query again and check the result
if code := cmd.Run([]string{"-address=" + url}); code != 0 {
@@ -98,6 +110,9 @@ func TestStatusCommand_Run(t *testing.T) {
if !strings.Contains(out, "Allocations") {
t.Fatalf("should dump allocations")
}
if !strings.Contains(out, "Created At") {
t.Fatal("should have created header")
}
ui.OutputWriter.Reset()
// Query jobs with prefix match
@@ -134,7 +149,7 @@ func TestStatusCommand_Run(t *testing.T) {
if strings.Contains(out, "Allocations") {
t.Fatalf("should not dump allocations")
}
if strings.Contains(out, evalId) {
if strings.Contains(out, evalId1) {
t.Fatalf("should not contain full identifiers, got %s", out)
}
ui.OutputWriter.Reset()
@@ -144,7 +159,7 @@ func TestStatusCommand_Run(t *testing.T) {
t.Fatalf("expected exit 0, got: %d", code)
}
out = ui.OutputWriter.String()
if !strings.Contains(out, evalId) {
if !strings.Contains(out, evalId1) {
t.Fatalf("should contain full identifiers, got %s", out)
}
}
@@ -170,3 +185,9 @@ func TestStatusCommand_Fails(t *testing.T) {
t.Fatalf("expected failed query error, got: %s", out)
}
}
func waitForSuccess(ui cli.Ui, client *api.Client, length int, t *testing.T, evalId string) int {
mon := newMonitor(ui, client, length)
monErr := mon.monitor(evalId, false)
return monErr
}

View File

@@ -39,7 +39,7 @@ func testServer(
}
func testJob(jobID string) *api.Job {
task := api.NewTask("task1", "exec").
task := api.NewTask("task1", "raw_exec").
SetConfig("command", "/bin/sleep").
Require(&api.Resources{
MemoryMB: 256,

View File

@@ -80,8 +80,8 @@ Task Group Queued Starting Running Failed Complete Lost
cache 0 0 1 0 0 0
Allocations
ID Eval ID Node ID Task Group Desired Status
24cfd201 81efc2fa 8d0331e9 cache run running
ID Eval ID Node ID Task Group Desired Status Created At
24cfd201 81efc2fa 8d0331e9 cache run running 08/08/16 21:03:19 CDT
```
Full status information of a job with placement failures:
@@ -106,12 +106,12 @@ Task Group "cache":
* Dimension "cpu exhausted" exhausted on 1 nodes
Allocations
ID Eval ID Node ID Task Group Desired Status
0b8b9e37 8bf94335 8d0331e9 cache run running
b206088c 8bf94335 8d0331e9 cache run running
b82f58b6 8bf94335 8d0331e9 cache run running
ed3665f5 8bf94335 8d0331e9 cache run running
24cfd201 8bf94335 8d0331e9 cache run running
ID Eval ID Node ID Task Group Desired Status Created At
0b8b9e37 8bf94335 8d0331e9 cache run running 08/08/16 21:03:19 CDT
b206088c 8bf94335 8d0331e9 cache run running 08/08/16 21:03:18 CDT
b82f58b6 8bf94335 8d0331e9 cache run running 08/08/16 21:03:17 CDT
ed3665f5 8bf94335 8d0331e9 cache run running 08/08/16 21:03:21 CDT
24cfd201 8bf94335 8d0331e9 cache run running 08/08/16 21:03:19 CDT
```
Full status information showing evaluations with a placement failure. The in
@@ -145,10 +145,10 @@ Task Group "cache":
* Dimension "cpu exhausted" exhausted on 1 nodes
Allocations
ID Eval ID Node ID Task Group Desired Status
0b8b9e37 8bf94335 8d0331e9 cache run running
b206088c 8bf94335 8d0331e9 cache run running
b82f58b6 8bf94335 8d0331e9 cache run running
ed3665f5 8bf94335 8d0331e9 cache run running
24cfd201 8bf94335 8d0331e9 cache run running
ID Eval ID Node ID Task Group Desired Status Created At
0b8b9e37 8bf94335 8d0331e9 cache run running 08/08/16 21:03:19 CDT
b206088c 8bf94335 8d0331e9 cache run running 08/08/16 21:03:19 CDT
b82f58b6 8bf94335 8d0331e9 cache run running 08/08/16 21:03:19 CDT
ed3665f5 8bf94335 8d0331e9 cache run running 08/08/16 21:03:19 CDT
24cfd201 8bf94335 8d0331e9 cache run running 08/08/16 21:03:19 CDT
```

View File

@@ -39,14 +39,14 @@ Task Group "cache":
* Dimension "cpu exhausted" exhausted on 1 nodes
Allocations
ID Eval ID Node ID Task Group Desired Status
12681940 8e38e6cf 4beef22f cache run running
395c5882 8e38e6cf 4beef22f cache run running
4d7c6f84 8e38e6cf 4beef22f cache run running
843b07b8 8e38e6cf 4beef22f cache run running
a8bc6d3e 8e38e6cf 4beef22f cache run running
b0beb907 8e38e6cf 4beef22f cache run running
da21c1fd 8e38e6cf 4beef22f cache run running
ID Eval ID Node ID Task Group Desired Status Created At
12681940 8e38e6cf 4beef22f cache run running 08/08/16 21:03:19 CDT
395c5882 8e38e6cf 4beef22f cache run running 08/08/16 21:03:19 CDT
4d7c6f84 8e38e6cf 4beef22f cache run running 08/08/16 21:03:19 CDT
843b07b8 8e38e6cf 4beef22f cache run running 08/08/16 21:03:19 CDT
a8bc6d3e 8e38e6cf 4beef22f cache run running 08/08/16 21:03:19 CDT
b0beb907 8e38e6cf 4beef22f cache run running 08/08/16 21:03:19 CDT
da21c1fd 8e38e6cf 4beef22f cache run running 08/08/16 21:03:19 CDT
```
In the above example we see that the job has a "blocked" evaluation that is in

View File

@@ -185,10 +185,10 @@ Status = running
Periodic = false
Allocations
ID Eval ID Node ID Task Group Desired Status
501154ac 8e0a7cf9 c887deef cache run running
7e2b3900 8e0a7cf9 fca62612 cache run running
9c66fcaf 8e0a7cf9 c887deef cache run running
ID Eval ID Node ID Task Group Desired Status Created At
501154ac 8e0a7cf9 c887deef cache run running 08/08/16 21:03:19 CDT
7e2b3900 8e0a7cf9 fca62612 cache run running 08/08/16 21:03:19 CDT
9c66fcaf 8e0a7cf9 c887deef cache run running 08/08/16 21:03:19 CDT
```
We can see that all our tasks have been allocated and are running.

View File

@@ -75,8 +75,8 @@ Task Group Queued Starting Running Failed Complete Lost
cache 0 0 1 0 0 0
Allocations
ID Eval ID Node ID Task Group Desired Status
dadcdb81 61b0b423 72687b1a cache run running
ID Eval ID Node ID Task Group Desired Status Created At
dadcdb81 61b0b423 72687b1a cache run running 06/23/16 01:41:13 UTC
```
Here we can see that the result of our evaluation was the creation of an