Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
24 changes: 24 additions & 0 deletions api/jobs_batch_queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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
}

// BatchQueueStatus is used to query the current batch job queue.
func (j *Jobs) BatchQueueStatus(q *QueryOptions) (*BatchQueueStatusResponse, *QueryMeta, error) {
Comment thread
mismithhisler marked this conversation as resolved.
Outdated
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
143 changes: 143 additions & 0 deletions command/job_queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright IBM Corp. 2015, 2026
// SPDX-License-Identifier: BUSL-1.1

package command

import (
"fmt"
"strings"

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

type JobQueueCommand struct {
Meta
forceRescheduling bool
Comment thread
mismithhisler marked this conversation as resolved.
Outdated
}

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

`
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,
})
}

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 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.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
opts := &api.QueryOptions{}

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

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

c.printOutput(resp)
return 0
}

func (c *JobQueueCommand) printOutput(resp *api.BatchQueueStatusResponse) {
if resp == nil {
c.Ui.Error("Empty batch queue response")
}

headers := []string{"JobID", "Tenant", "Priority"}

// compute column widths
col0, col1, col2 := len(headers[0]), len(headers[1]), len(headers[2])
for _, r := range resp.Workloads {
if len(r.JobID) > col0 {
col0 = len(r.JobID)
}
if len(r.Tenant) > col1 {
col1 = len(r.Tenant)
}
// convert int to string for width calculation
l := len(fmt.Sprintf("%d", r.Priority))
if l > col2 {
col2 = l
}
}
Comment thread
mismithhisler marked this conversation as resolved.
Outdated

headerFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n", col0, col1, col2)
rowFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%%dd\n", col0, col1, col2)

var output strings.Builder

// print header
fmt.Fprintf(&output, headerFmt, headers[0], headers[1], headers[2])

// print separator
fmt.Fprintf(&output, "%s-+-%s-+-%s\n",
strings.Repeat("-", col0),
strings.Repeat("-", col1),
strings.Repeat("-", col2),
)

// print rows
for _, w := range resp.Workloads {
fmt.Fprintf(&output, rowFmt, w.JobID, w.Tenant, w.Priority)
}

c.Ui.Output(output.String())
}
41 changes: 41 additions & 0 deletions command/job_queue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright IBM Corp. 2015, 2026
// SPDX-License-Identifier: BUSL-1.1

package command

import (
"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_printOutput(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.printOutput(testResp)

expect := "JobID | Tenant | Priority\n" +
"------+-------------+---------\n" +
"123 | testTenant1 | 5\n\n"

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