From 4011e80d83fabbbfce38cdff7247e5bd12b2ca47 Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Fri, 10 Dec 2021 13:45:29 -0500 Subject: [PATCH] cli: nomad eval list command Use the new filtering and pagination capabilities of the `Eval.List` RPC to provide filtering and pagination at the command line. --- command/commands.go | 5 ++ command/eval_list.go | 182 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 command/eval_list.go diff --git a/command/commands.go b/command/commands.go index 87f46b8f4ad..9b58cdd8f91 100644 --- a/command/commands.go +++ b/command/commands.go @@ -255,6 +255,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory { Meta: meta, }, nil }, + "eval list": func() (cli.Command, error) { + return &EvalListCommand{ + Meta: meta, + }, nil + }, "eval status": func() (cli.Command, error) { return &EvalStatusCommand{ Meta: meta, diff --git a/command/eval_list.go b/command/eval_list.go new file mode 100644 index 00000000000..c9776b5a73f --- /dev/null +++ b/command/eval_list.go @@ -0,0 +1,182 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/api/contexts" + "github.com/posener/complete" +) + +type EvalListCommand struct { + Meta +} + +func (c *EvalListCommand) Help() string { + helpText := ` +Usage: nomad eval list [options] + + List is used to list the set of evaluations processed by Nomad. + +General Options: + + ` + generalOptionsUsage(usageOptsDefault) + ` + +Eval Status Options: + + -verbose + Show full information. + + -per_page + How many results to show per page. + + -page_token + Where to start pagination. + + -job + Only show evaluations for this job ID. + + -status + Only show evaluations with this status. + + -json + Output the evaluation in its JSON format. + + -t + Format and display evaluation using a Go template. +` + + return strings.TrimSpace(helpText) +} + +func (c *EvalListCommand) Synopsis() string { + return "Display evaluation status and placement failure reasons" +} + +func (c *EvalListCommand) AutocompleteFlags() complete.Flags { + return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), + complete.Flags{ + "-json": complete.PredictNothing, + "-t": complete.PredictAnything, + "-verbose": complete.PredictNothing, + "-job": complete.PredictAnything, + "-status": complete.PredictAnything, + "-per_page": complete.PredictAnything, + "-page_token": complete.PredictAnything, + }) +} + +func (c *EvalListCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictFunc(func(a complete.Args) []string { + client, err := c.Meta.Client() + if err != nil { + return nil + } + + if err != nil { + return nil + } + + resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Evals, nil) + if err != nil { + return []string{} + } + return resp.Matches[contexts.Evals] + }) +} + +func (c *EvalListCommand) Name() string { return "eval status" } + +func (c *EvalListCommand) Run(args []string) int { + var monitor, verbose, json bool + var perPage int + var tmpl, pageToken, filterJobID, filterStatus string + + flags := c.Meta.FlagSet(c.Name(), FlagSetClient) + flags.Usage = func() { c.Ui.Output(c.Help()) } + flags.BoolVar(&monitor, "monitor", false, "") + flags.BoolVar(&verbose, "verbose", false, "") + flags.BoolVar(&json, "json", false, "") + flags.StringVar(&tmpl, "t", "", "") + flags.IntVar(&perPage, "per_page", 0, "") + flags.StringVar(&pageToken, "page_token", "", "") + flags.StringVar(&filterJobID, "job", "", "") + flags.StringVar(&filterStatus, "status", "", "") + + if err := flags.Parse(args); err != nil { + return 1 + } + + client, err := c.Meta.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + return 1 + } + + opts := &api.QueryOptions{ + PerPage: int32(perPage), + NextToken: pageToken, + Params: map[string]string{}, + } + if filterJobID != "" { + opts.Params["job"] = filterJobID + } + if filterStatus != "" { + opts.Params["status"] = filterStatus + } + + evals, qm, err := client.Evaluations().List(opts) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error querying evaluations: %v", err)) + return 1 + } + + // If args not specified but output format is specified, format + // and output the evaluations data list + if json || len(tmpl) > 0 { + out, err := Format(json, tmpl, evals) + if err != nil { + c.Ui.Error(err.Error()) + return 1 + } + + c.Ui.Output(out) + return 0 + } + + if len(evals) == 0 { + c.Ui.Output("No evals found") + return 0 + } + + // Truncate the id unless full length is requested + length := shortId + if verbose { + length = fullId + } + + out := make([]string, len(evals)+1) + out[0] = "ID|Priority|Triggered By|Job ID|Status|Placement Failures" + for i, eval := range evals { + failures, _ := evalFailureStatus(eval) + out[i+1] = fmt.Sprintf("%s|%d|%s|%s|%s|%s", + limit(eval.ID, length), + eval.Priority, + eval.TriggeredBy, + eval.JobID, + eval.Status, + failures, + ) + } + c.Ui.Output(formatList(out)) + + if qm.NextToken != "" { + c.Ui.Output(fmt.Sprintf(` +Results have been paginated. To get the next page run: + +nomad eval list -page_token %s`, qm.NextToken)) + } + + return 0 +}