Skip to content

Commit de132a9

Browse files
committed
command: initial batch queue cli implementation (#27909)
1 parent 71efc64 commit de132a9

4 files changed

Lines changed: 231 additions & 0 deletions

File tree

api/jobs_batch_queue.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright IBM Corp. 2015, 2026
2+
// SPDX-License-Identifier: BUSL-1.1
3+
4+
package api
5+
6+
type Workload struct {
7+
JobID string
8+
Tenant string
9+
Priority int
10+
}
11+
12+
type BatchQueueStatusResponse struct {
13+
Workloads []Workload
14+
}
15+
16+
type BatchQueueStatusOptions struct{}
17+
18+
// BatchQueueStatus is used to query the current batch job queue.
19+
func (j *Jobs) BatchQueueStatus(opts *BatchQueueStatusOptions, q *QueryOptions) (*BatchQueueStatusResponse, *QueryMeta, error) {
20+
var resp BatchQueueStatusResponse
21+
qm, err := j.client.query("/v1/jobs/queue/status", &resp, q)
22+
if err != nil {
23+
return nil, nil, err
24+
}
25+
return &resp, qm, nil
26+
}

command/commands.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
501501
Meta: meta,
502502
}, nil
503503
},
504+
"job queue": func() (cli.Command, error) {
505+
return &JobQueueCommand{
506+
Meta: meta,
507+
}, nil
508+
},
504509
"job revert": func() (cli.Command, error) {
505510
return &JobRevertCommand{
506511
Meta: meta,

command/job_queue.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright IBM Corp. 2015, 2026
2+
// SPDX-License-Identifier: BUSL-1.1
3+
4+
package command
5+
6+
import (
7+
"encoding/json"
8+
"fmt"
9+
"strings"
10+
11+
"github.com/hashicorp/nomad/api"
12+
"github.com/posener/complete"
13+
"github.com/ryanuber/columnize"
14+
)
15+
16+
type JobQueueCommand struct {
17+
Meta
18+
}
19+
20+
func (c *JobQueueCommand) Help() string {
21+
helpText := `
22+
Usage: nomad job queue [options]
23+
24+
View the current status of workloads queued in a batch job queue.
25+
26+
When ACLs are enabled, this command requires a token with either TBD
27+
capabilities. Probably at least 'list-jobs'.
28+
29+
General Options:
30+
31+
` + generalOptionsUsage(usageOptsDefault) + `
32+
33+
Eval Options:
34+
35+
-limit
36+
The maximum number of workloads to return
37+
38+
-verbose
39+
Display full output
40+
41+
-json
42+
Display output as json
43+
44+
`
45+
return strings.TrimSpace(helpText)
46+
}
47+
48+
func (c *JobQueueCommand) Synopsis() string {
49+
return "View the status of a batch job queue"
50+
}
51+
52+
func (c *JobQueueCommand) AutocompleteFlags() complete.Flags {
53+
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
54+
complete.Flags{
55+
"-verbose": complete.PredictNothing,
56+
"-limit": complete.PredictNothing,
57+
"-json": complete.PredictNothing,
58+
})
59+
}
60+
61+
func (c *JobQueueCommand) AutocompleteArgs() complete.Predictor {
62+
return JobPredictor(c.Meta.Client)
63+
}
64+
65+
func (c *JobQueueCommand) Name() string { return "job queue" }
66+
67+
func (c *JobQueueCommand) Run(args []string) int {
68+
var verbose, json bool
69+
var limit int
70+
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
71+
flags.Usage = func() { c.Ui.Output(c.Help()) }
72+
flags.BoolVar(&verbose, "verbose", false, "")
73+
flags.BoolVar(&json, "json", false, "")
74+
flags.IntVar(&limit, "limit", 0, "")
75+
76+
if err := flags.Parse(args); err != nil {
77+
return 1
78+
}
79+
80+
// Get the HTTP client
81+
client, err := c.Meta.Client()
82+
if err != nil {
83+
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
84+
return 255
85+
}
86+
87+
// Setup the options
88+
qo := &api.QueryOptions{}
89+
90+
if limit > 0 {
91+
qo.PerPage = int32(limit)
92+
}
93+
94+
// Submit the request
95+
resp, _, err := client.Jobs().BatchQueueStatus(nil, qo)
96+
if err != nil {
97+
c.Ui.Error(fmt.Sprintf("Error during batch queue request: %s", err))
98+
return 255
99+
}
100+
if resp == nil {
101+
c.Ui.Error("Empty batch queue response")
102+
}
103+
104+
if json {
105+
if err := c.printJSON(resp); err != nil {
106+
c.Ui.Error("Error unmarshaling json response")
107+
return 255
108+
}
109+
} else {
110+
c.printFormatted(resp)
111+
}
112+
return 0
113+
}
114+
115+
func (c *JobQueueCommand) printJSON(resp *api.BatchQueueStatusResponse) error {
116+
out, err := json.Marshal(resp.Workloads)
117+
if err != nil {
118+
return err
119+
}
120+
121+
c.Ui.Output(string(out))
122+
return nil
123+
}
124+
125+
func (c *JobQueueCommand) printFormatted(resp *api.BatchQueueStatusResponse) {
126+
127+
out := make([]string, len(resp.Workloads)+1)
128+
out[0] = "JobID|Tenant|Priority"
129+
130+
for i, v := range resp.Workloads {
131+
out[i+1] = fmt.Sprintf("%s|%s|%d", v.JobID, v.Tenant, v.Priority)
132+
}
133+
134+
c.Ui.Output(c.Colorize().Color("[bold]Batch Queue Workloads[reset]"))
135+
c.Ui.Output(columnize.SimpleFormat(out))
136+
}

command/job_queue_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright IBM Corp. 2015, 2026
2+
// SPDX-License-Identifier: BUSL-1.1
3+
4+
package command
5+
6+
import (
7+
"fmt"
8+
"testing"
9+
10+
"github.com/hashicorp/cli"
11+
"github.com/hashicorp/nomad/api"
12+
"github.com/hashicorp/nomad/ci"
13+
"github.com/shoenig/test/must"
14+
)
15+
16+
func TestJobQueue_Implements(t *testing.T) {
17+
ci.Parallel(t)
18+
var _ cli.Command = &JobQueueCommand{}
19+
}
20+
21+
func TestJobQueue_printFormatted(t *testing.T) {
22+
ci.Parallel(t)
23+
ui := cli.NewMockUi()
24+
cmd := &JobQueueCommand{Meta: Meta{Ui: ui}}
25+
26+
testResp := &api.BatchQueueStatusResponse{
27+
Workloads: []api.Workload{
28+
{
29+
JobID: "123",
30+
Tenant: "testTenant1",
31+
Priority: 5,
32+
},
33+
},
34+
}
35+
cmd.printFormatted(testResp)
36+
37+
expect := "Batch Queue Workloads\n" +
38+
"JobID Tenant Priority\n" +
39+
"123 testTenant1 5\n"
40+
41+
fmt.Println(ui.OutputWriter.String())
42+
must.Eq(t, expect, ui.OutputWriter.String())
43+
}
44+
45+
func TestJobQueue_printJSON(t *testing.T) {
46+
ci.Parallel(t)
47+
ui := cli.NewMockUi()
48+
cmd := &JobQueueCommand{Meta: Meta{Ui: ui}}
49+
50+
testResp := &api.BatchQueueStatusResponse{
51+
Workloads: []api.Workload{
52+
{
53+
JobID: "123",
54+
Tenant: "testTenant1",
55+
Priority: 5,
56+
},
57+
},
58+
}
59+
cmd.printJSON(testResp)
60+
61+
expect := "[{\"JobID\":\"123\",\"Tenant\":\"testTenant1\",\"Priority\":5}]\n"
62+
63+
must.Eq(t, expect, ui.OutputWriter.String())
64+
}

0 commit comments

Comments
 (0)