Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions api/jobs_batch_queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright IBM Corp. 2015, 2026
// SPDX-License-Identifier: BUSL-1.1

package api

type Workload struct {
Comment thread
mismithhisler marked this conversation as resolved.
JobID string
Tenant string
Priority int
}

type BatchQueueStatusResponse struct {
Workloads []Workload
}

type BatchQueueStatusOptions struct{}

// BatchQueueStatus is used to query the current batch job queue.
func (j *Jobs) BatchQueueStatus(opts *BatchQueueStatusOptions, q *QueryOptions) (*BatchQueueStatusResponse, *QueryMeta, error) {
var resp BatchQueueStatusResponse
qm, err := j.client.query("/v1/jobs/queue/status", &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, qm, nil
}
5 changes: 5 additions & 0 deletions command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
Meta: meta,
}, nil
},
"job queue": func() (cli.Command, error) {
return &JobQueueCommand{
Meta: meta,
}, nil
},
"job revert": func() (cli.Command, error) {
return &JobRevertCommand{
Meta: meta,
Expand Down
136 changes: 136 additions & 0 deletions command/job_queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright IBM Corp. 2015, 2026
// SPDX-License-Identifier: BUSL-1.1

package command

import (
"encoding/json"
"fmt"
"strings"

"github.com/hashicorp/nomad/api"
"github.com/posener/complete"
"github.com/ryanuber/columnize"
)

type JobQueueCommand struct {
Meta
}

func (c *JobQueueCommand) Help() string {
helpText := `
Usage: nomad job queue [options]

View the current status of workloads queued in a batch job queue.

When ACLs are enabled, this command requires a token with either TBD
capabilities. Probably at least 'list-jobs'.

General Options:

` + generalOptionsUsage(usageOptsDefault) + `

Eval Options:

-limit
Comment thread
mismithhisler marked this conversation as resolved.
The maximum number of workloads to return

-verbose
Display full output

-json
Display output as json

`
return strings.TrimSpace(helpText)
}

func (c *JobQueueCommand) Synopsis() string {
return "View the status of a batch job queue"
}

func (c *JobQueueCommand) AutocompleteFlags() complete.Flags {
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
complete.Flags{
"-verbose": complete.PredictNothing,
"-limit": complete.PredictNothing,
"-json": complete.PredictNothing,
})
}

func (c *JobQueueCommand) AutocompleteArgs() complete.Predictor {
return JobPredictor(c.Meta.Client)
}

func (c *JobQueueCommand) Name() string { return "job queue" }

func (c *JobQueueCommand) Run(args []string) int {
var verbose, json bool
var limit int
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.IntVar(&limit, "limit", 0, "")

if err := flags.Parse(args); err != nil {
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 255
}

// Setup the options
qo := &api.QueryOptions{}

if limit > 0 {
qo.Params["limit"] = fmt.Sprintf("%d", limit)
}

// Submit the request
resp, _, err := client.Jobs().BatchQueueStatus(nil, qo)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error during batch queue request: %s", err))
return 255
}
if resp == nil {
c.Ui.Error("Empty batch queue response")
}

if json {
if err := c.printJSON(resp); err != nil {
c.Ui.Error("Error unmarshaling json response")
return 255
}
} else {
c.printFormatted(resp)
}
return 0
}

func (c *JobQueueCommand) printJSON(resp *api.BatchQueueStatusResponse) error {
out, err := json.Marshal(resp.Workloads)
if err != nil {
return err
}

c.Ui.Output(string(out))
return nil
}

func (c *JobQueueCommand) printFormatted(resp *api.BatchQueueStatusResponse) {

out := make([]string, len(resp.Workloads)+1)
out[0] = "JobID|Tenant|Priority"

for i, v := range resp.Workloads {
out[i+1] = fmt.Sprintf("%s|%s|%d", v.JobID, v.Tenant, v.Priority)
}

c.Ui.Output(c.Colorize().Color("[bold]Batch Queue Workloads[reset]"))
c.Ui.Output(columnize.SimpleFormat(out))
}
64 changes: 64 additions & 0 deletions command/job_queue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright IBM Corp. 2015, 2026
// SPDX-License-Identifier: BUSL-1.1

package command

import (
"fmt"
"testing"

"github.com/hashicorp/cli"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/ci"
"github.com/shoenig/test/must"
)

func TestJobQueue_Implements(t *testing.T) {
ci.Parallel(t)
var _ cli.Command = &JobQueueCommand{}
}

func TestJobQueue_printFormatted(t *testing.T) {
ci.Parallel(t)
ui := cli.NewMockUi()
cmd := &JobQueueCommand{Meta: Meta{Ui: ui}}

testResp := &api.BatchQueueStatusResponse{
Workloads: []api.Workload{
{
JobID: "123",
Tenant: "testTenant1",
Priority: 5,
},
},
}
cmd.printFormatted(testResp)

expect := "Batch Queue Workloads\n" +
"JobID Tenant Priority\n" +
"123 testTenant1 5\n"

fmt.Println(ui.OutputWriter.String())
must.Eq(t, expect, ui.OutputWriter.String())
}

func TestJobQueue_printJSON(t *testing.T) {
ci.Parallel(t)
ui := cli.NewMockUi()
cmd := &JobQueueCommand{Meta: Meta{Ui: ui}}

testResp := &api.BatchQueueStatusResponse{
Workloads: []api.Workload{
{
JobID: "123",
Tenant: "testTenant1",
Priority: 5,
},
},
}
cmd.printJSON(testResp)

expect := "[{\"JobID\":\"123\",\"Tenant\":\"testTenant1\",\"Priority\":5}]\n"

must.Eq(t, expect, ui.OutputWriter.String())
}
Loading