From 5c797a12a8229b30124988723f786d4ee8dea807 Mon Sep 17 00:00:00 2001 From: Paul Belanger Date: Tue, 26 Jun 2018 18:59:07 -0400 Subject: [PATCH] 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 --- doc/source/user/jobs.rst | 19 +++++++ ...turn-skip-child-jobs-772988c87c495cb2.yaml | 8 +++ .../playbooks/data-return-child-jobs.yaml | 8 +++ .../data-return-invalid-child-job.yaml | 8 +++ .../playbooks/data-return-skip-all.yaml | 7 +++ .../data-return/git/common-config/zuul.yaml | 45 +++++++++++++++++ .../data-return/git/org_project1/README | 1 + .../data-return/git/org_project2/README | 1 + .../data-return/git/org_project3/README | 1 + tests/fixtures/config/data-return/main.yaml | 3 ++ tests/unit/test_v3.py | 46 +++++++++++++++++ zuul/model.py | 49 ++++++++++++++++--- 12 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/zuul-return-skip-child-jobs-772988c87c495cb2.yaml create mode 100644 tests/fixtures/config/data-return/git/common-config/playbooks/data-return-child-jobs.yaml create mode 100644 tests/fixtures/config/data-return/git/common-config/playbooks/data-return-invalid-child-job.yaml create mode 100644 tests/fixtures/config/data-return/git/common-config/playbooks/data-return-skip-all.yaml create mode 100644 tests/fixtures/config/data-return/git/org_project1/README create mode 100644 tests/fixtures/config/data-return/git/org_project2/README create mode 100644 tests/fixtures/config/data-return/git/org_project3/README diff --git a/doc/source/user/jobs.rst b/doc/source/user/jobs.rst index 3f0518cbf3..3390dc9320 100644 --- a/doc/source/user/jobs.rst +++ b/doc/source/user/jobs.rst @@ -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 diff --git a/releasenotes/notes/zuul-return-skip-child-jobs-772988c87c495cb2.yaml b/releasenotes/notes/zuul-return-skip-child-jobs-772988c87c495cb2.yaml new file mode 100644 index 0000000000..cb4f9b1c4f --- /dev/null +++ b/releasenotes/notes/zuul-return-skip-child-jobs-772988c87c495cb2.yaml @@ -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. diff --git a/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-child-jobs.yaml b/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-child-jobs.yaml new file mode 100644 index 0000000000..01c2a2dccf --- /dev/null +++ b/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-child-jobs.yaml @@ -0,0 +1,8 @@ +- hosts: localhost + tasks: + - zuul_return: + data: + zuul: + child_jobs: + - data-return + log_url: http://example.com/test/log/url/ diff --git a/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-invalid-child-job.yaml b/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-invalid-child-job.yaml new file mode 100644 index 0000000000..d5a383b70d --- /dev/null +++ b/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-invalid-child-job.yaml @@ -0,0 +1,8 @@ +- hosts: localhost + tasks: + - zuul_return: + data: + zuul: + child_jobs: + - invalid-job + log_url: http://example.com/test/log/url/ diff --git a/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-skip-all.yaml b/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-skip-all.yaml new file mode 100644 index 0000000000..45c943193b --- /dev/null +++ b/tests/fixtures/config/data-return/git/common-config/playbooks/data-return-skip-all.yaml @@ -0,0 +1,7 @@ +- hosts: localhost + tasks: + - zuul_return: + data: + zuul: + child_jobs: [] + log_url: http://example.com/test/log/url/ diff --git a/tests/fixtures/config/data-return/git/common-config/zuul.yaml b/tests/fixtures/config/data-return/git/common-config/zuul.yaml index 97b6b282a9..bb789b95e0 100644 --- a/tests/fixtures/config/data-return/git/common-config/zuul.yaml +++ b/tests/fixtures/config/data-return/git/common-config/zuul.yaml @@ -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 diff --git a/tests/fixtures/config/data-return/git/org_project1/README b/tests/fixtures/config/data-return/git/org_project1/README new file mode 100644 index 0000000000..9daeafb986 --- /dev/null +++ b/tests/fixtures/config/data-return/git/org_project1/README @@ -0,0 +1 @@ +test diff --git a/tests/fixtures/config/data-return/git/org_project2/README b/tests/fixtures/config/data-return/git/org_project2/README new file mode 100644 index 0000000000..9daeafb986 --- /dev/null +++ b/tests/fixtures/config/data-return/git/org_project2/README @@ -0,0 +1 @@ +test diff --git a/tests/fixtures/config/data-return/git/org_project3/README b/tests/fixtures/config/data-return/git/org_project3/README new file mode 100644 index 0000000000..9daeafb986 --- /dev/null +++ b/tests/fixtures/config/data-return/git/org_project3/README @@ -0,0 +1 @@ +test diff --git a/tests/fixtures/config/data-return/main.yaml b/tests/fixtures/config/data-return/main.yaml index 208e274b13..919921e916 100644 --- a/tests/fixtures/config/data-return/main.yaml +++ b/tests/fixtures/config/data-return/main.yaml @@ -6,3 +6,6 @@ - common-config untrusted-projects: - org/project + - org/project1 + - org/project2 + - org/project3 diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py index 3963b41e77..5c7d9d8408 100755 --- a/tests/unit/test_v3.py +++ b/tests/unit/test_v3.py @@ -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' diff --git a/zuul/model.py b/zuul/model.py index 71e677e186..7302b9d83d 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -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)