diff --git a/zuul/model.py b/zuul/model.py index 86fdc5c5d4..f95195def8 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -489,6 +489,14 @@ class Change(object): return False return True + def didAnyJobFail(self): + tree = self.project.getJobTreeForQueue(self.queue_name) + for job in self._filterJobs(tree.getJobs()): + build = self.current_build_set.getBuild(job.name) + if build and build.result == 'FAILURE': + return True + return False + def delete(self): if self.change_behind: self.change_behind.change_ahead = None diff --git a/zuul/scheduler.py b/zuul/scheduler.py index 232804d919..7b28dec211 100644 --- a/zuul/scheduler.py +++ b/zuul/scheduler.py @@ -632,10 +632,11 @@ for change %s:" % (job, change)) change.change_behind, change)) self.launchJobs(change.change_behind) - def cancelJobs(self, change): + def cancelJobs(self, change, prime=True): self.log.debug("Cancel jobs for change %s" % change) to_remove = [] - change.resetAllBuilds() + if prime: + change.resetAllBuilds() for build, build_change in self.building_jobs.items(): if build_change == change: self.log.debug("Found build %s for change %s to cancel" % ( @@ -653,7 +654,20 @@ for change %s" % (build, change)) if change.change_behind: self.log.debug("Canceling jobs for change %s, \ behind change %s" % (change.change_behind, change)) - self.cancelJobs(change.change_behind) + self.cancelJobs(change.change_behind, prime=prime) + + def onBuildCompleted(self, build): + change = self.building_jobs.get(build) + if not super(DependentQueueManager, self).onBuildCompleted(build): + return False + if change and change.didAnyJobFail(): + # This or some other build failed. All changes behind this change + # will need to be retested. To free up resources cancel the builds + # behind this one as they will be rerun anyways. + self.cancelJobs(change.change_behind, prime=False) + self.log.debug("Canceling builds behind change: %s due to" + " failure." % change) + return True def possiblyReportChange(self, change): self.log.debug("Possibly reporting change %s" % change)