summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Belanger <pabelanger@redhat.com>2017-11-14 04:47:41 -0500
committerPaul Belanger <pabelanger@redhat.com>2019-02-05 14:52:29 -0500
commit74a974bf4e3e7faa78fda2af3604a2db5e6a1829 (patch)
treea4e1878846c27b8d22b08cb5c40742be2caaacc5
parent363d881ab9291a2bbca4c0f3c270b873fd2ad009 (diff)
Allow run to be list of playbooks
Like pre-run and post-run, allow a user to run a list of playbooks for a job. One example would be your job workflow would be to run multiple playbooks over using a site.yaml file with include_playbook commands. A second use case, more related to job design. With multiple playbooks support for job.run, the first playbook would be use deploy your server and the second playbook to validate the server was provisioned properly. Today, this can be done using a single run and post-run playbooks, however if post-run fails, zuul will return POST_FAILURE, not FAILURE. Not a large issue, but could be confusing to users when POST_FAILURE is returned. While it is possible a user could create a single site.yaml playbook, and use multiple include_playbook statements to get this functionality, there are downsides to this approach (mostly with the leaking of variables). Today, operators simply run ansible-playbook multiple times with the specific playbooks they only want. Story: 2002543 Task: 22101 Change-Id: I6268d9944e745cc07407ea7dd040fbfeb79dad4d Related-To: https://review.openstack.org/519596 Signed-off-by: Paul Belanger <pabelanger@redhat.com>
Notes
Notes (review): Code-Review+2: James E. Blair <corvus@inaugust.com> Code-Review+2: Tobias Henkel <tobias.henkel@bmw.de> Workflow+1: Tobias Henkel <tobias.henkel@bmw.de> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Wed, 06 Feb 2019 04:38:02 +0000 Reviewed-on: https://review.openstack.org/592160 Project: openstack-infra/zuul Branch: refs/heads/master
-rw-r--r--doc/source/user/config.rst6
-rw-r--r--releasenotes/notes/job-run-list-7036fbac9c146098.yaml4
-rw-r--r--tests/fixtures/config/ansible/git/common-config/playbooks/bar.yaml5
-rw-r--r--tests/fixtures/config/ansible/git/common-config/playbooks/first-fail-post.yaml11
-rw-r--r--tests/fixtures/config/ansible/git/common-config/playbooks/first-fail.yaml4
-rw-r--r--tests/fixtures/config/ansible/git/common-config/playbooks/foo.yaml5
-rw-r--r--tests/fixtures/config/ansible/git/common-config/playbooks/foobar-post.yaml33
-rw-r--r--tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent-post.yaml11
-rw-r--r--tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent.yaml5
-rw-r--r--tests/fixtures/config/ansible/git/common-config/zuul.yaml64
-rw-r--r--tests/fixtures/config/ansible/git/org_project/.zuul.yaml4
-rw-r--r--tests/unit/test_v3.py14
-rw-r--r--zuul/configloader.py10
-rw-r--r--zuul/executor/server.py50
14 files changed, 196 insertions, 30 deletions
diff --git a/doc/source/user/config.rst b/doc/source/user/config.rst
index 04de33e..0bb1c46 100644
--- a/doc/source/user/config.rst
+++ b/doc/source/user/config.rst
@@ -835,9 +835,9 @@ Here is an example of two job definitions:
835 835
836 .. attr:: run 836 .. attr:: run
837 837
838 The name of the main playbook for this job. If it is not 838 The name of a playbook or list of playbooks for this job. If it
839 supplied, the parent's playbook will be used (and likewise up 839 is not supplied, the parent's playbook will be used (and likewise
840 the inheritance chain). The full path within the repo is 840 up the inheritance chain). The full path within the repo is
841 required. Example: 841 required. Example:
842 842
843 .. code-block:: yaml 843 .. code-block:: yaml
diff --git a/releasenotes/notes/job-run-list-7036fbac9c146098.yaml b/releasenotes/notes/job-run-list-7036fbac9c146098.yaml
new file mode 100644
index 0000000..0a48736
--- /dev/null
+++ b/releasenotes/notes/job-run-list-7036fbac9c146098.yaml
@@ -0,0 +1,4 @@
1---
2features:
3 - |
4 The :attr:`job.run` attribute now supports a single or list of playbooks.
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/bar.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/bar.yaml
new file mode 100644
index 0000000..c219e81
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/bar.yaml
@@ -0,0 +1,5 @@
1- hosts: bar
2 tasks:
3 - copy:
4 content: "bar456"
5 dest: "{{zuul.executor.log_root}}/bar.txt"
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/first-fail-post.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/first-fail-post.yaml
new file mode 100644
index 0000000..7e34c50
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/first-fail-post.yaml
@@ -0,0 +1,11 @@
1- hosts: all
2 tasks:
3 - name: Register bar.txt file.
4 stat:
5 path: "{{zuul.executor.log_root}}/bar.txt"
6 register: bar_st
7
8 - name: Assert bar.txt file.
9 assert:
10 that:
11 - not bar_st.stat.exists
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/first-fail.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/first-fail.yaml
new file mode 100644
index 0000000..3ff27a8
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/first-fail.yaml
@@ -0,0 +1,4 @@
1- hosts: foo
2 tasks:
3 - fail:
4 msg: "Always fail"
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/foo.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/foo.yaml
new file mode 100644
index 0000000..86e22d6
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/foo.yaml
@@ -0,0 +1,5 @@
1- hosts: foo
2 tasks:
3 - copy:
4 content: "foo123"
5 dest: "{{zuul.executor.log_root}}/foo.txt"
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/foobar-post.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/foobar-post.yaml
new file mode 100644
index 0000000..379e5d0
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/foobar-post.yaml
@@ -0,0 +1,33 @@
1- hosts: all
2 tasks:
3 - name: Register parent.txt file.
4 stat:
5 path: "{{zuul.executor.log_root}}/parent.txt"
6 register: parent_st
7
8 - name: Assert parent.txt does not exist.
9 assert:
10 that:
11 - not parent_st.stat.exists
12
13 - name: Register foo.txt file.
14 stat:
15 path: "{{zuul.executor.log_root}}/foo.txt"
16 register: foo_st
17
18 - name: Assert foo.txt exists.
19 assert:
20 that:
21 - foo_st.stat.exists
22 - foo_st.stat.isreg
23
24 - name: Register bar.txt file.
25 stat:
26 path: "{{zuul.executor.log_root}}/bar.txt"
27 register: bar_st
28
29 - name: Assert bar.txt exists.
30 assert:
31 that:
32 - bar_st.stat.exists
33 - bar_st.stat.isreg
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent-post.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent-post.yaml
new file mode 100644
index 0000000..8060fec
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent-post.yaml
@@ -0,0 +1,11 @@
1- hosts: all
2 tasks:
3 - name: Register parent.txt file.
4 stat:
5 path: "{{zuul.executor.log_root}}/parent.txt"
6 register: parent_st
7
8 - name: Assert parent.txt exist.
9 assert:
10 that:
11 - parent_st.stat.exists
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent.yaml
new file mode 100644
index 0000000..2d3d3fe
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent.yaml
@@ -0,0 +1,5 @@
1- hosts: foo
2 tasks:
3 - copy:
4 content: "parent"
5 dest: "{{zuul.executor.log_root}}/parent.txt"
diff --git a/tests/fixtures/config/ansible/git/common-config/zuul.yaml b/tests/fixtures/config/ansible/git/common-config/zuul.yaml
index a7576d6..eb252fd 100644
--- a/tests/fixtures/config/ansible/git/common-config/zuul.yaml
+++ b/tests/fixtures/config/ansible/git/common-config/zuul.yaml
@@ -172,6 +172,70 @@
172 name: renamed_secret 172 name: renamed_secret
173 173
174- job: 174- job:
175 name: multiple-parent
176 run: playbooks/multiple-parent.yaml
177 nodeset:
178 nodes:
179 - name: ubuntu-xenial
180 label: ubuntu-xenial
181 groups:
182 - name: foo
183 nodes:
184 - ubuntu-xenial
185 - name: bar
186 nodes:
187 - ubuntu-xenial
188
189- job:
190 name: multiple-child
191 parent: multiple-parent
192 run:
193 - playbooks/foo.yaml
194 - playbooks/bar.yaml
195 post-run: playbooks/foobar-post.yaml
196
197- job:
198 name: multiple-child-no-run
199 parent: multiple-parent
200 post-run: playbooks/multiple-parent-post.yaml
201
202- job:
203 name: multiple-run
204 run:
205 - playbooks/foo.yaml
206 - playbooks/bar.yaml
207 post-run: playbooks/foobar-post.yaml
208 nodeset:
209 nodes:
210 - name: ubuntu-xenial
211 label: ubuntu-xenial
212 groups:
213 - name: foo
214 nodes:
215 - ubuntu-xenial
216 - name: bar
217 nodes:
218 - ubuntu-xenial
219
220- job:
221 name: multiple-run-failure
222 run:
223 - playbooks/first-fail.yaml
224 - playbooks/bar.yaml
225 post-run: playbooks/first-fail-post.yaml
226 nodeset:
227 nodes:
228 - name: ubuntu-xenial
229 label: ubuntu-xenial
230 groups:
231 - name: foo
232 nodes:
233 - ubuntu-xenial
234 - name: bar
235 nodes:
236 - ubuntu-xenial
237
238- job:
175 parent: base-urls 239 parent: base-urls
176 name: hello 240 name: hello
177 run: playbooks/hello-post.yaml 241 run: playbooks/hello-post.yaml
diff --git a/tests/fixtures/config/ansible/git/org_project/.zuul.yaml b/tests/fixtures/config/ansible/git/org_project/.zuul.yaml
index a1d0d02..2a974c3 100644
--- a/tests/fixtures/config/ansible/git/org_project/.zuul.yaml
+++ b/tests/fixtures/config/ansible/git/org_project/.zuul.yaml
@@ -30,3 +30,7 @@
30 - post-timeout 30 - post-timeout
31 - hello-world 31 - hello-world
32 - failpost 32 - failpost
33 - multiple-child
34 - multiple-child-no-run
35 - multiple-run
36 - multiple-run-failure
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py
index d6554a0..7337b8c 100644
--- a/tests/unit/test_v3.py
+++ b/tests/unit/test_v3.py
@@ -2312,6 +2312,20 @@ class TestAnsible(AnsibleZuulTestCase):
2312 build_add_host = self.getJobFromHistory('add-host') 2312 build_add_host = self.getJobFromHistory('add-host')
2313 with self.jobLog(build_add_host): 2313 with self.jobLog(build_add_host):
2314 self.assertEqual(build_add_host.result, 'SUCCESS') 2314 self.assertEqual(build_add_host.result, 'SUCCESS')
2315 build_multiple_child = self.getJobFromHistory('multiple-child')
2316 with self.jobLog(build_multiple_child):
2317 self.assertEqual(build_multiple_child.result, 'SUCCESS')
2318 build_multiple_child_no_run = self.getJobFromHistory(
2319 'multiple-child-no-run')
2320 with self.jobLog(build_multiple_child_no_run):
2321 self.assertEqual(build_multiple_child_no_run.result, 'SUCCESS')
2322 build_multiple_run = self.getJobFromHistory('multiple-run')
2323 with self.jobLog(build_multiple_run):
2324 self.assertEqual(build_multiple_run.result, 'SUCCESS')
2325 build_multiple_run_failure = self.getJobFromHistory(
2326 'multiple-run-failure')
2327 with self.jobLog(build_multiple_run_failure):
2328 self.assertEqual(build_multiple_run_failure.result, 'FAILURE')
2315 build_python27 = self.getJobFromHistory('python27') 2329 build_python27 = self.getJobFromHistory('python27')
2316 with self.jobLog(build_python27): 2330 with self.jobLog(build_python27):
2317 self.assertEqual(build_python27.result, 'SUCCESS') 2331 self.assertEqual(build_python27.result, 'SUCCESS')
diff --git a/zuul/configloader.py b/zuul/configloader.py
index 64318e2..6965821 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -564,7 +564,7 @@ class JobParser(object):
564 'attempts': int, 564 'attempts': int,
565 'pre-run': to_list(str), 565 'pre-run': to_list(str),
566 'post-run': to_list(str), 566 'post-run': to_list(str),
567 'run': str, 567 'run': to_list(str),
568 '_source_context': model.SourceContext, 568 '_source_context': model.SourceContext,
569 '_start_mark': ZuulMark, 569 '_start_mark': ZuulMark,
570 'roles': to_list(role), 570 'roles': to_list(role),
@@ -717,10 +717,12 @@ class JobParser(object):
717 post_run_name, job.roles, 717 post_run_name, job.roles,
718 secrets) 718 secrets)
719 job.post_run = (post_run,) + job.post_run 719 job.post_run = (post_run,) + job.post_run
720
720 if 'run' in conf: 721 if 'run' in conf:
721 run = model.PlaybookContext(job.source_context, conf['run'], 722 for run_name in as_list(conf.get('run')):
722 job.roles, secrets) 723 run = model.PlaybookContext(job.source_context, run_name,
723 job.run = (run,) 724 job.roles, secrets)
725 job.run = job.run + (run,)
724 726
725 for k in self.simple_attributes: 727 for k in self.simple_attributes:
726 a = k.replace('-', '_') 728 a = k.replace('-', '_')
diff --git a/zuul/executor/server.py b/zuul/executor/server.py
index 219c234..a941c89 100644
--- a/zuul/executor/server.py
+++ b/zuul/executor/server.py
@@ -393,7 +393,6 @@ class JobDir(object):
393 'setup-inventory.yaml') 393 'setup-inventory.yaml')
394 self.logging_json = os.path.join(self.ansible_root, 'logging.json') 394 self.logging_json = os.path.join(self.ansible_root, 'logging.json')
395 self.playbooks = [] # The list of candidate playbooks 395 self.playbooks = [] # The list of candidate playbooks
396 self.playbook = None # A pointer to the candidate we have chosen
397 self.pre_playbooks = [] 396 self.pre_playbooks = []
398 self.post_playbooks = [] 397 self.post_playbooks = []
399 self.job_output_file = os.path.join(self.log_root, 'job-output.txt') 398 self.job_output_file = os.path.join(self.log_root, 'job-output.txt')
@@ -1126,26 +1125,30 @@ class AnsibleJob(object):
1126 self.cpu_times['children_system'])) 1125 self.cpu_times['children_system']))
1127 1126
1128 if not pre_failed: 1127 if not pre_failed:
1129 ansible_timeout = self.getAnsibleTimeout(time_started, job_timeout) 1128 for index, playbook in enumerate(self.jobdir.playbooks):
1130 job_status, job_code = self.runAnsiblePlaybook( 1129 ansible_timeout = self.getAnsibleTimeout(
1131 self.jobdir.playbook, ansible_timeout, phase='run') 1130 time_started, job_timeout)
1132 if job_status == self.RESULT_ABORTED: 1131 job_status, job_code = self.runAnsiblePlaybook(
1133 return 'ABORTED' 1132 playbook, ansible_timeout, phase='run', index=index)
1134 elif job_status == self.RESULT_TIMED_OUT: 1133 if job_status == self.RESULT_ABORTED:
1135 # Set the pre-failure flag so this doesn't get 1134 return 'ABORTED'
1136 # overridden by a post-failure. 1135 elif job_status == self.RESULT_TIMED_OUT:
1137 pre_failed = True 1136 # Set the pre-failure flag so this doesn't get
1138 result = 'TIMED_OUT' 1137 # overridden by a post-failure.
1139 elif job_status == self.RESULT_NORMAL: 1138 pre_failed = True
1140 success = (job_code == 0) 1139 result = 'TIMED_OUT'
1141 if success: 1140 break
1142 result = 'SUCCESS' 1141 elif job_status == self.RESULT_NORMAL:
1142 success = (job_code == 0)
1143 if success:
1144 result = 'SUCCESS'
1145 else:
1146 result = 'FAILURE'
1147 break
1143 else: 1148 else:
1144 result = 'FAILURE' 1149 # The result of the job is indeterminate. Zuul will
1145 else: 1150 # run it again.
1146 # The result of the job is indeterminate. Zuul will 1151 return None
1147 # run it again.
1148 return None
1149 1152
1150 # check if we need to pause here 1153 # check if we need to pause here
1151 result_data = self.getResultData() 1154 result_data = self.getResultData()
@@ -1343,14 +1346,15 @@ class AnsibleJob(object):
1343 jobdir_playbook = self.jobdir.addPrePlaybook() 1346 jobdir_playbook = self.jobdir.addPrePlaybook()
1344 self.preparePlaybook(jobdir_playbook, playbook, args) 1347 self.preparePlaybook(jobdir_playbook, playbook, args)
1345 1348
1349 job_playbook = None
1346 for playbook in args['playbooks']: 1350 for playbook in args['playbooks']:
1347 jobdir_playbook = self.jobdir.addPlaybook() 1351 jobdir_playbook = self.jobdir.addPlaybook()
1348 self.preparePlaybook(jobdir_playbook, playbook, args) 1352 self.preparePlaybook(jobdir_playbook, playbook, args)
1349 if jobdir_playbook.path is not None: 1353 if jobdir_playbook.path is not None:
1350 self.jobdir.playbook = jobdir_playbook 1354 if job_playbook is None:
1351 break 1355 job_playbook = jobdir_playbook
1352 1356
1353 if self.jobdir.playbook is None: 1357 if job_playbook is None:
1354 raise ExecutorError("No playbook specified") 1358 raise ExecutorError("No playbook specified")
1355 1359
1356 for playbook in args['post_playbooks']: 1360 for playbook in args['post_playbooks']: