Show preemption output in plan CLI
This commit is contained in:
parent
fcb06c9982
commit
5f0a9d2cfd
|
@ -133,8 +133,10 @@ type AllocationListStub struct {
|
|||
ID string
|
||||
EvalID string
|
||||
Name string
|
||||
Namespace string
|
||||
NodeID string
|
||||
JobID string
|
||||
JobType string
|
||||
JobVersion uint64
|
||||
TaskGroup string
|
||||
DesiredStatus string
|
||||
|
|
|
@ -20,6 +20,7 @@ When running the job with the check-index flag, the job will only be run if the
|
|||
server side version matches the job modify index returned. If the index has
|
||||
changed, another user has modified the job and the plan's results are
|
||||
potentially invalid.`
|
||||
preemptionShowByJobIdThreshold = 10
|
||||
)
|
||||
|
||||
type JobPlanCommand struct {
|
||||
|
@ -173,11 +174,74 @@ func (c *JobPlanCommand) Run(args []string) int {
|
|||
c.Colorize().Color(fmt.Sprintf("[bold][yellow]Job Warnings:\n%s[reset]\n", resp.Warnings)))
|
||||
}
|
||||
|
||||
// Print preemptions if there are any
|
||||
if resp.Annotations != nil && len(resp.Annotations.PreemptedAllocs) > 0 {
|
||||
c.addPreemptions(resp)
|
||||
}
|
||||
|
||||
// Print the job index info
|
||||
c.Ui.Output(c.Colorize().Color(formatJobModifyIndex(resp.JobModifyIndex, path)))
|
||||
return getExitCode(resp)
|
||||
}
|
||||
|
||||
// addPreemptions shows details about preempted allocations
|
||||
func (c *JobPlanCommand) addPreemptions(resp *api.JobPlanResponse) {
|
||||
c.Ui.Output(c.Colorize().Color("[bold][yellow]Preemptions:\n[reset]"))
|
||||
if len(resp.Annotations.PreemptedAllocs) < preemptionShowByJobIdThreshold {
|
||||
var allocs []string
|
||||
allocs = append(allocs, fmt.Sprintf("Alloc ID|Job ID|Task Group"))
|
||||
for _, alloc := range resp.Annotations.PreemptedAllocs {
|
||||
allocs = append(allocs, fmt.Sprintf("%s|%s|%s", alloc.ID, alloc.JobID, alloc.TaskGroup))
|
||||
}
|
||||
c.Ui.Output(formatList(allocs))
|
||||
} else {
|
||||
// Display in a summary format if the list is too large
|
||||
// Group by job type and job ids
|
||||
allocDetails := make(map[string]map[namespaceIdPair]int)
|
||||
numJobs := 0
|
||||
for _, alloc := range resp.Annotations.PreemptedAllocs {
|
||||
id := namespaceIdPair{alloc.JobID, alloc.Namespace}
|
||||
countMap := allocDetails[alloc.JobType]
|
||||
if countMap == nil {
|
||||
countMap = make(map[namespaceIdPair]int)
|
||||
}
|
||||
cnt, ok := countMap[id]
|
||||
if !ok {
|
||||
// First time we are seeing this job, increment counter
|
||||
numJobs++
|
||||
}
|
||||
countMap[id] = cnt + 1
|
||||
allocDetails[alloc.JobType] = countMap
|
||||
}
|
||||
var output []string
|
||||
// Show counts grouped by job ID if its less than a threshold
|
||||
if numJobs < preemptionShowByJobIdThreshold {
|
||||
output = append(output, fmt.Sprintf("Job ID|Namespace|Job Type|Preemptions"))
|
||||
for jobType, jobCounts := range allocDetails {
|
||||
for jobId, count := range jobCounts {
|
||||
output = append(output, fmt.Sprintf("%s|%s|%s|%d", jobId.id, jobId.namespace, jobType, count))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Show counts grouped by job type
|
||||
output = append(output, fmt.Sprintf("Job Type|Preemptions"))
|
||||
for jobType, jobCounts := range allocDetails {
|
||||
total := 0
|
||||
for _, count := range jobCounts {
|
||||
total += count
|
||||
}
|
||||
output = append(output, fmt.Sprintf("%s|%d", jobType, total))
|
||||
}
|
||||
}
|
||||
c.Ui.Output(formatList(output))
|
||||
}
|
||||
}
|
||||
|
||||
type namespaceIdPair struct {
|
||||
id string
|
||||
namespace string
|
||||
}
|
||||
|
||||
// getExitCode returns 0:
|
||||
// * 0: No allocations created or destroyed.
|
||||
// * 1: Allocations created or destroyed.
|
||||
|
|
|
@ -7,8 +7,12 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/testutil"
|
||||
"github.com/mitchellh/cli"
|
||||
require2 "github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPlanCommand_Implements(t *testing.T) {
|
||||
|
@ -169,3 +173,86 @@ func TestPlanCommand_From_URL(t *testing.T) {
|
|||
t.Fatalf("expected error getting jobfile, got: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanCommad_Preemptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
ui := new(cli.MockUi)
|
||||
cmd := &JobPlanCommand{Meta: Meta{Ui: ui}}
|
||||
require := require2.New(t)
|
||||
|
||||
// Only one preempted alloc
|
||||
resp1 := &api.JobPlanResponse{
|
||||
Annotations: &api.PlanAnnotations{
|
||||
PreemptedAllocs: []*api.AllocationListStub{
|
||||
{
|
||||
ID: "alloc1",
|
||||
JobID: "jobID1",
|
||||
TaskGroup: "meta",
|
||||
JobType: "batch",
|
||||
Namespace: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cmd.addPreemptions(resp1)
|
||||
out := ui.OutputWriter.String()
|
||||
require.Contains(out, "Alloc ID")
|
||||
require.Contains(out, "alloc1")
|
||||
|
||||
// Less than 10 unique job ids
|
||||
var preemptedAllocs []*api.AllocationListStub
|
||||
for i := 0; i < 12; i++ {
|
||||
job_id := "job" + strconv.Itoa(i%4)
|
||||
alloc := &api.AllocationListStub{
|
||||
ID: "alloc",
|
||||
JobID: job_id,
|
||||
TaskGroup: "meta",
|
||||
JobType: "batch",
|
||||
Namespace: "test",
|
||||
}
|
||||
preemptedAllocs = append(preemptedAllocs, alloc)
|
||||
}
|
||||
|
||||
resp2 := &api.JobPlanResponse{
|
||||
Annotations: &api.PlanAnnotations{
|
||||
PreemptedAllocs: preemptedAllocs,
|
||||
},
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
cmd.addPreemptions(resp2)
|
||||
out = ui.OutputWriter.String()
|
||||
require.Contains(out, "Job ID")
|
||||
require.Contains(out, "Namespace")
|
||||
|
||||
// More than 10 unique job IDs
|
||||
preemptedAllocs = make([]*api.AllocationListStub, 0)
|
||||
job_type := "batch"
|
||||
for i := 0; i < 20; i++ {
|
||||
job_id := "job" + strconv.Itoa(i)
|
||||
if i%2 == 0 {
|
||||
job_type = "service"
|
||||
} else {
|
||||
job_type = "batch"
|
||||
}
|
||||
alloc := &api.AllocationListStub{
|
||||
ID: "alloc",
|
||||
JobID: job_id,
|
||||
TaskGroup: "meta",
|
||||
JobType: job_type,
|
||||
Namespace: "test",
|
||||
}
|
||||
preemptedAllocs = append(preemptedAllocs, alloc)
|
||||
}
|
||||
|
||||
resp3 := &api.JobPlanResponse{
|
||||
Annotations: &api.PlanAnnotations{
|
||||
PreemptedAllocs: preemptedAllocs,
|
||||
},
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
cmd.addPreemptions(resp3)
|
||||
out = ui.OutputWriter.String()
|
||||
require.Contains(out, "Job Type")
|
||||
require.Contains(out, "batch")
|
||||
require.Contains(out, "service")
|
||||
}
|
||||
|
|
|
@ -7538,8 +7538,10 @@ func (a *Allocation) Stub() *AllocListStub {
|
|||
ID: a.ID,
|
||||
EvalID: a.EvalID,
|
||||
Name: a.Name,
|
||||
Namespace: a.Namespace,
|
||||
NodeID: a.NodeID,
|
||||
JobID: a.JobID,
|
||||
JobType: a.Job.Type,
|
||||
JobVersion: a.Job.Version,
|
||||
TaskGroup: a.TaskGroup,
|
||||
DesiredStatus: a.DesiredStatus,
|
||||
|
@ -7563,8 +7565,10 @@ type AllocListStub struct {
|
|||
ID string
|
||||
EvalID string
|
||||
Name string
|
||||
Namespace string
|
||||
NodeID string
|
||||
JobID string
|
||||
JobType string
|
||||
JobVersion uint64
|
||||
TaskGroup string
|
||||
DesiredStatus string
|
||||
|
|
Loading…
Reference in New Issue