diff --git a/api/jobs.go b/api/jobs.go index 3d8b8a4df..a248f4519 100644 --- a/api/jobs.go +++ b/api/jobs.go @@ -310,11 +310,12 @@ type JobPlanRequest struct { } type JobPlanResponse struct { - JobModifyIndex uint64 - CreatedEvals []*Evaluation - Diff *JobDiff - Annotations *PlanAnnotations - FailedTGAllocs map[string]*AllocationMetric + JobModifyIndex uint64 + CreatedEvals []*Evaluation + Diff *JobDiff + Annotations *PlanAnnotations + FailedTGAllocs map[string]*AllocationMetric + NextPeriodicLaunch time.Time } type JobDiff struct { diff --git a/command/helpers.go b/command/helpers.go index 430868c43..487675278 100644 --- a/command/helpers.go +++ b/command/helpers.go @@ -46,7 +46,7 @@ func limit(s string, length int) string { // formatTime formats the time to string based on RFC822 func formatTime(t time.Time) string { - return t.Format("02/01/06 15:04:05 MST") + return t.Format("01/02/06 15:04:05 MST") } // getLocalNodeID returns the node ID of the local Nomad Client and an error if diff --git a/command/plan.go b/command/plan.go index e17bd4622..872ced884 100644 --- a/command/plan.go +++ b/command/plan.go @@ -139,7 +139,7 @@ func (c *PlanCommand) Run(args []string) int { // Print the scheduler dry-run output c.Ui.Output(c.Colorize().Color("[bold]Scheduler dry-run:[reset]")) - c.Ui.Output(c.Colorize().Color(formatDryRun(resp.FailedTGAllocs, resp.CreatedEvals))) + c.Ui.Output(c.Colorize().Color(formatDryRun(resp))) c.Ui.Output("") // Print the job index info @@ -156,22 +156,22 @@ func formatJobModifyIndex(jobModifyIndex uint64, jobName string) string { } // formatDryRun produces a string explaining the results of the dry run. -func formatDryRun(failedTGAllocs map[string]*api.AllocationMetric, evals []*api.Evaluation) string { +func formatDryRun(resp *api.JobPlanResponse) string { var rolling *api.Evaluation - for _, eval := range evals { + for _, eval := range resp.CreatedEvals { if eval.TriggeredBy == "rolling-update" { rolling = eval } } var out string - if len(failedTGAllocs) == 0 { + if len(resp.FailedTGAllocs) == 0 { out = "[bold][green]- All tasks successfully allocated.[reset]\n" } else { out = "[bold][yellow]- WARNING: Failed to place all allocations.[reset]\n" - sorted := sortedTaskGroupFromMetrics(failedTGAllocs) + sorted := sortedTaskGroupFromMetrics(resp.FailedTGAllocs) for _, tg := range sorted { - metrics := failedTGAllocs[tg] + metrics := resp.FailedTGAllocs[tg] noun := "allocation" if metrics.CoalescedFailures > 0 { @@ -189,6 +189,10 @@ func formatDryRun(failedTGAllocs map[string]*api.AllocationMetric, evals []*api. out += fmt.Sprintf("[green]- Rolling update, next evaluation will be in %s.\n", rolling.Wait) } + if !resp.NextPeriodicLaunch.IsZero() { + out += fmt.Sprintf("[green]- If submitted now, next periodic launch would be at %s.\n", formatTime(resp.NextPeriodicLaunch)) + } + out = strings.TrimSuffix(out, "\n") return out } diff --git a/command/run.go b/command/run.go index 033ded3ce..b27edfd00 100644 --- a/command/run.go +++ b/command/run.go @@ -193,7 +193,7 @@ func (c *RunCommand) Run(args []string) int { if detach || periodic { c.Ui.Output("Job registration successful") if periodic { - c.Ui.Output(fmt.Sprintf("Approximate next launch time: %v", job.Periodic.Next(time.Now().UTC()))) + c.Ui.Output(fmt.Sprintf("Approximate next launch time: %v", formatTime(job.Periodic.Next(time.Now().UTC())))) } else { c.Ui.Output("Evaluation ID: " + evalID) } diff --git a/command/status.go b/command/status.go index 92e0f543f..6b4fc445e 100644 --- a/command/status.go +++ b/command/status.go @@ -164,8 +164,8 @@ func (c *StatusCommand) Run(args []string) int { } if periodic { - basic = append(basic, fmt.Sprintf("Next Periodic Launch|%v", - sJob.Periodic.Next(time.Now().UTC()))) + basic = append(basic, fmt.Sprintf("Next Periodic Launch|%s", + formatTime(sJob.Periodic.Next(time.Now().UTC())))) } c.Ui.Output(formatKV(basic)) diff --git a/nomad/job_endpoint.go b/nomad/job_endpoint.go index 473f39584..c66a3d01c 100644 --- a/nomad/job_endpoint.go +++ b/nomad/job_endpoint.go @@ -511,6 +511,11 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse) } updatedEval := planner.Evals[0] + // If it is a periodic job calculate the next launch + if args.Job.IsPeriodic() && args.Job.Periodic.Enabled { + reply.NextPeriodicLaunch = args.Job.Periodic.Next(time.Now().UTC()) + } + reply.FailedTGAllocs = updatedEval.FailedTGAllocs reply.JobModifyIndex = index reply.Annotations = annotations diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index cb97d2908..b4cc1a237 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -468,6 +468,10 @@ type JobPlanResponse struct { // causes an in-place update or create/destroy Diff *JobDiff + // NextPeriodicLaunch is the time duration till the job would be launched if + // submitted. + NextPeriodicLaunch time.Time + WriteMeta }