Skip to content

Commit

Permalink
Merge pull request #1206 from hashicorp/f-eval-status
Browse files Browse the repository at this point in the history
Introduce eval-status and deprecate eval-monitor
  • Loading branch information
dadgar committed May 31, 2016
2 parents 9496242 + f5398d2 commit aaac26f
Show file tree
Hide file tree
Showing 19 changed files with 455 additions and 196 deletions.
1 change: 1 addition & 0 deletions command/alloc_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ General Options:
` + generalOptionsUsage() + `
Alloc Status Options:
-short
Display short output. Shows only the most recent task event.
Expand Down
81 changes: 0 additions & 81 deletions command/eval_monitor.go

This file was deleted.

198 changes: 198 additions & 0 deletions command/eval_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package command

import (
"fmt"
"sort"
"strings"

"github.com/hashicorp/nomad/api"
)

type EvalStatusCommand struct {
Meta
}

func (c *EvalStatusCommand) Help() string {
helpText := `
Usage: nomad eval-status [options] <evaluation-id>
Display information about evaluations. This command can be used to inspect the
current status of an evaluation as well as determine the reason an evaluation
did not place all allocations.
General Options:
` + generalOptionsUsage() + `
Eval Status Options:
-monitor
Monitor an outstanding evaluation
-verbose
Show full information.
`

return strings.TrimSpace(helpText)
}

func (c *EvalStatusCommand) Synopsis() string {
return "Display evaluation status and placement failure reasons"
}

func (c *EvalStatusCommand) Run(args []string) int {
var monitor, verbose bool

flags := c.Meta.FlagSet("eval-status", FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&monitor, "monitor", false, "")
flags.BoolVar(&verbose, "verbose", false, "")

if err := flags.Parse(args); err != nil {
return 1
}

// Check that we got exactly one evaluation ID
args = flags.Args()
if len(args) != 1 {
c.Ui.Error(c.Help())
return 1
}
evalID := args[0]

// Get the HTTP client
client, err := c.Meta.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
return 1
}

// Truncate the id unless full length is requested
length := shortId
if verbose {
length = fullId
}

// Query the allocation info
if len(evalID) == 1 {
c.Ui.Error(fmt.Sprintf("Identifier must contain at least two characters."))
return 1
}
if len(evalID)%2 == 1 {
// Identifiers must be of even length, so we strip off the last byte
// to provide a consistent user experience.
evalID = evalID[:len(evalID)-1]
}

evals, _, err := client.Evaluations().PrefixList(evalID)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error querying evaluation: %v", err))
return 1
}
if len(evals) == 0 {
c.Ui.Error(fmt.Sprintf("No evaluation(s) with prefix or id %q found", evalID))
return 1
}
if len(evals) > 1 {
// Format the evals
out := make([]string, len(evals)+1)
out[0] = "ID|Priority|Triggered By|Status|Placement Failures"
for i, eval := range evals {
out[i+1] = fmt.Sprintf("%s|%d|%s|%s|%t",
limit(eval.ID, length),
eval.Priority,
eval.TriggeredBy,
eval.Status,
len(eval.FailedTGAllocs) != 0,
)
}
c.Ui.Output(fmt.Sprintf("Prefix matched multiple evaluations\n\n%s", formatList(out)))
return 0
}

// If we are in monitor mode, monitor and exit
if monitor {
mon := newMonitor(c.Ui, client, length)
return mon.monitor(evals[0].ID, true)
}

// Prefix lookup matched a single evaluation
eval, _, err := client.Evaluations().Info(evals[0].ID, nil)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error querying evaluation: %s", err))
return 1
}

failures := len(eval.FailedTGAllocs) != 0
triggerNoun, triggerSubj := getTriggerDetails(eval)
statusDesc := eval.StatusDescription
if statusDesc == "" {
statusDesc = eval.Status
}

// Format the evaluation data
basic := []string{
fmt.Sprintf("ID|%s", limit(eval.ID, length)),
fmt.Sprintf("Status|%s", eval.Status),
fmt.Sprintf("Status Description|%s", statusDesc),
fmt.Sprintf("Type|%s", eval.Type),
fmt.Sprintf("TriggeredBy|%s", eval.TriggeredBy),
fmt.Sprintf("%s|%s", triggerNoun, triggerSubj),
fmt.Sprintf("Priority|%d", eval.Priority),
fmt.Sprintf("Placement Failures|%t", failures),
}

if verbose {
// NextEval, PreviousEval, BlockedEval
basic = append(basic,
fmt.Sprintf("Previous Eval|%s", eval.PreviousEval),
fmt.Sprintf("Next Eval|%s", eval.NextEval),
fmt.Sprintf("Blocked Eval|%s", eval.BlockedEval))
}
c.Ui.Output(formatKV(basic))

if failures {
c.Ui.Output("\n==> Failed Placements")
sorted := sortedTaskGroupFromMetrics(eval.FailedTGAllocs)
for _, tg := range sorted {
metrics := eval.FailedTGAllocs[tg]

noun := "allocation"
if metrics.CoalescedFailures > 0 {
noun += "s"
}
c.Ui.Output(fmt.Sprintf("Task Group %q (failed to place %d %s):", tg, metrics.CoalescedFailures+1, noun))
dumpAllocMetrics(c.Ui, metrics, false)
c.Ui.Output("")
}

if eval.BlockedEval != "" {
c.Ui.Output(fmt.Sprintf("Evaluation %q waiting for additional capacity to place remainder",
limit(eval.BlockedEval, length)))
}
}

return 0
}

func sortedTaskGroupFromMetrics(groups map[string]*api.AllocationMetric) []string {
tgs := make([]string, 0, len(groups))
for tg, _ := range groups {
tgs = append(tgs, tg)
}
sort.Strings(tgs)
return tgs
}

func getTriggerDetails(eval *api.Evaluation) (noun, subject string) {
switch eval.TriggeredBy {
case "job-register", "job-deregister", "periodic-job", "rolling-update":
return "Job ID", eval.JobID
case "node-update":
return "Node ID", eval.NodeID
case "max-plan-attempts":
return "Previous Eval", eval.PreviousEval
default:
return "", ""
}
}
10 changes: 5 additions & 5 deletions command/eval_monitor_test.go → command/eval_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import (
"github.com/mitchellh/cli"
)

func TestEvalMonitorCommand_Implements(t *testing.T) {
var _ cli.Command = &EvalMonitorCommand{}
func TestEvalStatusCommand_Implements(t *testing.T) {
var _ cli.Command = &EvalStatusCommand{}
}

func TestEvalMonitorCommand_Fails(t *testing.T) {
func TestEvalStatusCommand_Fails(t *testing.T) {
srv, _, url := testServer(t, nil)
defer srv.Stop()

ui := new(cli.MockUi)
cmd := &EvalMonitorCommand{Meta: Meta{Ui: ui}}
cmd := &EvalStatusCommand{Meta: Meta{Ui: ui}}

// Fails on misuse
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
Expand All @@ -40,7 +40,7 @@ func TestEvalMonitorCommand_Fails(t *testing.T) {
if code := cmd.Run([]string{"-address=nope", "12345678-abcd-efab-cdef-123456789abc"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error reading evaluation") {
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying evaluation") {
t.Fatalf("expected failed query error, got: %s", out)
}
}
2 changes: 2 additions & 0 deletions command/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ General Options:
` + generalOptionsUsage() + `
FS Specific Options:
-H
Machine friendly output.
Expand Down
2 changes: 1 addition & 1 deletion command/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ General Options:
` + generalOptionsUsage() + `
Run Options:
Plan Options:
-diff
Defaults to true, but can be toggled off to omit diff output.
Expand Down
7 changes: 3 additions & 4 deletions command/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@ General Options:
Run Options:
-detach
Return immediately instead of entering monitor mode. After job
submission, the evaluation ID will be printed to the screen.
You can use this ID to start a monitor using the eval-monitor
command later if needed.
Return immediately instead of entering monitor mode. After job submission,
the evaluation ID will be printed to the screen, which can be used to
examine the evaluation using the eval-status command.
-verbose
Display full information.
Expand Down
2 changes: 1 addition & 1 deletion command/server_members.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ General Options:
` + generalOptionsUsage() + `
Agent Members Options:
Server Members Options:
-detailed
Show detailed information about each member. This dumps
Expand Down
Loading

0 comments on commit aaac26f

Please sign in to comment.