From 1806c7ce222777ce5ec739098a111d2d77ec2d2d Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Nov 2015 18:09:00 -0800 Subject: [PATCH 1/5] Display task information --- command/alloc_status.go | 127 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 7 deletions(-) diff --git a/command/alloc_status.go b/command/alloc_status.go index 2e30638503d2..74c0b138bdae 100644 --- a/command/alloc_status.go +++ b/command/alloc_status.go @@ -2,7 +2,11 @@ package command import ( "fmt" + "sort" "strings" + "time" + + "github.com/hashicorp/nomad/api" ) type AllocStatusCommand struct { @@ -13,14 +17,21 @@ func (c *AllocStatusCommand) Help() string { helpText := ` Usage: nomad alloc-status [options] - Display information about existing allocations. This command can - be used to inspect the current status of all allocation, - including its running status, metadata, and verbose failure - messages reported by internal subsystems. + Display information about existing allocations and its tasks. This command can + be used to inspect the current status of all allocation, including its running + status, metadata, and verbose failure messages reported by internal + subsystems. General Options: - ` + generalOptionsUsage() + ` + generalOptionsUsage() + ` + +Alloc Status Options: + + -short + Display short output, showing only the most recent task event. +` + return strings.TrimSpace(helpText) } @@ -29,15 +40,19 @@ func (c *AllocStatusCommand) Synopsis() string { } func (c *AllocStatusCommand) Run(args []string) int { + var short bool + flags := c.Meta.FlagSet("alloc-status", FlagSetClient) flags.Usage = func() { c.Ui.Output(c.Help()) } + flags.BoolVar(&short, "short", false, "") + if err := flags.Parse(args); err != nil { return 1 } // Check that we got exactly one allocation ID args = flags.Args() - if len(args) != 1 { + if len(args) == 0 || len(args) > 2 { c.Ui.Error(c.Help()) return 1 } @@ -65,7 +80,6 @@ func (c *AllocStatusCommand) Run(args []string) int { fmt.Sprintf("NodeID|%s", alloc.NodeID), fmt.Sprintf("JobID|%s", alloc.JobID), fmt.Sprintf("ClientStatus|%s", alloc.ClientStatus), - fmt.Sprintf("ClientDescription|%s", alloc.ClientDescription), fmt.Sprintf("NodesEvaluated|%d", alloc.Metrics.NodesEvaluated), fmt.Sprintf("NodesFiltered|%d", alloc.Metrics.NodesFiltered), fmt.Sprintf("NodesExhausted|%d", alloc.Metrics.NodesExhausted), @@ -74,9 +88,108 @@ func (c *AllocStatusCommand) Run(args []string) int { } c.Ui.Output(formatKV(basic)) + // Print the state of each task. + if short { + c.shortTaskStatus(alloc) + } else { + c.taskStatus(alloc) + } + // Format the detailed status c.Ui.Output("\n==> Status") dumpAllocStatus(c.Ui, alloc) return 0 } + +// shortTaskStatus prints out the current state of each task. +func (c *AllocStatusCommand) shortTaskStatus(alloc *api.Allocation) { + tasks := make([]string, 0, len(alloc.TaskStates)+1) + tasks = append(tasks, "Name|State|LastEvent|Time") + for task := range c.sortedTaskStateIterator(alloc.TaskStates) { + fmt.Println(task) + state := alloc.TaskStates[task] + lastState := state.State + var lastEvent, lastTime string + + l := len(state.Events) + if l != 0 { + last := state.Events[l-1] + lastEvent = last.Type + lastTime = c.formatUnixNonoTime(last.Time) + } + + tasks = append(tasks, fmt.Sprintf("%s|%s|%s|%s", + task, lastState, lastEvent, lastTime)) + } + + c.Ui.Output("\n==> Tasks") + c.Ui.Output(formatList(tasks)) +} + +// taskStatus prints out the most recent events for each task. +func (c *AllocStatusCommand) taskStatus(alloc *api.Allocation) { + for task := range c.sortedTaskStateIterator(alloc.TaskStates) { + state := alloc.TaskStates[task] + events := make([]string, len(state.Events)+1) + events[0] = "Time|Type|Description" + + size := len(state.Events) + for i, event := range state.Events { + formatedTime := c.formatUnixNonoTime(event.Time) + + // Build up the description based on the event type. + var desc string + switch event.Type { + case api.TaskDriverFailure: + desc = event.DriverError + case api.TaskKilled: + desc = event.KillError + case api.TaskTerminated: + var parts []string + parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode)) + + if event.Signal != 0 { + parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal)) + } + + if event.Message != "" { + parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message)) + } + desc = strings.Join(parts, ", ") + } + + // Reverse order so we are sorted by time + events[size-i] = fmt.Sprintf("%s|%s|%s", formatedTime, event.Type, desc) + } + + c.Ui.Output(fmt.Sprintf("\n==> Task %q is %q\nRecent Events:", task, state.State)) + c.Ui.Output(formatList(events)) + } +} + +// formatUnixNonoTime is a helper for formating time for output. +func (c *AllocStatusCommand) formatUnixNonoTime(nano int64) string { + t := time.Unix(0, nano) + return t.Format("15:04:05 01/02/06") +} + +// sortedTaskStateIterator is a helper that takes the task state map and returns a +// channel that returns the keys in a sorted order. +func (c *AllocStatusCommand) sortedTaskStateIterator(m map[string]*api.TaskState) <-chan string { + output := make(chan string, len(m)) + keys := make([]string, len(m)) + i := 0 + for k, _ := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + + for _, key := range keys { + output <- key + } + + close(output) + return output +} From d53aa72251085e0a24f173ce35509ef6035313c6 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Nov 2015 18:21:51 -0800 Subject: [PATCH 2/5] Get rid of incorrect length check --- command/alloc_status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/alloc_status.go b/command/alloc_status.go index 74c0b138bdae..63288a89db19 100644 --- a/command/alloc_status.go +++ b/command/alloc_status.go @@ -52,7 +52,7 @@ func (c *AllocStatusCommand) Run(args []string) int { // Check that we got exactly one allocation ID args = flags.Args() - if len(args) == 0 || len(args) > 2 { + if len(args) == 0 { c.Ui.Error(c.Help()) return 1 } From 5f34ae9ae8fa53359e8a8488b91b4aca0866ee10 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Nov 2015 18:36:13 -0800 Subject: [PATCH 3/5] Add docs --- command/alloc_status.go | 2 +- .../docs/commands/alloc-status.html.md.erb | 75 +++++++++++++++---- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/command/alloc_status.go b/command/alloc_status.go index 63288a89db19..a1bea3ab7585 100644 --- a/command/alloc_status.go +++ b/command/alloc_status.go @@ -29,7 +29,7 @@ General Options: Alloc Status Options: -short - Display short output, showing only the most recent task event. + Display short output. Shows only the most recent task event. ` return strings.TrimSpace(helpText) diff --git a/website/source/docs/commands/alloc-status.html.md.erb b/website/source/docs/commands/alloc-status.html.md.erb index 86a6799c025f..f84a217d0e20 100644 --- a/website/source/docs/commands/alloc-status.html.md.erb +++ b/website/source/docs/commands/alloc-status.html.md.erb @@ -3,14 +3,15 @@ layout: "docs" page_title: "Commands: alloc-status" sidebar_current: "docs-commands-alloc-status" description: > - Display status and metadata about existing allocations + Display status and metadata about existing allocations and their tasks. --- # Command: alloc-status -The `alloc-status` command displays status information and metadata about -an existing allocation. It can be useful while debugging to reveal the -underlying reasons for scheduling decisions or failures. +The `alloc-status` command displays status information and metadata about an +existing allocation and its tasks. It can be useful while debugging to reveal +the underlying reasons for scheduling decisions or failures, as well as the +current state of its tasks. ## Usage @@ -24,25 +25,71 @@ and detailed information for it will be dumped. ## General Options <%= general_options_usage %> +# +## Status Options + +* `-short`: Display short output. Shows only the most recent task event. ## Examples +Short status of an alloc: + ``` -nomad alloc-status 9f3276d6-c873-c0a3-81ae-247e8c665cbe -ID = 9f3276d6-c873-c0a3-81ae-247e8c665cbe -EvalID = dc186cc2-a9b2-218e-cc00-eea3d4eaccf4 +$ nomad alloc-status --short a7365fe4-8b9f-4284-612d-a101fb41e773 +ID = a7365fe4-8b9f-4284-612d-a101fb41e773 +EvalID = 44c2d9ed-6377-ca3d-14a8-b2e6327230ce Name = example.cache[0] -NodeID = +NodeID = e55859b1-4330-f00b-da49-8a292432ead3 JobID = example -ClientStatus = failed -ClientDescription = +ClientStatus = running NodesEvaluated = 1 -NodesFiltered = 1 +NodesFiltered = 0 NodesExhausted = 0 -AllocationTime = 15.242µs +AllocationTime = 911.026µs CoalescedFailures = 0 +redis +web + +==> Tasks +Name State LastEvent Time +redis running Started 02:29:40 11/17/15 +web running Started 02:30:41 11/17/15 ==> Status -Allocation "9f3276d6-c873-c0a3-81ae-247e8c665cbe" status "failed" (1/1 nodes filtered) - * Constraint "$attr.kernel.name = linux" filtered 1 nodes +Allocation "a7365fe4-8b9f-4284-612d-a101fb41e773" status "running" (0/1 nodes filtered) + * Score "e55859b1-4330-f00b-da49-8a292432ead3.binpack" = 10.334026 +``` + +Full status of an alloc, which shows one of the tasks dying and then being restarted: + +``` +$ nomad alloc-status a7365fe4-8b9f-4284-612d-a101fb41e773 +ID = a7365fe4-8b9f-4284-612d-a101fb41e773 +EvalID = 44c2d9ed-6377-ca3d-14a8-b2e6327230ce +Name = example.cache[0] +NodeID = e55859b1-4330-f00b-da49-8a292432ead3 +JobID = example +ClientStatus = running +NodesEvaluated = 1 +NodesFiltered = 0 +NodesExhausted = 0 +AllocationTime = 911.026µs +CoalescedFailures = 0 + +==> Task "redis" is "running" +Recent Events: +Time Type Description +02:29:40 11/17/15 Started + +==> Task "web" is "running" +Recent Events: +Time Type Description +02:30:41 11/17/15 Started +02:30:02 11/17/15 Terminated Exit Code: 137, Exit Message: "Docker container exited with non-zero exit code: 137" +02:29:40 11/17/15 Started + +==> Status +Allocation "a7365fe4-8b9f-4284-612d-a101fb41e773" status "running" (0/1 nodes filtered) + * Score "e55859b1-4330-f00b-da49-8a292432ead3.binpack" = 10.334026 + ``` From 08d8ae3541123a64c5224b1d1208badd1df564ef Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Nov 2015 18:37:38 -0800 Subject: [PATCH 4/5] Review fixes --- command/alloc_status.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/command/alloc_status.go b/command/alloc_status.go index a1bea3ab7585..a3618c69f3c9 100644 --- a/command/alloc_status.go +++ b/command/alloc_status.go @@ -116,7 +116,7 @@ func (c *AllocStatusCommand) shortTaskStatus(alloc *api.Allocation) { if l != 0 { last := state.Events[l-1] lastEvent = last.Type - lastTime = c.formatUnixNonoTime(last.Time) + lastTime = c.formatUnixNanoTime(last.Time) } tasks = append(tasks, fmt.Sprintf("%s|%s|%s|%s", @@ -136,7 +136,7 @@ func (c *AllocStatusCommand) taskStatus(alloc *api.Allocation) { size := len(state.Events) for i, event := range state.Events { - formatedTime := c.formatUnixNonoTime(event.Time) + formatedTime := c.formatUnixNanoTime(event.Time) // Build up the description based on the event type. var desc string @@ -168,8 +168,8 @@ func (c *AllocStatusCommand) taskStatus(alloc *api.Allocation) { } } -// formatUnixNonoTime is a helper for formating time for output. -func (c *AllocStatusCommand) formatUnixNonoTime(nano int64) string { +// formatUnixNanoTime is a helper for formating time for output. +func (c *AllocStatusCommand) formatUnixNanoTime(nano int64) string { t := time.Unix(0, nano) return t.Format("15:04:05 01/02/06") } @@ -180,7 +180,7 @@ func (c *AllocStatusCommand) sortedTaskStateIterator(m map[string]*api.TaskState output := make(chan string, len(m)) keys := make([]string, len(m)) i := 0 - for k, _ := range m { + for k := range m { keys[i] = k i++ } From 57cc352610fc4354feb789552d40777f966d714a Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Nov 2015 18:44:37 -0800 Subject: [PATCH 5/5] fix arg check --- command/alloc_status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/alloc_status.go b/command/alloc_status.go index a3618c69f3c9..07467597650c 100644 --- a/command/alloc_status.go +++ b/command/alloc_status.go @@ -52,7 +52,7 @@ func (c *AllocStatusCommand) Run(args []string) int { // Check that we got exactly one allocation ID args = flags.Args() - if len(args) == 0 { + if len(args) != 1 { c.Ui.Error(c.Help()) return 1 }