mirror of
https://github.com/kemko/nomad.git
synced 2026-01-04 01:15:43 +03:00
Merge pull request #1540 from novilabs/add-creation-time-to-job-status
Add creation time to job status
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user