`nomad eval list` command (#11675)

Use the new filtering and pagination capabilities of the `Eval.List`
RPC to provide filtering and pagination at the command line.

Also includes note that `nomad eval status -json` is deprecated and
will be replaced with a single evaluation view in a future version of
Nomad.
This commit is contained in:
Tim Gross 2021-12-15 11:58:38 -05:00 committed by GitHub
parent f8a133a810
commit 536e3c5282
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 389 additions and 3 deletions

3
.changelog/11675.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
cli: Added a `nomad eval list` command.
```

View File

@ -255,6 +255,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
Meta: meta, Meta: meta,
}, nil }, nil
}, },
"eval list": func() (cli.Command, error) {
return &EvalListCommand{
Meta: meta,
}, nil
},
"eval status": func() (cli.Command, error) { "eval status": func() (cli.Command, error) {
return &EvalStatusCommand{ return &EvalStatusCommand{
Meta: meta, Meta: meta,

213
command/eval_list.go Normal file
View File

@ -0,0 +1,213 @@
package command
import (
"fmt"
"os"
"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 List 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 "List the set of evaluations processed by Nomad"
}
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
}
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 list" }
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
}
// Check that we got no arguments
args = flags.Args()
if l := len(args); l != 0 {
c.Ui.Error("This command takes no arguments")
c.Ui.Error(commandErrorText(c))
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:
%s -page-token %s`, argsWithoutPageToken(os.Args), qm.NextToken))
}
return 0
}
// argsWithoutPageToken strips out of the -page-token argument and
// returns the joined string
func argsWithoutPageToken(osArgs []string) string {
args := []string{}
i := 0
for {
if i >= len(osArgs) {
break
}
arg := osArgs[i]
if strings.HasPrefix(arg, "-page-token") {
if strings.Contains(arg, "=") {
i += 1
} else {
i += 2
}
continue
}
args = append(args, arg)
i++
}
return strings.Join(args, " ")
}

60
command/eval_list_test.go Normal file
View File

@ -0,0 +1,60 @@
package command
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestEvalList_ArgsWithoutPageToken(t *testing.T) {
cases := []struct {
cli string
expected string
}{
{
cli: "nomad eval list -page-token=abcdef",
expected: "nomad eval list",
},
{
cli: "nomad eval list -page-token abcdef",
expected: "nomad eval list",
},
{
cli: "nomad eval list -per-page 3 -page-token abcdef",
expected: "nomad eval list -per-page 3",
},
{
cli: "nomad eval list -page-token abcdef -per-page 3",
expected: "nomad eval list -per-page 3",
},
{
cli: "nomad eval list -per-page=3 -page-token abcdef",
expected: "nomad eval list -per-page=3",
},
{
cli: "nomad eval list -verbose -page-token abcdef",
expected: "nomad eval list -verbose",
},
{
cli: "nomad eval list -page-token abcdef -verbose",
expected: "nomad eval list -verbose",
},
{
cli: "nomad eval list -verbose -page-token abcdef -per-page 3",
expected: "nomad eval list -verbose -per-page 3",
},
{
cli: "nomad eval list -page-token abcdef -verbose -per-page 3",
expected: "nomad eval list -verbose -per-page 3",
},
}
for _, tc := range cases {
args := strings.Split(tc.cli, " ")
assert.Equal(t, tc.expected, argsWithoutPageToken(args),
"for input: %s", tc.cli)
}
}

View File

@ -0,0 +1,23 @@
---
layout: docs
page_title: 'Commands: eval'
description: |
The eval command is used to interact with evals.
---
# Command: eval
The `eval` command is used to interact with evals.
## Usage
Usage: `nomad eval <subcommand> [options]`
Run `nomad eval <subcommand> -h` for help on that subcommand. The following
subcommands are available:
- [`eval list`][list] - List all evals
- [`eval status`][status] - Display the status of a eval
[list]: /docs/commands/eval/list 'List all evals'
[status]: /docs/commands/eval/status 'Display the status of a eval'

View File

@ -0,0 +1,51 @@
---
layout: docs
page_title: 'Commands: eval list'
description: |
The eval list command is used to list evaluations.
---
# Command: eval list
The `eval list` command is used list all evaluations.
## Usage
```plaintext
nomad eval list [options]
```
The `eval list` command requires no arguments.
When ACLs are enabled, this command requires a token with the `read-job`
capability for the requested namespace.
## General Options
@include 'general_options.mdx'
## List 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.
## Examples
List all tracked evaluations:
```shell-session
$ nomad eval list -per-page 3 -status complete
ID Priority Triggered By Job ID Status Placement Failures
456e37aa 50 deployment-watcher example complete false
1a1eafe6 50 alloc-stop example complete false
3411e37b 50 job-register example complete false
Results have been paginated. To get the next page run:
nomad eval list -page-token 9ecffbba-73be-d909-5d7e-ac2694c10e0c
```

View File

@ -45,7 +45,10 @@ indicated by exit code 1.
- `-monitor`: Monitor an outstanding evaluation - `-monitor`: Monitor an outstanding evaluation
- `-verbose`: Show full information. - `-verbose`: Show full information.
- `-json` : Output the evaluation in its JSON format. - `-json` : Output a list of all evaluations in JSON format. This
behavior is deprecated and has been replaced by `nomad eval list
-json`. In Nomad 1.4.0 the behavior of this option will change to
output only the selected evaluation in JSON.
- `-t` : Format and display evaluation using a Go template. - `-t` : Format and display evaluation using a Go template.
## Examples ## Examples

View File

@ -13,6 +13,16 @@ upgrade. However, specific versions of Nomad may have more details provided for
their upgrades as a result of new features or changed behavior. This page is their upgrades as a result of new features or changed behavior. This page is
used to document those details separately from the standard upgrade flow. used to document those details separately from the standard upgrade flow.
## Nomad 1.2.4
#### `nomad eval status -json` deprecated
Nomad 1.2.4 includes a new `nomad eval list` command that has the
option to display the results in JSON format with the `-json`
flag. This replaces the existing `nomad eval status -json` option. In
Nomad 1.4.0, `nomad eval status -json` will be changed to display only
the selected evaluation in JSON format.
## Nomad 1.2.2 ## Nomad 1.2.2
### Panic on node class filtering for system and sysbatch jobs fixed ### Panic on node class filtering for system and sysbatch jobs fixed

View File

@ -353,8 +353,21 @@
] ]
}, },
{ {
"title": "eval status", "title": "eval",
"path": "commands/eval-status" "routes": [
{
"title": "Overview",
"path": "commands/eval"
},
{
"title": "list",
"path": "commands/eval/list"
},
{
"title": "status",
"path": "commands/eval/status"
}
]
}, },
{ {
"title": "job", "title": "job",

View File

@ -589,6 +589,11 @@ module.exports = [
destination: '/docs/commands/alloc/status', destination: '/docs/commands/alloc/status',
permanent: true, permanent: true,
}, },
{
source: '/docs/commands/eval-status',
destination: '/docs/commands/eval/status',
permanent: true,
},
{ {
source: '/docs/commands/fs', source: '/docs/commands/fs',
destination: '/docs/commands/alloc/fs', destination: '/docs/commands/alloc/fs',