diff --git a/nomad/core_sched.go b/nomad/core_sched.go index 5601dad09..6218eaf04 100644 --- a/nomad/core_sched.go +++ b/nomad/core_sched.go @@ -610,6 +610,12 @@ func allocGCEligible(a *structs.Allocation, job *structs.Job, gcTime time.Time, return true } + // If the allocation's desired state is Stop, it can be GCed even if it + // has failed and hasn't been rescheduled. This can happen during job updates + if a.DesiredStatus == structs.AllocDesiredStatusStop { + return true + } + // If the alloc hasn't failed then we don't need to consider it for rescheduling // Rescheduling needs to copy over information from the previous alloc so that it // can enforce the reschedule policy diff --git a/nomad/core_sched_test.go b/nomad/core_sched_test.go index c0381cb95..cb65dc0b8 100644 --- a/nomad/core_sched_test.go +++ b/nomad/core_sched_test.go @@ -1980,6 +1980,27 @@ func TestAllocation_GCEligible(t *testing.T) { JobStatus: structs.JobStatusDead, ShouldGC: true, }, + { + Desc: "GC when desired status is stop, unlimited reschedule policy, no previous reschedule events", + ClientStatus: structs.AllocClientStatusFailed, + DesiredStatus: structs.AllocDesiredStatusStop, + GCTime: fail, + ReschedulePolicy: &structs.ReschedulePolicy{Unlimited: true, Delay: 5 * time.Second, DelayFunction: "constant"}, + ShouldGC: true, + }, + { + Desc: "GC when desired status is stop, limited reschedule policy, some previous reschedule events", + ClientStatus: structs.AllocClientStatusFailed, + DesiredStatus: structs.AllocDesiredStatusStop, + GCTime: fail, + ReschedulePolicy: &structs.ReschedulePolicy{Attempts: 5, Interval: 30 * time.Minute}, + RescheduleTrackers: []*structs.RescheduleEvent{ + { + RescheduleTime: fail.Add(-3 * time.Minute).UTC().UnixNano(), + }, + }, + ShouldGC: true, + }, } for _, tc := range harness {