Support zuul.child_jobs via zuul_return

Expose the ability for a user to skip child jobs using zuul_return and
zuul.child_jobs.

Change-Id: I21ea9b2d3b1711f0d55bbe3d626ac7dde5db2919
Signed-off-by: Paul Belanger <pabelanger@redhat.com>
This commit is contained in:
Paul Belanger 2018-06-26 18:59:07 -04:00
parent 144df5e2d5
commit 5c797a12a8
No known key found for this signature in database
GPG Key ID: 611A80832067AF38
12 changed files with 190 additions and 6 deletions

View File

@ -580,6 +580,25 @@ To set the log URL for a build, use *zuul_return* to set the
zuul:
log_url: http://logs.example.com/path/to/build/logs
To skip a child job for the current build, use *zuul_return* to set the
:var:`zuul.child_jobs` value. For example:
.. code-block:: yaml
tasks:
- zuul_return:
data:
zuul:
child_jobs:
- child_jobA
- child_jobC
Will tell zuul to only run the child_jobA and child_jobC for pre-configured
child jobs. If child_jobB was configured, it would be now marked as SKIPPED. If
zuul.child_jobs is empty, all jobs will be marked as SKIPPED. Invalid child jobs
are stripped and ignored, if only invalid jobs are listed it is the same as
providing an empty list to zuul.child_jobs.
Any values other than those in the ``zuul`` hierarchy will be supplied
as Ansible variables to child jobs. These variables have less
precedence than any other type of variable in Zuul, so be sure their

View File

@ -0,0 +1,8 @@
---
features:
- |
It is now possible to use zuul_return to skip child jobs. You can
use the :var:`zuul.child_jobs` inventory variable to get a list of
child jobs configured to run, then use zuul_return to modify the
list. Any child job not in zuul_return zuul.child_jobs will be
skipped. See :ref:`return_values` for examples.

View File

@ -0,0 +1,8 @@
- hosts: localhost
tasks:
- zuul_return:
data:
zuul:
child_jobs:
- data-return
log_url: http://example.com/test/log/url/

View File

@ -0,0 +1,8 @@
- hosts: localhost
tasks:
- zuul_return:
data:
zuul:
child_jobs:
- invalid-job
log_url: http://example.com/test/log/url/

View File

@ -0,0 +1,7 @@
- hosts: localhost
tasks:
- zuul_return:
data:
zuul:
child_jobs: []
log_url: http://example.com/test/log/url/

View File

@ -20,6 +20,18 @@
name: data-return
run: playbooks/data-return.yaml
- job:
name: data-return-child-jobs
run: playbooks/data-return-child-jobs.yaml
- job:
name: data-return-invalid-child-job
run: playbooks/data-return-invalid-child-job.yaml
- job:
name: data-return-skip-all
run: playbooks/data-return-skip-all.yaml
- job:
name: data-return-relative
success-url: docs/index.html
@ -39,3 +51,36 @@
dependencies:
- data-return
- data-return-relative
- project:
name: org/project1
check:
jobs:
- data-return-child-jobs
- data-return:
dependencies:
- data-return-child-jobs
- child:
dependencies:
- data-return-child-jobs
- project:
name: org/project2
check:
jobs:
- data-return-invalid-child-job
- data-return:
dependencies:
- data-return-invalid-child-job
- project:
name: org/project3
check:
jobs:
- data-return-skip-all
- data-return:
dependencies:
- data-return-skip-all
- child:
dependencies:
- data-return-skip-all

View File

@ -0,0 +1 @@
test

View File

@ -0,0 +1 @@
test

View File

@ -0,0 +1 @@
test

View File

@ -6,3 +6,6 @@
- common-config
untrusted-projects:
- org/project
- org/project1
- org/project2
- org/project3

View File

@ -2827,6 +2827,52 @@ class TestDataReturn(AnsibleZuulTestCase):
'http://example.com/test/log/url/docs/index.html',
A.messages[-1])
def test_data_return_child_jobs(self):
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='data-return-child-jobs', result='SUCCESS',
changes='1,1'),
dict(name='data-return', result='SUCCESS', changes='1,1'),
])
self.assertIn(
'- data-return-child-jobs http://example.com/test/log/url/',
A.messages[-1])
self.assertIn(
'- data-return http://example.com/test/log/url/',
A.messages[-1])
self.assertIn('child : SKIPPED', A.messages[-1])
self.assertIn('Build succeeded', A.messages[-1])
def test_data_return_invalid_child_job(self):
A = self.fake_gerrit.addFakeChange('org/project2', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='data-return-invalid-child-job', result='SUCCESS',
changes='1,1')])
self.assertIn(
'- data-return-invalid-child-job http://example.com/test/log/url/',
A.messages[-1])
self.assertIn('data-return : SKIPPED', A.messages[-1])
self.assertIn('Build succeeded', A.messages[-1])
def test_data_return_skip_all_child_jobs(self):
A = self.fake_gerrit.addFakeChange('org/project3', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertHistory([
dict(name='data-return-skip-all', result='SUCCESS',
changes='1,1'),
])
self.assertIn(
'- data-return-skip-all http://example.com/test/log/url/',
A.messages[-1])
self.assertIn('child : SKIPPED', A.messages[-1])
self.assertIn('data-return : SKIPPED', A.messages[-1])
self.assertIn('Build succeeded', A.messages[-1])
class TestDiskAccounting(AnsibleZuulTestCase):
config_file = 'zuul-disk-accounting.conf'

View File

@ -1802,14 +1802,24 @@ class QueueItem(object):
def didAllJobsSucceed(self):
if not self.hasJobGraph():
return False
skipped = True
for job in self.getJobs():
if not job.voting:
continue
build = self.current_build_set.getBuild(job.name)
if not build:
return False
if build.result != 'SUCCESS':
if build.result == 'SKIPPED':
continue
elif build.result != 'SUCCESS':
return False
skipped = False
# NOTE(pabelanger): We shouldn't be able to skip all jobs.
if skipped:
return False
return True
def didAnyJobFail(self):
@ -1971,12 +1981,39 @@ class QueueItem(object):
def setResult(self, build):
if build.retry:
self.removeBuild(build)
return
skipped = []
# NOTE(pabelanger): Check successful jobs to see if zuul_return
# includes zuul.child_jobs.
build_result = build.result_data.get('zuul', {})
if 'child_jobs' in build_result:
zuul_return = build_result.get('child_jobs', [])
dependent_jobs = self.job_graph.getDirectDependentJobs(
build.job.name)
if not zuul_return:
# If zuul.child_jobs exists and is empty, user want to skip all
# child jobs.
skipped += self.job_graph.getDependentJobsRecursively(
build.job.name)
else:
# We have list of jobs to run.
intersect_jobs = dependent_jobs.intersection(zuul_return)
for skip in (dependent_jobs - intersect_jobs):
skipped.append(self.job_graph.jobs.get(skip))
skipped += self.job_graph.getDependentJobsRecursively(
skip)
elif build.result != 'SUCCESS':
for job in self.job_graph.getDependentJobsRecursively(
build.job.name):
fakebuild = Build(job, None)
fakebuild.result = 'SKIPPED'
self.addBuild(fakebuild)
skipped += self.job_graph.getDependentJobsRecursively(
build.job.name)
for job in skipped:
fakebuild = Build(job, None)
fakebuild.result = 'SKIPPED'
self.addBuild(fakebuild)
def setNodeRequestFailure(self, job):
fakebuild = Build(job, None)