Skip to content

Commit d41e5fa

Browse files
committed
add create and modify timestamps to evaluations (#5881)
1 parent d5e429d commit d41e5fa

15 files changed

+148
-7
lines changed

api/evaluations.go

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ type Evaluation struct {
8080
SnapshotIndex uint64
8181
CreateIndex uint64
8282
ModifyIndex uint64
83+
CreateTime int64
84+
ModifyTime int64
8385
}
8486

8587
// EvalIndexSort is a wrapper to sort evaluations by CreateIndex.

command/eval_status.go

+13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"sort"
66
"strings"
7+
"time"
78

89
"github.com/hashicorp/nomad/api"
910
"github.com/hashicorp/nomad/api/contexts"
@@ -203,9 +204,21 @@ func (c *EvalStatusCommand) Run(args []string) int {
203204
statusDesc = eval.Status
204205
}
205206

207+
// Format eval timestamps
208+
var formattedCreateTime, formattedModifyTime string
209+
if verbose {
210+
formattedCreateTime = formatUnixNanoTime(eval.CreateTime)
211+
formattedModifyTime = formatUnixNanoTime(eval.ModifyTime)
212+
} else {
213+
formattedCreateTime = prettyTimeDiff(time.Unix(0, eval.CreateTime), time.Now())
214+
formattedModifyTime = prettyTimeDiff(time.Unix(0, eval.ModifyTime), time.Now())
215+
}
216+
206217
// Format the evaluation data
207218
basic := []string{
208219
fmt.Sprintf("ID|%s", limit(eval.ID, length)),
220+
fmt.Sprintf("Create Time|%s", formattedCreateTime),
221+
fmt.Sprintf("Modify Time|%s", formattedModifyTime),
209222
fmt.Sprintf("Status|%s", eval.Status),
210223
fmt.Sprintf("Status Description|%s", statusDesc),
211224
fmt.Sprintf("Type|%s", eval.Type),

nomad/alloc_endpoint.go

+3
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ func (a *Alloc) Stop(args *structs.AllocStopRequest, reply *structs.AllocStopRes
235235
return fmt.Errorf(structs.ErrUnknownAllocationPrefix)
236236
}
237237

238+
now := time.Now().UTC().UnixNano()
238239
eval := &structs.Evaluation{
239240
ID: uuid.Generate(),
240241
Namespace: alloc.Namespace,
@@ -244,6 +245,8 @@ func (a *Alloc) Stop(args *structs.AllocStopRequest, reply *structs.AllocStopRes
244245
JobID: alloc.Job.ID,
245246
JobModifyIndex: alloc.Job.ModifyIndex,
246247
Status: structs.EvalStatusPending,
248+
CreateTime: now,
249+
ModifyTime: now,
247250
}
248251

249252
transitionReq := &structs.AllocUpdateDesiredTransitionRequest{

nomad/deploymentwatcher/deployment_watcher.go

+3
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,7 @@ func (w *deploymentWatcher) createBatchedUpdate(allowReplacements []string, forI
791791

792792
// getEval returns an evaluation suitable for the deployment
793793
func (w *deploymentWatcher) getEval() *structs.Evaluation {
794+
now := time.Now().UTC().UnixNano()
794795
return &structs.Evaluation{
795796
ID: uuid.Generate(),
796797
Namespace: w.j.Namespace,
@@ -800,6 +801,8 @@ func (w *deploymentWatcher) getEval() *structs.Evaluation {
800801
JobID: w.j.ID,
801802
DeploymentID: w.deploymentID,
802803
Status: structs.EvalStatusPending,
804+
CreateTime: now,
805+
ModifyTime: now,
803806
}
804807
}
805808

nomad/drainer/drainer.go

+3
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ func (n *NodeDrainer) drainAllocs(future *structs.BatchFuture, allocs []*structs
402402
}
403403

404404
evals := make([]*structs.Evaluation, 0, len(jobs))
405+
now := time.Now().UTC().UnixNano()
405406
for job, alloc := range jobs {
406407
evals = append(evals, &structs.Evaluation{
407408
ID: uuid.Generate(),
@@ -411,6 +412,8 @@ func (n *NodeDrainer) drainAllocs(future *structs.BatchFuture, allocs []*structs
411412
TriggeredBy: structs.EvalTriggerNodeDrain,
412413
JobID: job,
413414
Status: structs.EvalStatusPending,
415+
CreateTime: now,
416+
ModifyTime: now,
414417
})
415418
}
416419

nomad/job_endpoint.go

+19
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
208208
}
209209

210210
// Create a new evaluation
211+
now := time.Now().UTC().UnixNano()
211212
eval := &structs.Evaluation{
212213
ID: uuid.Generate(),
213214
Namespace: args.RequestNamespace(),
@@ -217,6 +218,8 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
217218
JobID: args.Job.ID,
218219
JobModifyIndex: reply.JobModifyIndex,
219220
Status: structs.EvalStatusPending,
221+
CreateTime: now,
222+
ModifyTime: now,
220223
}
221224
update := &structs.EvalUpdateRequest{
222225
Evals: []*structs.Evaluation{eval},
@@ -571,6 +574,7 @@ func (j *Job) Evaluate(args *structs.JobEvaluateRequest, reply *structs.JobRegis
571574
}
572575

573576
// Create a new evaluation
577+
now := time.Now().UTC().UnixNano()
574578
eval := &structs.Evaluation{
575579
ID: uuid.Generate(),
576580
Namespace: args.RequestNamespace(),
@@ -580,6 +584,8 @@ func (j *Job) Evaluate(args *structs.JobEvaluateRequest, reply *structs.JobRegis
580584
JobID: job.ID,
581585
JobModifyIndex: job.ModifyIndex,
582586
Status: structs.EvalStatusPending,
587+
CreateTime: now,
588+
ModifyTime: now,
583589
}
584590

585591
// Create a AllocUpdateDesiredTransitionRequest request with the eval and any forced rescheduled allocs
@@ -650,6 +656,7 @@ func (j *Job) Deregister(args *structs.JobDeregisterRequest, reply *structs.JobD
650656
// Create a new evaluation
651657
// XXX: The job priority / type is strange for this, since it's not a high
652658
// priority even if the job was.
659+
now := time.Now().UTC().UnixNano()
653660
eval := &structs.Evaluation{
654661
ID: uuid.Generate(),
655662
Namespace: args.RequestNamespace(),
@@ -659,6 +666,8 @@ func (j *Job) Deregister(args *structs.JobDeregisterRequest, reply *structs.JobD
659666
JobID: args.JobID,
660667
JobModifyIndex: index,
661668
Status: structs.EvalStatusPending,
669+
CreateTime: now,
670+
ModifyTime: now,
662671
}
663672
update := &structs.EvalUpdateRequest{
664673
Evals: []*structs.Evaluation{eval},
@@ -738,6 +747,7 @@ func (j *Job) BatchDeregister(args *structs.JobBatchDeregisterRequest, reply *st
738747
}
739748

740749
// Create a new evaluation
750+
now := time.Now().UTC().UnixNano()
741751
eval := &structs.Evaluation{
742752
ID: uuid.Generate(),
743753
Namespace: jobNS.Namespace,
@@ -746,6 +756,8 @@ func (j *Job) BatchDeregister(args *structs.JobBatchDeregisterRequest, reply *st
746756
TriggeredBy: structs.EvalTriggerJobDeregister,
747757
JobID: jobNS.ID,
748758
Status: structs.EvalStatusPending,
759+
CreateTime: now,
760+
ModifyTime: now,
749761
}
750762
args.Evals = append(args.Evals, eval)
751763
}
@@ -1195,6 +1207,7 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse)
11951207
}
11961208

11971209
// Create an eval and mark it as requiring annotations and insert that as well
1210+
now := time.Now().UTC().UnixNano()
11981211
eval := &structs.Evaluation{
11991212
ID: uuid.Generate(),
12001213
Namespace: args.RequestNamespace(),
@@ -1205,6 +1218,9 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse)
12051218
JobModifyIndex: updatedIndex,
12061219
Status: structs.EvalStatusPending,
12071220
AnnotatePlan: true,
1221+
// Timestamps are added for consistency but this eval is never persisted
1222+
CreateTime: now,
1223+
ModifyTime: now,
12081224
}
12091225

12101226
snap.UpsertEvals(100, []*structs.Evaluation{eval})
@@ -1415,6 +1431,7 @@ func (j *Job) Dispatch(args *structs.JobDispatchRequest, reply *structs.JobDispa
14151431
// If the job is periodic, we don't create an eval.
14161432
if !dispatchJob.IsPeriodic() {
14171433
// Create a new evaluation
1434+
now := time.Now().UTC().UnixNano()
14181435
eval := &structs.Evaluation{
14191436
ID: uuid.Generate(),
14201437
Namespace: args.RequestNamespace(),
@@ -1424,6 +1441,8 @@ func (j *Job) Dispatch(args *structs.JobDispatchRequest, reply *structs.JobDispa
14241441
JobID: dispatchJob.ID,
14251442
JobModifyIndex: jobCreateIndex,
14261443
Status: structs.EvalStatusPending,
1444+
CreateTime: now,
1445+
ModifyTime: now,
14271446
}
14281447
update := &structs.EvalUpdateRequest{
14291448
Evals: []*structs.Evaluation{eval},

nomad/job_endpoint_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ func TestJobEndpoint_Register(t *testing.T) {
9696
if eval.Status != structs.EvalStatusPending {
9797
t.Fatalf("bad: %#v", eval)
9898
}
99+
if eval.CreateTime == 0 {
100+
t.Fatalf("eval CreateTime is unset: %#v", eval)
101+
}
102+
if eval.ModifyTime == 0 {
103+
t.Fatalf("eval ModifyTime is unset: %#v", eval)
104+
}
99105
}
100106

101107
func TestJobEndpoint_Register_ACL(t *testing.T) {
@@ -302,6 +308,12 @@ func TestJobEndpoint_Register_Existing(t *testing.T) {
302308
if eval.Status != structs.EvalStatusPending {
303309
t.Fatalf("bad: %#v", eval)
304310
}
311+
if eval.CreateTime == 0 {
312+
t.Fatalf("eval CreateTime is unset: %#v", eval)
313+
}
314+
if eval.ModifyTime == 0 {
315+
t.Fatalf("eval ModifyTime is unset: %#v", eval)
316+
}
305317

306318
if err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp); err != nil {
307319
t.Fatalf("err: %v", err)
@@ -1500,6 +1512,12 @@ func TestJobEndpoint_Evaluate(t *testing.T) {
15001512
if eval.Status != structs.EvalStatusPending {
15011513
t.Fatalf("bad: %#v", eval)
15021514
}
1515+
if eval.CreateTime == 0 {
1516+
t.Fatalf("eval CreateTime is unset: %#v", eval)
1517+
}
1518+
if eval.ModifyTime == 0 {
1519+
t.Fatalf("eval ModifyTime is unset: %#v", eval)
1520+
}
15031521
}
15041522

15051523
func TestJobEndpoint_ForceRescheduleEvaluate(t *testing.T) {
@@ -1569,6 +1587,8 @@ func TestJobEndpoint_ForceRescheduleEvaluate(t *testing.T) {
15691587
require.Equal(eval.JobID, job.ID)
15701588
require.Equal(eval.JobModifyIndex, resp.JobModifyIndex)
15711589
require.Equal(eval.Status, structs.EvalStatusPending)
1590+
require.NotZero(eval.CreateTime)
1591+
require.NotZero(eval.ModifyTime)
15721592

15731593
// Lookup the alloc, verify DesiredTransition ForceReschedule
15741594
alloc, err = state.AllocByID(ws, alloc.ID)
@@ -1647,6 +1667,8 @@ func TestJobEndpoint_Evaluate_ACL(t *testing.T) {
16471667
require.Equal(eval.JobID, job.ID)
16481668
require.Equal(eval.JobModifyIndex, validResp2.JobModifyIndex)
16491669
require.Equal(eval.Status, structs.EvalStatusPending)
1670+
require.NotZero(eval.CreateTime)
1671+
require.NotZero(eval.ModifyTime)
16501672
}
16511673

16521674
func TestJobEndpoint_Evaluate_Periodic(t *testing.T) {
@@ -1790,6 +1812,8 @@ func TestJobEndpoint_Deregister(t *testing.T) {
17901812
require.Equal(structs.EvalTriggerJobDeregister, eval.TriggeredBy)
17911813
require.Equal(job.ID, eval.JobID)
17921814
require.Equal(structs.EvalStatusPending, eval.Status)
1815+
require.NotZero(eval.CreateTime)
1816+
require.NotZero(eval.ModifyTime)
17931817

17941818
// Deregister and purge
17951819
dereg2 := &structs.JobDeregisterRequest{
@@ -1820,6 +1844,8 @@ func TestJobEndpoint_Deregister(t *testing.T) {
18201844
require.Equal(structs.EvalTriggerJobDeregister, eval.TriggeredBy)
18211845
require.Equal(job.ID, eval.JobID)
18221846
require.Equal(structs.EvalStatusPending, eval.Status)
1847+
require.NotZero(eval.CreateTime)
1848+
require.NotZero(eval.ModifyTime)
18231849
}
18241850

18251851
func TestJobEndpoint_Deregister_ACL(t *testing.T) {
@@ -1899,6 +1925,8 @@ func TestJobEndpoint_Deregister_ACL(t *testing.T) {
18991925
require.Equal(eval.JobID, job.ID)
19001926
require.Equal(eval.JobModifyIndex, validResp2.JobModifyIndex)
19011927
require.Equal(eval.Status, structs.EvalStatusPending)
1928+
require.NotZero(eval.CreateTime)
1929+
require.NotZero(eval.ModifyTime)
19021930
}
19031931

19041932
func TestJobEndpoint_Deregister_Nonexistent(t *testing.T) {
@@ -1959,6 +1987,12 @@ func TestJobEndpoint_Deregister_Nonexistent(t *testing.T) {
19591987
if eval.Status != structs.EvalStatusPending {
19601988
t.Fatalf("bad: %#v", eval)
19611989
}
1990+
if eval.CreateTime == 0 {
1991+
t.Fatalf("eval CreateTime is unset: %#v", eval)
1992+
}
1993+
if eval.ModifyTime == 0 {
1994+
t.Fatalf("eval ModifyTime is unset: %#v", eval)
1995+
}
19621996
}
19631997

19641998
func TestJobEndpoint_Deregister_Periodic(t *testing.T) {
@@ -2165,6 +2199,8 @@ func TestJobEndpoint_BatchDeregister(t *testing.T) {
21652199
require.Equal(structs.EvalTriggerJobDeregister, eval.TriggeredBy)
21662200
require.Equal(expectedJob.ID, eval.JobID)
21672201
require.Equal(structs.EvalStatusPending, eval.Status)
2202+
require.NotZero(eval.CreateTime)
2203+
require.NotZero(eval.ModifyTime)
21682204
}
21692205
}
21702206

nomad/leader.go

+3
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,10 @@ func (s *Server) reapFailedEvaluations(stopCh chan struct{}) {
523523
// due to the fairly large backoff.
524524
followupEvalWait := s.config.EvalFailedFollowupBaselineDelay +
525525
time.Duration(rand.Int63n(int64(s.config.EvalFailedFollowupDelayRange)))
526+
526527
followupEval := eval.CreateFailedFollowUpEval(followupEvalWait)
527528
updateEval.NextEval = followupEval.ID
529+
updateEval.UpdateModifyTime()
528530

529531
// Update via Raft
530532
req := structs.EvalUpdateRequest{
@@ -561,6 +563,7 @@ func (s *Server) reapDupBlockedEvaluations(stopCh chan struct{}) {
561563
newEval := dup.Copy()
562564
newEval.Status = structs.EvalStatusCancelled
563565
newEval.StatusDescription = fmt.Sprintf("existing blocked evaluation exists for job %q", newEval.JobID)
566+
newEval.UpdateModifyTime()
564567
cancel[i] = newEval
565568
}
566569

nomad/mock/mock.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -402,13 +402,16 @@ func PeriodicJob() *structs.Job {
402402
}
403403

404404
func Eval() *structs.Evaluation {
405+
now := time.Now().UTC().UnixNano()
405406
eval := &structs.Evaluation{
406-
ID: uuid.Generate(),
407-
Namespace: structs.DefaultNamespace,
408-
Priority: 50,
409-
Type: structs.JobTypeService,
410-
JobID: uuid.Generate(),
411-
Status: structs.EvalStatusPending,
407+
ID: uuid.Generate(),
408+
Namespace: structs.DefaultNamespace,
409+
Priority: 50,
410+
Type: structs.JobTypeService,
411+
JobID: uuid.Generate(),
412+
Status: structs.EvalStatusPending,
413+
CreateTime: now,
414+
ModifyTime: now,
412415
}
413416
return eval
414417
}

nomad/node_endpoint.go

+10
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,8 @@ func (n *Node) UpdateAlloc(args *structs.AllocUpdateRequest, reply *structs.Gene
10771077
Type: job.Type,
10781078
Priority: job.Priority,
10791079
Status: structs.EvalStatusPending,
1080+
CreateTime: now.UTC().UnixNano(),
1081+
ModifyTime: now.UTC().UnixNano(),
10801082
}
10811083
evals = append(evals, eval)
10821084
}
@@ -1134,6 +1136,9 @@ func (n *Node) batchUpdate(future *structs.BatchFuture, updates []*structs.Alloc
11341136
}
11351137
_, exists := evalsByJobId[namespacedID]
11361138
if !exists {
1139+
now := time.Now().UTC().UnixNano()
1140+
eval.CreateTime = now
1141+
eval.ModifyTime = now
11371142
trimmedEvals = append(trimmedEvals, eval)
11381143
evalsByJobId[namespacedID] = struct{}{}
11391144
}
@@ -1281,6 +1286,7 @@ func (n *Node) createNodeEvals(nodeID string, nodeIndex uint64) ([]string, uint6
12811286
var evals []*structs.Evaluation
12821287
var evalIDs []string
12831288
jobIDs := make(map[string]struct{})
1289+
now := time.Now().UTC().UnixNano()
12841290

12851291
for _, alloc := range allocs {
12861292
// Deduplicate on JobID
@@ -1300,6 +1306,8 @@ func (n *Node) createNodeEvals(nodeID string, nodeIndex uint64) ([]string, uint6
13001306
NodeID: nodeID,
13011307
NodeModifyIndex: nodeIndex,
13021308
Status: structs.EvalStatusPending,
1309+
CreateTime: now,
1310+
ModifyTime: now,
13031311
}
13041312
evals = append(evals, eval)
13051313
evalIDs = append(evalIDs, eval.ID)
@@ -1324,6 +1332,8 @@ func (n *Node) createNodeEvals(nodeID string, nodeIndex uint64) ([]string, uint6
13241332
NodeID: nodeID,
13251333
NodeModifyIndex: nodeIndex,
13261334
Status: structs.EvalStatusPending,
1335+
CreateTime: now,
1336+
ModifyTime: now,
13271337
}
13281338
evals = append(evals, eval)
13291339
evalIDs = append(evalIDs, eval.ID)

0 commit comments

Comments
 (0)