Children fixes + nomad status outputs summaries
Children object is always initialized instead of lazily. `nomad status` outputs children summaries and has specialized view for constructor jobs.
This commit is contained in:
parent
5a742528c9
commit
bf1e157bd8
14
api/jobs.go
14
api/jobs.go
|
@ -247,9 +247,17 @@ type JobSummary struct {
|
||||||
|
|
||||||
// JobChildrenSummary contains the summary of children job status
|
// JobChildrenSummary contains the summary of children job status
|
||||||
type JobChildrenSummary struct {
|
type JobChildrenSummary struct {
|
||||||
Pending uint64
|
Pending int64
|
||||||
Running uint64
|
Running int64
|
||||||
Dead uint64
|
Dead int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jc *JobChildrenSummary) Sum() int {
|
||||||
|
if jc == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(jc.Pending + jc.Running + jc.Dead)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskGroup summarizes the state of all the allocations of a particular
|
// TaskGroup summarizes the state of all the allocations of a particular
|
||||||
|
|
|
@ -127,13 +127,14 @@ func (c *StatusCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it is periodic
|
// Check if it is periodic or a constructor job
|
||||||
sJob, err := convertApiJob(job)
|
sJob, err := convertApiJob(job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
|
c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
periodic := sJob.IsPeriodic()
|
periodic := sJob.IsPeriodic()
|
||||||
|
constructor := sJob.IsConstructor()
|
||||||
|
|
||||||
// Format the job info
|
// Format the job info
|
||||||
basic := []string{
|
basic := []string{
|
||||||
|
@ -144,6 +145,7 @@ func (c *StatusCommand) Run(args []string) int {
|
||||||
fmt.Sprintf("Datacenters|%s", strings.Join(job.Datacenters, ",")),
|
fmt.Sprintf("Datacenters|%s", strings.Join(job.Datacenters, ",")),
|
||||||
fmt.Sprintf("Status|%s", job.Status),
|
fmt.Sprintf("Status|%s", job.Status),
|
||||||
fmt.Sprintf("Periodic|%v", periodic),
|
fmt.Sprintf("Periodic|%v", periodic),
|
||||||
|
fmt.Sprintf("Constructor|%v", constructor),
|
||||||
}
|
}
|
||||||
|
|
||||||
if periodic {
|
if periodic {
|
||||||
|
@ -167,14 +169,17 @@ func (c *StatusCommand) Run(args []string) int {
|
||||||
c.Ui.Error(err.Error())
|
c.Ui.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
} else if constructor {
|
||||||
return 0
|
if err := c.outputConstructorInfo(client, job); err != nil {
|
||||||
|
c.Ui.Error(err.Error())
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if err := c.outputJobInfo(client, job); err != nil {
|
if err := c.outputJobInfo(client, job); err != nil {
|
||||||
c.Ui.Error(err.Error())
|
c.Ui.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -182,6 +187,11 @@ func (c *StatusCommand) Run(args []string) int {
|
||||||
// outputPeriodicInfo prints information about the passed periodic job. If a
|
// outputPeriodicInfo prints information about the passed periodic job. If a
|
||||||
// request fails, an error is returned.
|
// request fails, an error is returned.
|
||||||
func (c *StatusCommand) outputPeriodicInfo(client *api.Client, job *api.Job) error {
|
func (c *StatusCommand) outputPeriodicInfo(client *api.Client, job *api.Job) error {
|
||||||
|
// Output the summary
|
||||||
|
if err := c.outputJobSummary(client, job); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the prefix that matches launched jobs from the periodic job.
|
// Generate the prefix that matches launched jobs from the periodic job.
|
||||||
prefix := fmt.Sprintf("%s%s", job.ID, structs.PeriodicLaunchSuffix)
|
prefix := fmt.Sprintf("%s%s", job.ID, structs.PeriodicLaunchSuffix)
|
||||||
children, _, err := client.Jobs().PrefixList(prefix)
|
children, _, err := client.Jobs().PrefixList(prefix)
|
||||||
|
@ -208,7 +218,55 @@ func (c *StatusCommand) outputPeriodicInfo(client *api.Client, job *api.Job) err
|
||||||
child.Status))
|
child.Status))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Ui.Output(fmt.Sprintf("\nPreviously launched jobs:\n%s", formatList(out)))
|
c.Ui.Output(c.Colorize().Color("\n[bold]Previously Launched Jobs[reset]"))
|
||||||
|
c.Ui.Output(formatList(out))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputConstructorInfo prints information about the passed constructor job. If a
|
||||||
|
// request fails, an error is returned.
|
||||||
|
func (c *StatusCommand) outputConstructorInfo(client *api.Client, job *api.Job) error {
|
||||||
|
// Output constructor details
|
||||||
|
c.Ui.Output(c.Colorize().Color("\n[bold]Constructor[reset]"))
|
||||||
|
constructor := make([]string, 3)
|
||||||
|
constructor[0] = fmt.Sprintf("Payload|%s", job.Constructor.Payload)
|
||||||
|
constructor[1] = fmt.Sprintf("Required Metadata|%v", strings.Join(job.Constructor.MetaRequired, ", "))
|
||||||
|
constructor[2] = fmt.Sprintf("Optional Metadata|%v", strings.Join(job.Constructor.MetaOptional, ", "))
|
||||||
|
c.Ui.Output(formatKV(constructor))
|
||||||
|
|
||||||
|
// Output the summary
|
||||||
|
if err := c.outputJobSummary(client, job); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the prefix that matches launched jobs from the periodic job.
|
||||||
|
prefix := fmt.Sprintf("%s%s", job.ID, structs.DispatchLaunchSuffic)
|
||||||
|
children, _, err := client.Jobs().PrefixList(prefix)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error querying job: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(children) == 0 {
|
||||||
|
c.Ui.Output("\nNo dispatched instances of constructor job found")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]string, 1)
|
||||||
|
out[0] = "ID|Status"
|
||||||
|
for _, child := range children {
|
||||||
|
// Ensure that we are only showing jobs whose parent is the requested
|
||||||
|
// job.
|
||||||
|
if child.ParentID != job.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, fmt.Sprintf("%s|%s",
|
||||||
|
child.ID,
|
||||||
|
child.Status))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Ui.Output(c.Colorize().Color("\n[bold]Dispatched Jobs[reset]"))
|
||||||
|
c.Ui.Output(formatList(out))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,31 +287,9 @@ func (c *StatusCommand) outputJobInfo(client *api.Client, job *api.Job) error {
|
||||||
return fmt.Errorf("Error querying job evaluations: %s", err)
|
return fmt.Errorf("Error querying job evaluations: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the summary
|
// Output the summary
|
||||||
summary, _, err := client.Jobs().Summary(job.ID, nil)
|
if err := c.outputJobSummary(client, job); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return fmt.Errorf("Error querying job summary: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the summary
|
|
||||||
c.Ui.Output(c.Colorize().Color("\n[bold]Summary[reset]"))
|
|
||||||
if summary != nil {
|
|
||||||
summaries := make([]string, len(summary.Summary)+1)
|
|
||||||
summaries[0] = "Task Group|Queued|Starting|Running|Failed|Complete|Lost"
|
|
||||||
taskGroups := make([]string, 0, len(summary.Summary))
|
|
||||||
for taskGroup := range summary.Summary {
|
|
||||||
taskGroups = append(taskGroups, taskGroup)
|
|
||||||
}
|
|
||||||
sort.Strings(taskGroups)
|
|
||||||
for idx, taskGroup := range taskGroups {
|
|
||||||
tgs := summary.Summary[taskGroup]
|
|
||||||
summaries[idx+1] = fmt.Sprintf("%s|%d|%d|%d|%d|%d|%d",
|
|
||||||
taskGroup, tgs.Queued, tgs.Starting,
|
|
||||||
tgs.Running, tgs.Failed,
|
|
||||||
tgs.Complete, tgs.Lost,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
c.Ui.Output(formatList(summaries))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine latest evaluation with failures whose follow up hasn't
|
// Determine latest evaluation with failures whose follow up hasn't
|
||||||
|
@ -320,6 +356,66 @@ func (c *StatusCommand) outputJobInfo(client *api.Client, job *api.Job) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// outputJobSummary displays the given jobs summary and children job summary
|
||||||
|
// where appropriate
|
||||||
|
func (c *StatusCommand) outputJobSummary(client *api.Client, job *api.Job) error {
|
||||||
|
// Query the summary
|
||||||
|
summary, _, err := client.Jobs().Summary(job.ID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error querying job summary: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if summary == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sJob, err := convertApiJob(job)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error converting job: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
periodic := sJob.IsPeriodic()
|
||||||
|
constructor := sJob.IsConstructor()
|
||||||
|
|
||||||
|
// Print the summary
|
||||||
|
if !periodic && !constructor {
|
||||||
|
c.Ui.Output(c.Colorize().Color("\n[bold]Summary[reset]"))
|
||||||
|
summaries := make([]string, len(summary.Summary)+1)
|
||||||
|
summaries[0] = "Task Group|Queued|Starting|Running|Failed|Complete|Lost"
|
||||||
|
taskGroups := make([]string, 0, len(summary.Summary))
|
||||||
|
for taskGroup := range summary.Summary {
|
||||||
|
taskGroups = append(taskGroups, taskGroup)
|
||||||
|
}
|
||||||
|
sort.Strings(taskGroups)
|
||||||
|
for idx, taskGroup := range taskGroups {
|
||||||
|
tgs := summary.Summary[taskGroup]
|
||||||
|
summaries[idx+1] = fmt.Sprintf("%s|%d|%d|%d|%d|%d|%d",
|
||||||
|
taskGroup, tgs.Queued, tgs.Starting,
|
||||||
|
tgs.Running, tgs.Failed,
|
||||||
|
tgs.Complete, tgs.Lost,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
c.Ui.Output(formatList(summaries))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always display the summary if we are periodic or a constructor job
|
||||||
|
// but only display if the summary is non-zero on normal jobs
|
||||||
|
if summary.Children != nil && (constructor || periodic || summary.Children.Sum() > 0) {
|
||||||
|
if constructor {
|
||||||
|
c.Ui.Output(c.Colorize().Color("\n[bold]Dispatched Job Summary[reset]"))
|
||||||
|
} else {
|
||||||
|
c.Ui.Output(c.Colorize().Color("\n[bold]Children Job Summary[reset]"))
|
||||||
|
}
|
||||||
|
summaries := make([]string, 2)
|
||||||
|
summaries[0] = "Pending|Running|Dead"
|
||||||
|
summaries[1] = fmt.Sprintf("%d|%d|%d",
|
||||||
|
summary.Children.Pending, summary.Children.Running, summary.Children.Dead)
|
||||||
|
c.Ui.Output(formatList(summaries))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *StatusCommand) outputFailedPlacements(failedEval *api.Evaluation) {
|
func (c *StatusCommand) outputFailedPlacements(failedEval *api.Evaluation) {
|
||||||
if failedEval == nil || len(failedEval.FailedTGAllocs) == 0 {
|
if failedEval == nil || len(failedEval.FailedTGAllocs) == 0 {
|
||||||
return
|
return
|
||||||
|
|
|
@ -818,6 +818,9 @@ func (j *Job) Dispatch(args *structs.JobDispatchRequest, reply *structs.JobDispa
|
||||||
|
|
||||||
// Merge in the meta data
|
// Merge in the meta data
|
||||||
for k, v := range args.Meta {
|
for k, v := range args.Meta {
|
||||||
|
if dispatchJob.Meta == nil {
|
||||||
|
dispatchJob.Meta = make(map[string]string, len(args.Meta))
|
||||||
|
}
|
||||||
dispatchJob.Meta[k] = v
|
dispatchJob.Meta[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1627,6 +1627,7 @@ func (s *StateStore) updateSummaryWithJob(index uint64, job *structs.Job,
|
||||||
existing = &structs.JobSummary{
|
existing = &structs.JobSummary{
|
||||||
JobID: job.ID,
|
JobID: job.ID,
|
||||||
Summary: make(map[string]structs.TaskGroupSummary),
|
Summary: make(map[string]structs.TaskGroupSummary),
|
||||||
|
Children: new(structs.JobChildrenSummary),
|
||||||
CreateIndex: index,
|
CreateIndex: index,
|
||||||
}
|
}
|
||||||
hasSummaryChanged = true
|
hasSummaryChanged = true
|
||||||
|
|
|
@ -1411,8 +1411,7 @@ type JobSummary struct {
|
||||||
// Summmary contains the summary per task group for the Job
|
// Summmary contains the summary per task group for the Job
|
||||||
Summary map[string]TaskGroupSummary
|
Summary map[string]TaskGroupSummary
|
||||||
|
|
||||||
// Children contains a summary for the children of this job. If there are no
|
// Children contains a summary for the children of this job.
|
||||||
// children the summary will be nil
|
|
||||||
Children *JobChildrenSummary
|
Children *JobChildrenSummary
|
||||||
|
|
||||||
// Raft Indexes
|
// Raft Indexes
|
||||||
|
|
Loading…
Reference in New Issue