From 231c0b9fda54ebf9107afd6e27c3c01a8e32fff4 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 27 Mar 2017 10:35:36 -0700 Subject: [PATCH] Fix periodic job state This PR fixes an issue in which a periodic job would incorrectly transistion to status dead. Fixes https://github.com/hashicorp/nomad/issues/2268 --- nomad/state/state_store.go | 21 +++++++++++-- nomad/state/state_store_test.go | 55 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/nomad/state/state_store.go b/nomad/state/state_store.go index bbee3f72083..a5235d3456e 100644 --- a/nomad/state/state_store.go +++ b/nomad/state/state_store.go @@ -878,7 +878,15 @@ func (s *StateStore) EvalsByJob(ws memdb.WatchSet, jobID string) ([]*structs.Eva if raw == nil { break } - out = append(out, raw.(*structs.Evaluation)) + + e := raw.(*structs.Evaluation) + + // Filter non-exact matches + if e.JobID != jobID { + continue + } + + out = append(out, e) } return out, nil } @@ -1619,9 +1627,16 @@ func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete b } hasEval := false - for eval := evals.Next(); eval != nil; eval = evals.Next() { + for raw := evals.Next(); raw != nil; raw = evals.Next() { + e := raw.(*structs.Evaluation) + + // Filter non-exact matches + if e.JobID != job.ID { + continue + } + hasEval = true - if !eval.(*structs.Evaluation).TerminalStatus() { + if !e.TerminalStatus() { return structs.JobStatusPending, nil } } diff --git a/nomad/state/state_store_test.go b/nomad/state/state_store_test.go index 5162e8f2430..f3d96c18b2e 100644 --- a/nomad/state/state_store_test.go +++ b/nomad/state/state_store_test.go @@ -1,6 +1,7 @@ package state import ( + "fmt" "os" "reflect" "sort" @@ -494,6 +495,60 @@ func TestStateStore_UpdateUpsertJob_Job(t *testing.T) { } } +func TestStateStore_UpdateUpsertJob_PeriodicJob(t *testing.T) { + state := testStateStore(t) + job := mock.PeriodicJob() + + // Create a watchset so we can test that upsert fires the watch + ws := memdb.NewWatchSet() + _, err := state.JobByID(ws, job.ID) + if err != nil { + t.Fatalf("bad: %v", err) + } + + if err := state.UpsertJob(1000, job); err != nil { + t.Fatalf("err: %v", err) + } + + // Create a child and an evaluation + job2 := job.Copy() + job2.Periodic = nil + job2.ID = fmt.Sprintf("%v/%s-1490635020", job.ID, structs.PeriodicLaunchSuffix) + err = state.UpsertJob(1001, job2) + if err != nil { + t.Fatalf("err: %v", err) + } + + eval := mock.Eval() + eval.JobID = job2.ID + err = state.UpsertEvals(1002, []*structs.Evaluation{eval}) + if err != nil { + t.Fatalf("err: %v", err) + } + + job3 := job.Copy() + job3.TaskGroups[0].Tasks[0].Name = "new name" + err = state.UpsertJob(1003, job3) + if err != nil { + t.Fatalf("err: %v", err) + } + + if !watchFired(ws) { + t.Fatalf("bad") + } + + ws = memdb.NewWatchSet() + out, err := state.JobByID(ws, job.ID) + if err != nil { + t.Fatalf("err: %v", err) + } + + if s, e := out.Status, structs.JobStatusRunning; s != e { + t.Fatalf("got status %v; want %v", s, e) + } + +} + // This test ensures that UpsertJob creates the EphemeralDisk is a job doesn't have // one and clear out the task's disk resource asks // COMPAT 0.4.1 -> 0.5