Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: use shared logic for resolving job prefix #16306

Merged
merged 3 commits into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions .changelog/16306.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:improvement
cli: Add job prefix match to the `nomad job dispatch`, `nomad job eval`, `nomad job scale`, and `nomad job scaling-events` commands
```

```release-note:improvement
cli: Add support for the wildcard namespace `*` to the `nomad job dispatch`, `nomad job eval`, `nomad job scale`, and `nomad job scaling-events` commands
```
20 changes: 4 additions & 16 deletions command/job_allocs.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,27 +105,15 @@ func (c *JobAllocsCommand) Run(args []string) int {
return 1
}

jobID := strings.TrimSpace(args[0])

// Check if the job exists
jobs, _, err := client.Jobs().PrefixList(jobID)
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, nil)
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))
c.Ui.Error(err.Error())
return 1
}
if len(jobs) > 1 {
if (jobID != jobs[0].ID) || (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}
q := &api.QueryOptions{Namespace: namespace}

allocs, _, err := client.Jobs().Allocations(jobID, all, q)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions command/job_allocs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ func TestJobAllocsCommand_Fails(t *testing.T) {
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)
require.Containsf(t, outerr, "Error querying job prefix", "expected failed query error, got: %s", outerr)

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)
require.Containsf(t, outerr, "No job(s) with prefix or ID \"foo\" found", "expected no job found, got: %s", outerr)

ui.ErrorWriter.Reset()
}
Expand Down
20 changes: 4 additions & 16 deletions command/job_deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,27 +110,15 @@ func (c *JobDeploymentsCommand) Run(args []string) int {
return 1
}

jobID := strings.TrimSpace(args[0])

// Check if the job exists
jobs, _, err := client.Jobs().PrefixList(jobID)
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, nil)
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))
c.Ui.Error(err.Error())
return 1
}
if len(jobs) > 1 {
if (jobID != jobs[0].ID) || (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}
q := &api.QueryOptions{Namespace: namespace}

// Truncate the id unless full length is requested
length := shortId
Expand Down
2 changes: 1 addition & 1 deletion command/job_deployments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestJobDeploymentsCommand_Fails(t *testing.T) {
if code := cmd.Run([]string{"-address=nope", "foo"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error listing jobs") {
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying job prefix") {
t.Fatalf("expected failed query error, got: %s", out)
}
ui.ErrorWriter.Reset()
Expand Down
14 changes: 12 additions & 2 deletions command/job_dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ func (c *JobDispatchCommand) Run(args []string) int {
return 1
}

job := args[0]
var payload []byte
var readErr error

Expand Down Expand Up @@ -175,11 +174,22 @@ func (c *JobDispatchCommand) Run(args []string) int {
return 1
}

// Check if the job exists
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, func(j *api.JobListStub) bool {
return j.ParameterizedJob
})
if err != nil {
c.Ui.Error(err.Error())
return 1
}

// Dispatch the job
w := &api.WriteOptions{
IdempotencyToken: idempotencyToken,
Namespace: namespace,
}
resp, _, err := client.Jobs().Dispatch(job, metaMap, payload, idPrefixTemplate, w)
resp, _, err := client.Jobs().Dispatch(jobID, metaMap, payload, idPrefixTemplate, w)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to dispatch job: %s", err))
return 1
Expand Down
2 changes: 1 addition & 1 deletion command/job_dispatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestJobDispatchCommand_Fails(t *testing.T) {
if code := cmd.Run([]string{"-address=nope", "foo"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Failed to dispatch") {
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying job prefix") {
t.Fatalf("expected failed query error, got: %s", out)
}
ui.ErrorWriter.Reset()
Expand Down
16 changes: 13 additions & 3 deletions command/job_eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,23 @@ func (c *JobEvalCommand) Run(args []string) int {
if verbose {
length = fullId
}
// Call eval endpoint
jobID := args[0]

// Check if the job exists
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, nil)
if err != nil {
c.Ui.Error(err.Error())
return 1
}

// Call eval endpoint
opts := api.EvalOptions{
ForceReschedule: c.forceRescheduling,
}
evalId, _, err := client.Jobs().EvaluateWithOpts(jobID, opts, nil)
w := &api.WriteOptions{
Namespace: namespace,
}
evalId, _, err := client.Jobs().EvaluateWithOpts(jobID, opts, w)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error evaluating job: %s", err))
return 1
Expand Down
21 changes: 5 additions & 16 deletions command/job_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,29 +121,18 @@ func (c *JobHistoryCommand) Run(args []string) int {
return 1
}

jobID := strings.TrimSpace(args[0])

// Check if the job exists
jobs, _, err := client.Jobs().PrefixList(jobID)
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, nil)
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))
c.Ui.Error(err.Error())
return 1
}
if len(jobs) > 1 {
if (jobID != jobs[0].ID) || (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
}
}

q := &api.QueryOptions{Namespace: jobs[0].JobSummary.Namespace}
q := &api.QueryOptions{Namespace: namespace}

// Prefix lookup matched a single job
versions, diffs, _, err := client.Jobs().Versions(jobs[0].ID, diff, q)
versions, diffs, _, err := client.Jobs().Versions(jobID, diff, q)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error retrieving job versions: %s", err))
return 1
Expand Down
2 changes: 1 addition & 1 deletion command/job_history_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestJobHistoryCommand_Fails(t *testing.T) {
if code := cmd.Run([]string{"-address=nope", "foo"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error listing jobs") {
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying job prefix") {
t.Fatalf("expected failed query error, got: %s", out)
}
ui.ErrorWriter.Reset()
Expand Down
18 changes: 4 additions & 14 deletions command/job_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,24 +117,14 @@ func (c *JobInspectCommand) Run(args []string) int {
c.Ui.Error(commandErrorText(c))
return 1
}
jobID := strings.TrimSpace(args[0])

// Check if the job exists
jobs, _, err := client.Jobs().PrefixList(jobID)
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, nil)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error inspecting job: %s", err))
return 1
}
if len(jobs) == 0 {
c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID))
c.Ui.Error(err.Error())
return 1
}
if len(jobs) > 1 {
if (jobID != jobs[0].ID) || (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
}
}

var version *uint64
if versionStr != "" {
Expand All @@ -148,7 +138,7 @@ func (c *JobInspectCommand) Run(args []string) int {
}

// Prefix lookup matched a single job
job, err := getJob(client, jobs[0].JobSummary.Namespace, jobs[0].ID, version)
job, err := getJob(client, namespace, jobID, version)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error inspecting job: %s", err))
return 1
Expand Down
4 changes: 2 additions & 2 deletions command/job_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestInspectCommand_Fails(t *testing.T) {
if code := cmd.Run([]string{"-address=" + url, "nope"}); code != 1 {
t.Fatalf("expect exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No job(s) with prefix or id") {
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No job(s) with prefix or ID") {
t.Fatalf("expect not found error, got: %s", out)
}
ui.ErrorWriter.Reset()
Expand All @@ -47,7 +47,7 @@ func TestInspectCommand_Fails(t *testing.T) {
if code := cmd.Run([]string{"-address=nope", "nope"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error inspecting job") {
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying job prefix") {
t.Fatalf("expected failed query error, got: %s", out)
}
ui.ErrorWriter.Reset()
Expand Down
31 changes: 10 additions & 21 deletions command/job_periodic_force.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package command

import (
"errors"
"fmt"
"strings"

Expand Down Expand Up @@ -112,31 +113,19 @@ func (c *JobPeriodicForceCommand) Run(args []string) int {
}

// Check if the job exists
jobID := args[0]
jobs, _, err := client.Jobs().PrefixList(jobID)
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, func(j *api.JobListStub) bool {
return j.Periodic
})
if err != nil {
c.Ui.Error(fmt.Sprintf("Error forcing periodic job: %s", err))
return 1
}
// filter non-periodic jobs
periodicJobs := make([]*api.JobListStub, 0, len(jobs))
for _, j := range jobs {
if j.Periodic {
periodicJobs = append(periodicJobs, j)
var noPrefixErr *NoJobWithPrefixError
if errors.As(err, &noPrefixErr) {
err = fmt.Errorf("No periodic job(s) with prefix or ID %q found", jobIDPrefix)
}
}
if len(periodicJobs) == 0 {
c.Ui.Error(fmt.Sprintf("No periodic job(s) with prefix or id %q found", jobID))
return 1
}
// preriodicJobs is sorted by job ID
// so if there is a job whose ID is equal to jobID then it must be the first item
if len(periodicJobs) > 1 && periodicJobs[0].ID != jobID {
c.Ui.Error(fmt.Sprintf("Prefix matched multiple periodic jobs\n\n%s", createStatusListOutput(periodicJobs, c.allNamespaces())))
c.Ui.Error(err.Error())
return 1
}
jobID = periodicJobs[0].ID
q := &api.WriteOptions{Namespace: periodicJobs[0].JobSummary.Namespace}
q := &api.WriteOptions{Namespace: namespace}

// force the evaluation
evalID, _, err := client.Jobs().PeriodicForce(jobID, q)
Expand Down
2 changes: 1 addition & 1 deletion command/job_periodic_force_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestJobPeriodicForceCommand_Fails(t *testing.T) {
code = cmd.Run([]string{"-address=nope", "12"})
require.Equal(t, code, 1, "expected error")
out = ui.ErrorWriter.String()
require.Contains(t, out, "Error forcing periodic job", "expected force error")
require.Contains(t, out, "Error querying job prefix", "expected force error")
}

func TestJobPeriodicForceCommand_AutocompleteArgs(t *testing.T) {
Expand Down
21 changes: 5 additions & 16 deletions command/job_promote.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,24 +117,13 @@ func (c *JobPromoteCommand) Run(args []string) int {
}

// Check if the job exists
jobID := strings.TrimSpace(args[0])
jobs, _, err := client.Jobs().PrefixList(jobID)
jobIDPrefix := strings.TrimSpace(args[0])
jobID, namespace, err := c.JobIDByPrefix(client, jobIDPrefix, nil)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error promoting job: %s", err))
c.Ui.Error(err.Error())
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.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}
q := &api.QueryOptions{Namespace: namespace}

// Do a prefix lookup
deploy, _, err := client.Jobs().LatestDeployment(jobID, q)
Expand All @@ -148,7 +137,7 @@ func (c *JobPromoteCommand) Run(args []string) int {
return 1
}

wq := &api.WriteOptions{Namespace: jobs[0].JobSummary.Namespace}
wq := &api.WriteOptions{Namespace: namespace}
var u *api.DeploymentUpdateResponse
if len(groups) == 0 {
u, _, err = client.Deployments().PromoteAll(deploy.ID, wq)
Expand Down
2 changes: 1 addition & 1 deletion command/job_promote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestJobPromoteCommand_Fails(t *testing.T) {
if code := cmd.Run([]string{"-address=nope", "12"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error promoting") {
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying job prefix") {
t.Fatalf("expected failed to promote error, got: %s", out)
}
ui.ErrorWriter.Reset()
Expand Down
Loading