Add pipeline precedence
Allow, eg, jobs in a gate pipeline to take precedence over
a check pipeline.
Change-Id: Idf91527704cc75b00a336291f91b908286f8e630
Reviewed-on: https://review.openstack.org/36552
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index 430acf0..d9e245b 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -316,6 +316,13 @@
do when a change is added to the pipeline manager. This can be used,
for example, to reset the value of the Verified review category.
+**precedence**
+ Indicates how the build scheduler should prioritize jobs for
+ different pipelines. Each pipeline may have one precedence, jobs
+ for pipelines with a higher precedence will be run before ones with
+ lower. The value should be one of ``high``, ``normal``, or ``low``.
+ Default: ``normal``.
+
Some example pipeline configurations are included in the sample layout
file. The first is called a *check* pipeline::
diff --git a/tests/fixtures/layout.yaml b/tests/fixtures/layout.yaml
index 2695719..37ea552 100644
--- a/tests/fixtures/layout.yaml
+++ b/tests/fixtures/layout.yaml
@@ -31,6 +31,7 @@
verified: -2
start:
verified: 0
+ precedence: high
- name: unused
manager: IndependentPipelineManager
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 023ed35..b67e09f 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -495,7 +495,8 @@
self.worker.build_history.append(
BuildHistory(name=self.name, number=self.number,
result=result, changes=changes, node=self.node,
- uuid=self.unique, description=self.description)
+ uuid=self.unique, description=self.description,
+ pipeline=self.parameters['ZUUL_PIPELINE'])
)
self.job.sendWorkComplete(json.dumps(data))
@@ -2267,6 +2268,28 @@
self.assertTrue(re.search("project-test2.*SUCCESS", desc))
self.assertTrue(re.search("Reported result.*SUCCESS", desc))
+ def test_queue_precedence(self):
+ "Test that queue precedence works"
+
+ self.gearman_server.hold_jobs_in_queue = True
+ A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+ self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+ A.addApproval('CRVW', 2)
+ self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+
+ self.waitUntilSettled()
+ self.gearman_server.hold_jobs_in_queue = False
+ self.gearman_server.release()
+ self.waitUntilSettled()
+
+ self.log.debug(self.history)
+ self.assertEqual(self.history[0].pipeline, 'gate')
+ self.assertEqual(self.history[1].pipeline, 'check')
+ self.assertEqual(self.history[2].pipeline, 'gate')
+ self.assertEqual(self.history[3].pipeline, 'gate')
+ self.assertEqual(self.history[4].pipeline, 'check')
+ self.assertEqual(self.history[5].pipeline, 'check')
+
def test_json_status(self):
"Test that we can retrieve JSON status info"
self.worker.hold_jobs_in_build = True
diff --git a/zuul/launcher/gearman.py b/zuul/launcher/gearman.py
index 9390eee..0f9cc54 100644
--- a/zuul/launcher/gearman.py
+++ b/zuul/launcher/gearman.py
@@ -19,6 +19,7 @@
import threading
from uuid import uuid4
+import zuul.model
from zuul.model import Build
@@ -297,8 +298,15 @@
self.onBuildCompleted(gearman_job, 'LOST')
return build
+ if pipeline.precedence == zuul.model.PRECEDENCE_NORMAL:
+ precedence = gear.PRECEDENCE_NORMAL
+ elif pipeline.precedence == zuul.model.PRECEDENCE_HIGH:
+ precedence = gear.PRECEDENCE_HIGH
+ elif pipeline.precedence == zuul.model.PRECEDENCE_LOW:
+ precedence = gear.PRECEDENCE_LOW
+
try:
- self.gearman.submitJob(gearman_job)
+ self.gearman.submitJob(gearman_job, precedence=precedence)
except Exception:
self.log.exception("Unable to submit job to Gearman")
self.onBuildCompleted(gearman_job, 'LOST')
@@ -407,7 +415,7 @@
json.dumps(data), unique=stop_uuid)
self.meta_jobs[stop_uuid] = stop_job
self.log.debug("Submitting stop job: %s", stop_job)
- self.gearman.submitJob(stop_job)
+ self.gearman.submitJob(stop_job, precedence=gear.PRECEDENCE_HIGH)
return True
def setBuildDescription(self, build, desc):
@@ -428,7 +436,7 @@
desc_job = gear.Job(name, json.dumps(data), unique=desc_uuid)
self.meta_jobs[desc_uuid] = desc_job
self.log.debug("Submitting describe job: %s", desc_job)
- self.gearman.submitJob(desc_job)
+ self.gearman.submitJob(desc_job, precedence=gear.PRECEDENCE_LOW)
return True
def lookForLostBuilds(self):
diff --git a/zuul/layoutvalidator.py b/zuul/layoutvalidator.py
index 5588afe..724b236 100644
--- a/zuul/layoutvalidator.py
+++ b/zuul/layoutvalidator.py
@@ -30,6 +30,9 @@
manager = v.Any('IndependentPipelineManager',
'DependentPipelineManager')
+
+ precedence = v.Any('normal', 'low', 'high')
+
variable_dict = v.Schema({}, extra=True)
trigger = {v.Required('event'): toList(v.Any('patchset-created',
@@ -47,6 +50,7 @@
pipeline = {v.Required('name'): str,
v.Required('manager'): manager,
+ 'precedence': precedence,
'description': str,
'success-message': str,
'failure-message': str,
diff --git a/zuul/model.py b/zuul/model.py
index ac475ba..6b83feb 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -22,6 +22,17 @@
MERGE_IF_NECESSARY = 3
CHERRY_PICK = 4
+PRECEDENCE_NORMAL = 0
+PRECEDENCE_LOW = 1
+PRECEDENCE_HIGH = 2
+
+PRECEDENCE_MAP = {
+ None: PRECEDENCE_NORMAL,
+ 'low': PRECEDENCE_LOW,
+ 'normal': PRECEDENCE_NORMAL,
+ 'high': PRECEDENCE_HIGH,
+}
+
class Pipeline(object):
"""A top-level pipeline such as check, gate, post, etc."""
@@ -34,6 +45,7 @@
self.job_trees = {} # project -> JobTree
self.manager = None
self.queues = []
+ self.precedence = PRECEDENCE_NORMAL
def __repr__(self):
return '<Pipeline %s>' % self.name
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 52df7db..338eb63 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -123,6 +123,8 @@
for conf_pipeline in data.get('pipelines', []):
pipeline = Pipeline(conf_pipeline['name'])
pipeline.description = conf_pipeline.get('description')
+ precedence = model.PRECEDENCE_MAP[conf_pipeline.get('precedence')]
+ pipeline.precedence = precedence
pipeline.failure_message = conf_pipeline.get('failure-message',
"Build failed.")
pipeline.success_message = conf_pipeline.get('success-message',