diff --git a/.changelog/16405.txt b/.changelog/16405.txt new file mode 100644 index 000000000..4cfad3085 --- /dev/null +++ b/.changelog/16405.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: Added `-json` and `-t` flag to `alloc checks` command +``` diff --git a/command/alloc_checks.go b/command/alloc_checks.go index ce0206056..f5084f829 100644 --- a/command/alloc_checks.go +++ b/command/alloc_checks.go @@ -30,6 +30,12 @@ Checks Specific Options: -verbose Show full information. + + -json + Output the latest health check status information in a JSON format. + + -t + Format and display latest health check status information using a Go template. ` return strings.TrimSpace(helpText) } @@ -42,6 +48,8 @@ func (c *AllocChecksCommand) AutocompleteFlags() complete.Flags { return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), complete.Flags{ "-verbose": complete.PredictNothing, + "-json": complete.PredictNothing, + "-t": complete.PredictAnything, }) } @@ -64,17 +72,21 @@ func (c *AllocChecksCommand) Name() string { } func (c *AllocChecksCommand) Run(args []string) int { - var verbose bool + var json, verbose bool + var tmpl string flags := c.Meta.FlagSet(c.Name(), FlagSetClient) flags.Usage = func() { c.Ui.Output(c.Help()) } flags.BoolVar(&verbose, "verbose", false, "") + flags.BoolVar(&json, "json", false, "") + flags.StringVar(&tmpl, "t", "", "") if err := flags.Parse(args); err != nil { return 1 } - args = flags.Args() + // Check that we got only one argument + args = flags.Args() if numArgs := len(args); numArgs < 1 { c.Ui.Error("An allocation ID is required") c.Ui.Error(commandErrorText(c)) @@ -128,6 +140,17 @@ func (c *AllocChecksCommand) Run(args []string) int { return 1 } + if json || len(tmpl) > 0 { + out, err := Format(json, tmpl, checks) + if err != nil { + c.Ui.Error(err.Error()) + return 1 + } + + c.Ui.Output(out) + return 0 + } + c.Ui.Output(fmt.Sprintf("Status of %d Nomad Service Checks", len(checks))) c.Ui.Output("") @@ -138,6 +161,7 @@ func (c *AllocChecksCommand) Run(args []string) int { } return s } + for _, check := range checks { list := []string{ pair("ID", check.ID), @@ -155,6 +179,7 @@ func (c *AllocChecksCommand) Run(args []string) int { pair("Timestamp", formatTaskTimes(time.Unix(check.Timestamp, 0))), pair("Output", check.Output), ) + c.Ui.Output(formatList(list)) c.Ui.Output("") } diff --git a/command/alloc_checks_test.go b/command/alloc_checks_test.go index 84c518653..fe133e9fe 100644 --- a/command/alloc_checks_test.go +++ b/command/alloc_checks_test.go @@ -1,8 +1,10 @@ package command import ( + "encoding/json" "testing" + "github.com/hashicorp/nomad/api" "github.com/hashicorp/nomad/ci" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" @@ -83,6 +85,7 @@ func TestAllocChecksCommand_AutocompleteArgs(t *testing.T) { func TestAllocChecksCommand_Run(t *testing.T) { ci.Parallel(t) srv, client, url := testServer(t, true, nil) + defer srv.Shutdown() // wait for nodes @@ -117,4 +120,26 @@ func TestAllocChecksCommand_Run(t *testing.T) { must.StrContains(t, out, `Task = (group)`) must.StrContains(t, out, `Service = service1`) must.StrContains(t, out, `Mode = healthiness`) + + ui.OutputWriter.Reset() + + // List json + cmd = &AllocChecksCommand{Meta: Meta{Ui: ui, flagAddress: url}} + must.Zero(t, cmd.Run([]string{"-address=" + url, "-json", allocID})) + + outJson := api.AllocCheckStatuses{} + err = json.Unmarshal(ui.OutputWriter.Bytes(), &outJson) + must.NoError(t, err) + + ui.OutputWriter.Reset() + + // Go template to format the output + code = cmd.Run([]string{"-address=" + url, "-t", "{{range .}}{{ .Status }}{{end}}", allocID}) + must.Zero(t, code) + + out = ui.OutputWriter.String() + must.StrContains(t, out, "failure") + + ui.OutputWriter.Reset() + ui.ErrorWriter.Reset() } diff --git a/website/content/docs/commands/alloc/checks.mdx b/website/content/docs/commands/alloc/checks.mdx new file mode 100644 index 000000000..fc1b02c1e --- /dev/null +++ b/website/content/docs/commands/alloc/checks.mdx @@ -0,0 +1,86 @@ +--- +layout: docs +page_title: 'Commands: alloc checks' +description: | + Outputs service health check status information. +--- + +# Command: alloc checks + + +The `alloc checks` command outputs the latest health check status information for checks in the allocation. + +## Usage + +```plaintext +nomad alloc checks [options] +``` + +Outputs the latest health check status information for services in the allocation +using the Nomad service discovery provider. This command accepts an allocation +ID or prefix as the sole argument. + +When ACLs are enabled, this command requires a token with the 'read-job' capability +for the allocation's namespace. The 'list-jobs' capability is required to run the +command with an allocation ID prefix instead of the exact allocation ID. + +## General Options + +@include 'general_options.mdx' + +## Checks Options + +- `-verbose`: Display verbose output. + +- `-json`: Output the allocation in its JSON format. + +- `-t`: Format and display the health checks status using a Go template. + +## Examples + +Show checks of an alloc health check using its short ID: + +```shell-session +$ nomad alloc checks e0fdbd85 + +Status of 1 Nomad Service Checks + +ID = 9f4e18fd0867cebb19a8fac3d7a1cf27 +Name = alive +Group = example.cache[0] +Task = (group) +Service = redis-cache +Status = success +Mode = healthiness +Timestamp = 2023-03-09T16:10:23+01:00 +Output = nomad: tcp ok +``` + +The `-json` flag can be used to get the health checks status in json format: + +```shell-session +nomad alloc checks -json 54fde4f8 + +{ + "9810e90177a4c21ce3bfe04dc7da6131": { + "Check": "alive", + "Group": "example.cache[0]", + "ID": "9810e90177a4c21ce3bfe04dc7da6131", + "Mode": "healthiness", + "Output": "nomad: tcp ok", + "Service": "redis-cache", + "Status": "success", + "StatusCode": 0, + "Task": "", + "Timestamp": 1678444309 + } +} +``` + +Or use the `-t` flag to format and display the health checks using a Go template: + +```shell-session +$ nomad alloc checks -t '{{range .}}{{ printf "%s: %s\n" .ID .Status }}{{end}}' 54 + +9810e90177a4c21ce3bfe04dc7da6131: success +``` \ No newline at end of file diff --git a/website/content/docs/commands/alloc/index.mdx b/website/content/docs/commands/alloc/index.mdx index 92652b017..b673a63f0 100644 --- a/website/content/docs/commands/alloc/index.mdx +++ b/website/content/docs/commands/alloc/index.mdx @@ -16,6 +16,7 @@ Usage: `nomad alloc [options]` Run `nomad alloc -h` for help on that subcommand. The following subcommands are available: +- [`alloc checks`][checks] - Outputs service health check status information. - [`alloc exec`][exec] - Run a command in a running allocation - [`alloc fs`][fs] - Inspect the contents of an allocation directory - [`alloc logs`][logs] - Streams the logs of a task @@ -24,6 +25,7 @@ subcommands are available: - [`alloc status`][status] - Display allocation status information and metadata - [`alloc stop`][stop] - Stop and reschedule a running allocation +[checks]: /nomad/docs/commands/alloc/checks 'Outputs service health check status information' [exec]: /nomad/docs/commands/alloc/exec 'Run a command in a running allocation' [fs]: /nomad/docs/commands/alloc/fs 'Inspect the contents of an allocation directory' [logs]: /nomad/docs/commands/alloc/logs 'Streams the logs of a task' diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 3ff074c6c..e2d2a7e86 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -386,6 +386,10 @@ "title": "Overview", "path": "commands/alloc" }, + { + "title": "checks", + "path": "commands/alloc/checks" + }, { "title": "exec", "path": "commands/alloc/exec"