From 1b599c07d3aefe8e8971dd605b06322ba76c2d98 Mon Sep 17 00:00:00 2001 From: davemay99 Date: Wed, 22 Sep 2021 23:31:33 -0400 Subject: [PATCH 1/6] cli: Add nomad job allocs command --- command/commands.go | 5 ++ command/job_allocs.go | 155 +++++++++++++++++++++++++++++++++++++ command/job_allocs_test.go | 109 ++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 command/job_allocs.go create mode 100644 command/job_allocs_test.go diff --git a/command/commands.go b/command/commands.go index 89a42e3b565a..ad22c65e2273 100644 --- a/command/commands.go +++ b/command/commands.go @@ -297,6 +297,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory { Meta: meta, }, nil }, + "job allocs": func() (cli.Command, error) { + return &JobAllocsCommand{ + Meta: meta, + }, nil + }, "job deployments": func() (cli.Command, error) { return &JobDeploymentsCommand{ Meta: meta, diff --git a/command/job_allocs.go b/command/job_allocs.go new file mode 100644 index 000000000000..cd09b4919d9d --- /dev/null +++ b/command/job_allocs.go @@ -0,0 +1,155 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/api/contexts" + "github.com/posener/complete" +) + +type JobAllocsCommand struct { + Meta +} + +func (c *JobAllocsCommand) Help() string { + helpText := ` +Usage: nomad job allocs [options] + + Allocs is used to display the allocations for a particular job. + + When ACLs are enabled, this command requires a token with the 'read-job' and + 'list-jobs' capabilities for the job's namespace. + +General Options: + + ` + generalOptionsUsage(usageOptsDefault) + ` + +Allocs Options: + + -json + Output the allocations in a JSON format. + + -t + Format and display allocations using a Go template. + + -verbose + Display full information. +` + return strings.TrimSpace(helpText) +} + +func (c *JobAllocsCommand) Synopsis() string { + return "List allocations for a job" +} + +func (c *JobAllocsCommand) AutocompleteFlags() complete.Flags { + return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), + complete.Flags{ + "-json": complete.PredictNothing, + "-t": complete.PredictAnything, + "-verbose": complete.PredictNothing, + "-all": complete.PredictNothing, + }) +} + +func (c *JobAllocsCommand) 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.Jobs, nil) + if err != nil { + return []string{} + } + return resp.Matches[contexts.Jobs] + }) +} + +func (c *JobAllocsCommand) Name() string { return "job allocations" } + +func (c *JobAllocsCommand) Run(args []string) int { + var json, verbose, all 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(&all, "all", false, "") + flags.BoolVar(&json, "json", false, "") + flags.StringVar(&tmpl, "t", "", "") + + if err := flags.Parse(args); err != nil { + return 1 + } + + // Check that we got exactly one job + args = flags.Args() + if l := len(args); l != 1 { + c.Ui.Error("This command takes one argument: ") + c.Ui.Error(commandErrorText(c)) + return 1 + } + + // Get the HTTP client + client, err := c.Meta.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) + return 1 + } + + jobID := strings.TrimSpace(args[0]) + + // Check if the job exists + jobs, _, err := client.Jobs().PrefixList(jobID) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error listing jobs: %s", err)) + return 1 + } + if len(jobs) == 0 { + c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID)) + return 1 + } + if len(jobs) > 1 { + if jobID != jobs[0].ID { + c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs, c.allNamespaces()))) + return 1 + } + if c.allNamespaces() && jobs[0].ID == jobs[1].ID { + c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs, c.allNamespaces()))) + return 1 + } + } + + jobID = jobs[0].ID + q := &api.QueryOptions{Namespace: jobs[0].JobSummary.Namespace} + + allocs, _, err := client.Jobs().Allocations(jobID, all, q) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error retrieving allocations: %s", err)) + return 1 + } + + if json || len(tmpl) > 0 { + out, err := Format(json, tmpl, allocs) + if err != nil { + c.Ui.Error(err.Error()) + return 1 + } + + c.Ui.Output(out) + return 0 + } + + // Truncate the id unless full length is requested + length := shortId + if verbose { + length = fullId + } + + c.Ui.Output(formatAllocListStubs(allocs, verbose, length)) + return 0 +} diff --git a/command/job_allocs_test.go b/command/job_allocs_test.go new file mode 100644 index 000000000000..2af273ace358 --- /dev/null +++ b/command/job_allocs_test.go @@ -0,0 +1,109 @@ +package command + +import ( + "testing" + + "github.com/hashicorp/nomad/nomad/structs" + + "github.com/hashicorp/nomad/nomad/mock" + "github.com/mitchellh/cli" + "github.com/posener/complete" + "github.com/stretchr/testify/require" +) + +func TestJobAllocsCommand_Implements(t *testing.T) { + t.Parallel() + var _ cli.Command = &JobAllocsCommand{} +} + +func TestJobAllocsCommand_Fails(t *testing.T) { + t.Parallel() + ui := cli.NewMockUi() + cmd := &JobAllocsCommand{Meta: Meta{Ui: ui}} + + // Fails on misuse + code := cmd.Run([]string{"some", "bad", "args"}) + require.Equalf(t, 1, code, "expected exit code 1, got: %d", code) + + out := ui.ErrorWriter.String() + require.Containsf(t, out, commandErrorText(cmd), "expected help output, got: %s", out) + + ui.ErrorWriter.Reset() + + code = cmd.Run([]string{"-address=nope", "foo"}) + require.Equalf(t, 1, code, "expected exit code 1, got: %d", code) + + out = ui.ErrorWriter.String() + require.Containsf(t, out, "Error listing jobs", "expected failed query error, got: %s", out) + + ui.ErrorWriter.Reset() +} + +func TestJobAllocsCommand_Run(t *testing.T) { + t.Parallel() + srv, _, url := testServer(t, true, nil) + defer srv.Shutdown() + + ui := cli.NewMockUi() + cmd := &JobAllocsCommand{Meta: Meta{Ui: ui}} + + // Should return an error message for no job match + code := cmd.Run([]string{"-address=" + url, "foo"}) + require.Equalf(t, 1, code, "expected exit 1, got: %d", code) + + // Create a job without an allocation + job := mock.Job() + state := srv.Agent.Server().State() + require.Nil(t, state.UpsertJob(structs.MsgTypeTestSetup, 100, job)) + + // Should display no match if the job doesn't have allocations + code = cmd.Run([]string{"-address=" + url, job.ID}) + require.Equalf(t, 0, code, "expected exit 0, got: %d", code) + + out := ui.OutputWriter.String() + require.Containsf(t, out, "No allocations placed", "expected no allocations placed, got: %s", out) + + ui.OutputWriter.Reset() + + // Inject an allocation + a := mock.Alloc() + a.Job = job + a.JobID = job.ID + a.TaskGroup = job.TaskGroups[0].Name + a.Metrics = &structs.AllocMetric{} + a.DesiredStatus = structs.AllocDesiredStatusRun + a.ClientStatus = structs.AllocClientStatusRunning + require.Nil(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 200, []*structs.Allocation{a})) + + // Should now display the alloc + code = cmd.Run([]string{"-address=" + url, "-verbose", job.ID}) + require.Equalf(t, 0, code, "expected exit 0, got: %d", code) + + out = ui.OutputWriter.String() + require.Containsf(t, out, a.ID, "expected alloc output, got: %s", out) + + ui.OutputWriter.Reset() +} + +func TestJobAllocsCommand_AutocompleteArgs(t *testing.T) { + t.Parallel() + + srv, _, url := testServer(t, true, nil) + defer srv.Shutdown() + + ui := cli.NewMockUi() + cmd := &JobAllocsCommand{Meta: Meta{Ui: ui, flagAddress: url}} + + // Create a fake job + state := srv.Agent.Server().State() + j := mock.Job() + require.Nil(t, state.UpsertJob(structs.MsgTypeTestSetup, 1000, j)) + + prefix := j.ID[:len(j.ID)-5] + args := complete.Args{Last: prefix} + predictor := cmd.AutocompleteArgs() + + res := predictor.Predict(args) + require.Equal(t, 1, len(res)) + require.Equal(t, j.ID, res[0]) +} From dd0bc50ea9ed76b6fc5f70e6106537e054d351f1 Mon Sep 17 00:00:00 2001 From: davemay99 Date: Thu, 23 Sep 2021 16:49:54 -0400 Subject: [PATCH 2/6] Cleanup tests --- command/job_allocs_test.go | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/command/job_allocs_test.go b/command/job_allocs_test.go index 2af273ace358..756be85d185e 100644 --- a/command/job_allocs_test.go +++ b/command/job_allocs_test.go @@ -18,23 +18,33 @@ func TestJobAllocsCommand_Implements(t *testing.T) { func TestJobAllocsCommand_Fails(t *testing.T) { t.Parallel() + srv, _, url := testServer(t, true, nil) + defer srv.Shutdown() + ui := cli.NewMockUi() cmd := &JobAllocsCommand{Meta: Meta{Ui: ui}} // Fails on misuse code := cmd.Run([]string{"some", "bad", "args"}) + outerr := ui.ErrorWriter.String() require.Equalf(t, 1, code, "expected exit code 1, got: %d", code) - - out := ui.ErrorWriter.String() - require.Containsf(t, out, commandErrorText(cmd), "expected help output, got: %s", out) + require.Containsf(t, outerr, commandErrorText(cmd), "expected help output, got: %s", outerr) ui.ErrorWriter.Reset() + // Bad address code = cmd.Run([]string{"-address=nope", "foo"}) + outerr = ui.ErrorWriter.String() require.Equalf(t, 1, code, "expected exit code 1, got: %d", code) + require.Containsf(t, outerr, "Error listing jobs", "expected failed query error, got: %s", outerr) - out = ui.ErrorWriter.String() - require.Containsf(t, out, "Error listing jobs", "expected failed query error, got: %s", out) + ui.ErrorWriter.Reset() + + // Bad job name + code = cmd.Run([]string{"-address=" + url, "foo"}) + outerr = ui.ErrorWriter.String() + require.Equalf(t, 1, code, "expected exit 1, got: %d", code) + require.Containsf(t, outerr, "No job(s) with prefix or id \"foo\" found", "expected no job found, got: %s", outerr) ui.ErrorWriter.Reset() } @@ -47,20 +57,15 @@ func TestJobAllocsCommand_Run(t *testing.T) { ui := cli.NewMockUi() cmd := &JobAllocsCommand{Meta: Meta{Ui: ui}} - // Should return an error message for no job match - code := cmd.Run([]string{"-address=" + url, "foo"}) - require.Equalf(t, 1, code, "expected exit 1, got: %d", code) - // Create a job without an allocation job := mock.Job() state := srv.Agent.Server().State() require.Nil(t, state.UpsertJob(structs.MsgTypeTestSetup, 100, job)) // Should display no match if the job doesn't have allocations - code = cmd.Run([]string{"-address=" + url, job.ID}) - require.Equalf(t, 0, code, "expected exit 0, got: %d", code) - + code := cmd.Run([]string{"-address=" + url, job.ID}) out := ui.OutputWriter.String() + require.Equalf(t, 0, code, "expected exit 0, got: %d", code) require.Containsf(t, out, "No allocations placed", "expected no allocations placed, got: %s", out) ui.OutputWriter.Reset() @@ -77,8 +82,15 @@ func TestJobAllocsCommand_Run(t *testing.T) { // Should now display the alloc code = cmd.Run([]string{"-address=" + url, "-verbose", job.ID}) + out = ui.OutputWriter.String() + outerr := ui.ErrorWriter.String() require.Equalf(t, 0, code, "expected exit 0, got: %d", code) + require.Emptyf(t, outerr, "expected no error output, got: \n\n%s", outerr) + require.Containsf(t, out, a.ID, "expected alloc output, got: %s", out) + ui.OutputWriter.Reset() + ui.ErrorWriter.Reset() +} out = ui.OutputWriter.String() require.Containsf(t, out, a.ID, "expected alloc output, got: %s", out) @@ -87,7 +99,6 @@ func TestJobAllocsCommand_Run(t *testing.T) { func TestJobAllocsCommand_AutocompleteArgs(t *testing.T) { t.Parallel() - srv, _, url := testServer(t, true, nil) defer srv.Shutdown() From b0b48f374f76b54a7ffa04fb7d2aee442e0897f6 Mon Sep 17 00:00:00 2001 From: davemay99 Date: Thu, 23 Sep 2021 16:50:27 -0400 Subject: [PATCH 3/6] Add test for gotemplate formatting --- command/job_allocs_test.go | 56 +++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/command/job_allocs_test.go b/command/job_allocs_test.go index 756be85d185e..449a12253a5b 100644 --- a/command/job_allocs_test.go +++ b/command/job_allocs_test.go @@ -91,10 +91,64 @@ func TestJobAllocsCommand_Run(t *testing.T) { ui.OutputWriter.Reset() ui.ErrorWriter.Reset() } + +func TestJobAllocsCommand_Template(t *testing.T) { + t.Parallel() + srv, _, url := testServer(t, true, nil) + defer srv.Shutdown() + + ui := cli.NewMockUi() + cmd := &JobAllocsCommand{Meta: Meta{Ui: ui}} + + // Create a job + job := mock.Job() + state := srv.Agent.Server().State() + require.Nil(t, state.UpsertJob(structs.MsgTypeTestSetup, 100, job)) + + // Inject a running allocation + a := mock.Alloc() + a.Job = job + a.JobID = job.ID + a.TaskGroup = job.TaskGroups[0].Name + a.Metrics = &structs.AllocMetric{} + a.DesiredStatus = structs.AllocDesiredStatusRun + a.ClientStatus = structs.AllocClientStatusRunning + require.Nil(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 200, []*structs.Allocation{a})) + + // Inject a pending allocation + b := mock.Alloc() + b.Job = job + b.JobID = job.ID + b.TaskGroup = job.TaskGroups[0].Name + b.Metrics = &structs.AllocMetric{} + b.DesiredStatus = structs.AllocDesiredStatusRun + b.ClientStatus = structs.AllocClientStatusPending + require.Nil(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 300, []*structs.Allocation{b})) + + // Should display an AllocacitonListStub object + code := cmd.Run([]string{"-address=" + url, "-t", "'{{printf \"%#+v\" .}}'", job.ID}) + out := ui.OutputWriter.String() + outerr := ui.ErrorWriter.String() + + require.Equalf(t, 0, code, "expected exit 0, got: %d", code) + require.Emptyf(t, outerr, "expected no error output, got: \n\n%s", outerr) + require.Containsf(t, out, "api.AllocationListStub", "expected alloc output, got: %s", out) + + ui.OutputWriter.Reset() + ui.ErrorWriter.Reset() + + // Should display only the running allocation ID + code = cmd.Run([]string{"-address=" + url, "-t", "'{{ range . }}{{ if eq .ClientStatus \"running\" }}{{ println .ID }}{{ end }}{{ end }}'", job.ID}) out = ui.OutputWriter.String() - require.Containsf(t, out, a.ID, "expected alloc output, got: %s", out) + outerr = ui.ErrorWriter.String() + + require.Equalf(t, 0, code, "expected exit 0, got: %d", code) + require.Emptyf(t, outerr, "expected no error output, got: \n\n%s", outerr) + require.Containsf(t, out, a.ID, "expected ID of alloc a, got: %s", out) + require.NotContainsf(t, out, b.ID, "should not contain ID of alloc b, got: %s", out) ui.OutputWriter.Reset() + ui.ErrorWriter.Reset() } func TestJobAllocsCommand_AutocompleteArgs(t *testing.T) { From d3061cc362312777d71c2fb7953e755375d2da6c Mon Sep 17 00:00:00 2001 From: davemay99 Date: Thu, 23 Sep 2021 16:55:02 -0400 Subject: [PATCH 4/6] Add job allocs documentation --- website/content/docs/commands/job/allocs.mdx | 73 ++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 website/content/docs/commands/job/allocs.mdx diff --git a/website/content/docs/commands/job/allocs.mdx b/website/content/docs/commands/job/allocs.mdx new file mode 100644 index 000000000000..97c73f20c37a --- /dev/null +++ b/website/content/docs/commands/job/allocs.mdx @@ -0,0 +1,73 @@ +--- +layout: docs +page_title: 'Commands: job allocs' +description: | + The allocs command is used to list allocations for a job. +--- + +# Command: job allocs + +The `job allocs` command is used to display the allocations for a +particular job. + +## Usage + +```plaintext +nomad job allocs [options] +``` + +The `job allocs` command requires a single argument, the job ID or an ID +prefix of a job to display the list of allocations for. + +When ACLs are enabled, this command requires a token with the `read-job` and +`list-jobs` capabilities for the job's namespace. + +## General Options + +@include 'general_options.mdx' + +## Deployment Options + +- `-json` : Output the allocations in JSON format. + +- `-t` : Format and display the allocations using a Go template. + +- `-verbose`: Show full information. + +- `-all`: Display all allocations matching the job ID, even those from an + older instance of the job. + +## Examples + +List the allocations for a particular job: + +```shell-session +$ nomad job allocs example +ID Node ID Task Group Version Desired Status Created Modified +c2b4606d 35085106 cache 2 run running 21s ago 10s ago +c413424b 35085106 cache 2 run pending 1m8s ago 11s ago +``` + +Verbose listing of allocations for a particular job: + +```shell-session +$ nomad job allocs -verbose example +ID Eval ID Node ID Node Name Task Group Version Desired Status Created Modified +c2b4606d-1b02-0d8d-5fdd-031167cd4c91 5e2badb6-b7cf-5177-8281-8fe14f7193d2 35085106-9480-b465-a348-deb745024394 ubuntu cache 2 run running 2021-09-23T14:45:09-04:00 2021-09-23T14:45:19-04:00 +c413424b-d80e-9bc6-ea92-a02b336eaaf5 5e2badb6-b7cf-5177-8281-8fe14f7193d2 35085106-9480-b465-a348-deb745024394 ubuntu cache 2 run pending 2021-09-23T14:44:22-04:00 2021-09-23T14:45:19-04:00 +``` + +Format job allocations using a Go template: +```shell-session +$ nomad job allocs -t '{{ range . }}{{ println .ID }}{{ end }}' example +c2b4606d-1b02-0d8d-5fdd-031167cd4c91 +c413424b-d80e-9bc6-ea92-a02b336eaaf5 +``` + +Use a Go template to filter only allocations which are running +```shell-session +$ nomad job allocs -t '{{ range . }}{{ if eq .ClientStatus "running" }}{{ println .ID }}{{ end }}{{ end }}' example +c2b4606d-1b02-0d8d-5fdd-031167cd4c91 +``` + +Refer to the [Format Nomad Command Output With Templates](https://learn.hashicorp.com/tutorials/nomad/format-output-with-templates)] tutorial for more examples of using Go templates to format Nomad CLI output. From c223f0b788220594e809f1e0b368c8dbd3bcd90f Mon Sep 17 00:00:00 2001 From: davemay99 Date: Wed, 29 Sep 2021 17:55:16 -0400 Subject: [PATCH 5/6] Add changelog entry --- .changelog/11242.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/11242.txt diff --git a/.changelog/11242.txt b/.changelog/11242.txt new file mode 100644 index 000000000000..82aed56dc22f --- /dev/null +++ b/.changelog/11242.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: Add nomad job allocs command +``` \ No newline at end of file From b3d736a49a642a92dabd13708338cec900773607 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Fri, 8 Oct 2021 19:16:41 -0400 Subject: [PATCH 6/6] code review --- .changelog/11242.txt | 4 ++-- command/job_allocs.go | 8 ++++++-- website/content/docs/commands/job/allocs.mdx | 21 +++++++++++--------- website/data/docs-nav-data.json | 4 ++++ 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/.changelog/11242.txt b/.changelog/11242.txt index 82aed56dc22f..a2ea64d8f818 100644 --- a/.changelog/11242.txt +++ b/.changelog/11242.txt @@ -1,3 +1,3 @@ ```release-note:improvement -cli: Add nomad job allocs command -``` \ No newline at end of file +cli: Add `nomad job allocs` command +``` diff --git a/command/job_allocs.go b/command/job_allocs.go index cd09b4919d9d..77139199cc4c 100644 --- a/command/job_allocs.go +++ b/command/job_allocs.go @@ -17,7 +17,7 @@ func (c *JobAllocsCommand) Help() string { helpText := ` Usage: nomad job allocs [options] - Allocs is used to display the allocations for a particular job. + Display allocations for a particular job. When ACLs are enabled, this command requires a token with the 'read-job' and 'list-jobs' capabilities for the job's namespace. @@ -28,6 +28,10 @@ General Options: Allocs Options: + -all + Display all allocations matching the job ID, even those from an older + instance of the job. + -json Output the allocations in a JSON format. @@ -88,7 +92,7 @@ func (c *JobAllocsCommand) Run(args []string) int { // Check that we got exactly one job args = flags.Args() - if l := len(args); l != 1 { + if len(args) != 1 { c.Ui.Error("This command takes one argument: ") c.Ui.Error(commandErrorText(c)) return 1 diff --git a/website/content/docs/commands/job/allocs.mdx b/website/content/docs/commands/job/allocs.mdx index 97c73f20c37a..c5bdaf8e4a35 100644 --- a/website/content/docs/commands/job/allocs.mdx +++ b/website/content/docs/commands/job/allocs.mdx @@ -26,23 +26,23 @@ When ACLs are enabled, this command requires a token with the `read-job` and @include 'general_options.mdx' -## Deployment Options +## Allocs Options -- `-json` : Output the allocations in JSON format. +- `-all`: Display all allocations matching the job ID, even those from an + older instance of the job. -- `-t` : Format and display the allocations using a Go template. +- `-json`: Output the allocations in JSON format. -- `-verbose`: Show full information. +- `-t`: Format and display the allocations using a Go template. -- `-all`: Display all allocations matching the job ID, even those from an - older instance of the job. +- `-verbose`: Show full information. ## Examples List the allocations for a particular job: ```shell-session -$ nomad job allocs example +$ nomad job allocs example ID Node ID Task Group Version Desired Status Created Modified c2b4606d 35085106 cache 2 run running 21s ago 10s ago c413424b 35085106 cache 2 run pending 1m8s ago 11s ago @@ -51,7 +51,7 @@ c413424b 35085106 cache 2 run pending 1m8s ago 11s ago Verbose listing of allocations for a particular job: ```shell-session -$ nomad job allocs -verbose example +$ nomad job allocs -verbose example ID Eval ID Node ID Node Name Task Group Version Desired Status Created Modified c2b4606d-1b02-0d8d-5fdd-031167cd4c91 5e2badb6-b7cf-5177-8281-8fe14f7193d2 35085106-9480-b465-a348-deb745024394 ubuntu cache 2 run running 2021-09-23T14:45:09-04:00 2021-09-23T14:45:19-04:00 c413424b-d80e-9bc6-ea92-a02b336eaaf5 5e2badb6-b7cf-5177-8281-8fe14f7193d2 35085106-9480-b465-a348-deb745024394 ubuntu cache 2 run pending 2021-09-23T14:44:22-04:00 2021-09-23T14:45:19-04:00 @@ -70,4 +70,7 @@ $ nomad job allocs -t '{{ range . }}{{ if eq .ClientStatus "running" }}{{ printl c2b4606d-1b02-0d8d-5fdd-031167cd4c91 ``` -Refer to the [Format Nomad Command Output With Templates](https://learn.hashicorp.com/tutorials/nomad/format-output-with-templates)] tutorial for more examples of using Go templates to format Nomad CLI output. +Refer to the [Format Nomad Command Output With Templates][format_tutorial] +tutorial for more examples of using Go templates to format Nomad CLI output. + +[format_tutorial]: https://learn.hashicorp.com/tutorials/nomad/format-output-with-templates diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index b15e7b36e443..389fa2a62cbe 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -359,6 +359,10 @@ "title": "Overview", "path": "commands/job" }, + { + "title": "allocs", + "path": "commands/job/allocs" + }, { "title": "deployments", "path": "commands/job/deployments"