diff --git a/scheduler/generic_sched_test.go b/scheduler/generic_sched_test.go index e3d5e9a84..43853fd2e 100644 --- a/scheduler/generic_sched_test.go +++ b/scheduler/generic_sched_test.go @@ -1316,3 +1316,71 @@ func TestBatchSched_Run_FailedAlloc(t *testing.T) { h.AssertEvalStatus(t, structs.EvalStatusComplete) } + +func TestBatchSched_ReRun_SuccessfullyFinishedAlloc(t *testing.T) { + h := NewHarness(t) + + // Create two nodes, one that is drained and has a successfully finished + // alloc and a fresh undrained one + node := mock.Node() + node.Drain = true + node2 := mock.Node() + noErr(t, h.State.UpsertNode(h.NextIndex(), node)) + noErr(t, h.State.UpsertNode(h.NextIndex(), node2)) + + // Create a job + job := mock.Job() + job.Type = structs.JobTypeBatch + job.TaskGroups[0].Count = 1 + noErr(t, h.State.UpsertJob(h.NextIndex(), job)) + + // Create a successful alloc + alloc := mock.Alloc() + alloc.Job = job + alloc.JobID = job.ID + alloc.NodeID = node.ID + alloc.Name = "my-job.web[0]" + alloc.ClientStatus = structs.AllocClientStatusComplete + alloc.TaskStates = map[string]*structs.TaskState{ + "web": &structs.TaskState{ + State: structs.TaskStateDead, + Events: []*structs.TaskEvent{ + { + Type: structs.TaskTerminated, + ExitCode: 0, + }, + }, + }, + } + noErr(t, h.State.UpsertAllocs(h.NextIndex(), []*structs.Allocation{alloc})) + + // Create a mock evaluation to rerun the job + eval := &structs.Evaluation{ + ID: structs.GenerateUUID(), + Priority: job.Priority, + TriggeredBy: structs.EvalTriggerJobRegister, + JobID: job.ID, + } + + // Process the evaluation + err := h.Process(NewBatchScheduler, eval) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Ensure no plan + if len(h.Plans) != 0 { + t.Fatalf("bad: %#v", h.Plans) + } + + // Lookup the allocations by JobID + out, err := h.State.AllocsByJob(job.ID) + noErr(t, err) + + // Ensure no replacement alloc was placed. + if len(out) != 1 { + t.Fatalf("bad: %#v", out) + } + + h.AssertEvalStatus(t, structs.EvalStatusComplete) +} diff --git a/scheduler/util.go b/scheduler/util.go index 9c3035ee4..e910f3db1 100644 --- a/scheduler/util.go +++ b/scheduler/util.go @@ -84,13 +84,14 @@ func diffAllocs(job *structs.Job, taintedNodes map[string]bool, // If we are on a tainted node, we must migrate if we are a service or // if the batch allocation did not finish if taintedNodes[exist.NodeID] { - if exist.Job.Type != structs.JobTypeBatch || !exist.RanSuccessfully() { - result.migrate = append(result.migrate, allocTuple{ - Name: name, - TaskGroup: tg, - Alloc: exist, - }) + if exist.Job.Type == structs.JobTypeBatch && exist.RanSuccessfully() { + goto IGNORE } + result.migrate = append(result.migrate, allocTuple{ + Name: name, + TaskGroup: tg, + Alloc: exist, + }) continue } @@ -105,6 +106,7 @@ func diffAllocs(job *structs.Job, taintedNodes map[string]bool, } // Everything is up-to-date + IGNORE: result.ignore = append(result.ignore, allocTuple{ Name: name, TaskGroup: tg, diff --git a/scheduler/util_test.go b/scheduler/util_test.go index 1d689fe2b..57e2cb482 100644 --- a/scheduler/util_test.go +++ b/scheduler/util_test.go @@ -74,6 +74,7 @@ func TestDiffAllocs(t *testing.T) { ID: structs.GenerateUUID(), NodeID: "zip", Name: "my-job.web[10]", + Job: oldJob, }, // Migrate the 3rd @@ -81,6 +82,7 @@ func TestDiffAllocs(t *testing.T) { ID: structs.GenerateUUID(), NodeID: "dead", Name: "my-job.web[2]", + Job: oldJob, }, } @@ -155,6 +157,7 @@ func TestDiffSystemAllocs(t *testing.T) { ID: structs.GenerateUUID(), NodeID: "dead", Name: "my-job.web[0]", + Job: oldJob, }, }