Browse Source

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>
Paul Belanger 1 year ago
parent
commit
74a974bf4e

+ 3
- 3
doc/source/user/config.rst View File

@@ -835,9 +835,9 @@ Here is an example of two job definitions:
835 835
 
836 836
    .. attr:: run
837 837
 
838
-      The name of the main playbook for this job.  If it is not
839
-      supplied, the parent's playbook will be used (and likewise up
840
-      the inheritance chain).  The full path within the repo is
838
+      The name of a playbook or list of playbooks for this job.  If it
839
+      is not supplied, the parent's playbook will be used (and likewise
840
+      up the inheritance chain).  The full path within the repo is
841 841
       required.  Example:
842 842
 
843 843
       .. code-block:: yaml

+ 4
- 0
releasenotes/notes/job-run-list-7036fbac9c146098.yaml View File

@@ -0,0 +1,4 @@
1
+---
2
+features:
3
+  - |
4
+    The :attr:`job.run` attribute now supports a single or list of playbooks.

+ 5
- 0
tests/fixtures/config/ansible/git/common-config/playbooks/bar.yaml View File

@@ -0,0 +1,5 @@
1
+- hosts: bar
2
+  tasks:
3
+    - copy:
4
+        content: "bar456"
5
+        dest: "{{zuul.executor.log_root}}/bar.txt"

+ 11
- 0
tests/fixtures/config/ansible/git/common-config/playbooks/first-fail-post.yaml View File

@@ -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

+ 4
- 0
tests/fixtures/config/ansible/git/common-config/playbooks/first-fail.yaml View File

@@ -0,0 +1,4 @@
1
+- hosts: foo
2
+  tasks:
3
+    - fail:
4
+        msg: "Always fail"

+ 5
- 0
tests/fixtures/config/ansible/git/common-config/playbooks/foo.yaml View File

@@ -0,0 +1,5 @@
1
+- hosts: foo
2
+  tasks:
3
+    - copy:
4
+        content: "foo123"
5
+        dest: "{{zuul.executor.log_root}}/foo.txt"

+ 33
- 0
tests/fixtures/config/ansible/git/common-config/playbooks/foobar-post.yaml View File

@@ -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

+ 11
- 0
tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent-post.yaml View File

@@ -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

+ 5
- 0
tests/fixtures/config/ansible/git/common-config/playbooks/multiple-parent.yaml View File

@@ -0,0 +1,5 @@
1
+- hosts: foo
2
+  tasks:
3
+    - copy:
4
+        content: "parent"
5
+        dest: "{{zuul.executor.log_root}}/parent.txt"

+ 64
- 0
tests/fixtures/config/ansible/git/common-config/zuul.yaml View File

@@ -171,6 +171,70 @@
171 171
       - secret: vartest_secret
172 172
         name: renamed_secret
173 173
 
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
+
174 238
 - job:
175 239
     parent: base-urls
176 240
     name: hello

+ 4
- 0
tests/fixtures/config/ansible/git/org_project/.zuul.yaml View File

@@ -30,3 +30,7 @@
30 30
         - post-timeout
31 31
         - hello-world
32 32
         - failpost
33
+        - multiple-child
34
+        - multiple-child-no-run
35
+        - multiple-run
36
+        - multiple-run-failure

+ 14
- 0
tests/unit/test_v3.py View File

@@ -2312,6 +2312,20 @@ class TestAnsible(AnsibleZuulTestCase):
2312 2312
         build_add_host = self.getJobFromHistory('add-host')
2313 2313
         with self.jobLog(build_add_host):
2314 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 2329
         build_python27 = self.getJobFromHistory('python27')
2316 2330
         with self.jobLog(build_python27):
2317 2331
             self.assertEqual(build_python27.result, 'SUCCESS')

+ 6
- 4
zuul/configloader.py View File

@@ -564,7 +564,7 @@ class JobParser(object):
564 564
                       'attempts': int,
565 565
                       'pre-run': to_list(str),
566 566
                       'post-run': to_list(str),
567
-                      'run': str,
567
+                      'run': to_list(str),
568 568
                       '_source_context': model.SourceContext,
569 569
                       '_start_mark': ZuulMark,
570 570
                       'roles': to_list(role),
@@ -717,10 +717,12 @@ class JobParser(object):
717 717
                                              post_run_name, job.roles,
718 718
                                              secrets)
719 719
             job.post_run = (post_run,) + job.post_run
720
+
720 721
         if 'run' in conf:
721
-            run = model.PlaybookContext(job.source_context, conf['run'],
722
-                                        job.roles, secrets)
723
-            job.run = (run,)
722
+            for run_name in as_list(conf.get('run')):
723
+                run = model.PlaybookContext(job.source_context, run_name,
724
+                                            job.roles, secrets)
725
+                job.run = job.run + (run,)
724 726
 
725 727
         for k in self.simple_attributes:
726 728
             a = k.replace('-', '_')

+ 27
- 23
zuul/executor/server.py View File

@@ -393,7 +393,6 @@ class JobDir(object):
393 393
                                             'setup-inventory.yaml')
394 394
         self.logging_json = os.path.join(self.ansible_root, 'logging.json')
395 395
         self.playbooks = []  # The list of candidate playbooks
396
-        self.playbook = None  # A pointer to the candidate we have chosen
397 396
         self.pre_playbooks = []
398 397
         self.post_playbooks = []
399 398
         self.job_output_file = os.path.join(self.log_root, 'job-output.txt')
@@ -1126,26 +1125,30 @@ class AnsibleJob(object):
1126 1125
              self.cpu_times['children_system']))
1127 1126
 
1128 1127
         if not pre_failed:
1129
-            ansible_timeout = self.getAnsibleTimeout(time_started, job_timeout)
1130
-            job_status, job_code = self.runAnsiblePlaybook(
1131
-                self.jobdir.playbook, ansible_timeout, phase='run')
1132
-            if job_status == self.RESULT_ABORTED:
1133
-                return 'ABORTED'
1134
-            elif job_status == self.RESULT_TIMED_OUT:
1135
-                # Set the pre-failure flag so this doesn't get
1136
-                # overridden by a post-failure.
1137
-                pre_failed = True
1138
-                result = 'TIMED_OUT'
1139
-            elif job_status == self.RESULT_NORMAL:
1140
-                success = (job_code == 0)
1141
-                if success:
1142
-                    result = 'SUCCESS'
1128
+            for index, playbook in enumerate(self.jobdir.playbooks):
1129
+                ansible_timeout = self.getAnsibleTimeout(
1130
+                    time_started, job_timeout)
1131
+                job_status, job_code = self.runAnsiblePlaybook(
1132
+                    playbook, ansible_timeout, phase='run', index=index)
1133
+                if job_status == self.RESULT_ABORTED:
1134
+                    return 'ABORTED'
1135
+                elif job_status == self.RESULT_TIMED_OUT:
1136
+                    # Set the pre-failure flag so this doesn't get
1137
+                    # overridden by a post-failure.
1138
+                    pre_failed = True
1139
+                    result = 'TIMED_OUT'
1140
+                    break
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 1148
                 else:
1144
-                    result = 'FAILURE'
1145
-            else:
1146
-                # The result of the job is indeterminate.  Zuul will
1147
-                # run it again.
1148
-                return None
1149
+                    # The result of the job is indeterminate.  Zuul will
1150
+                    # run it again.
1151
+                    return None
1149 1152
 
1150 1153
         # check if we need to pause here
1151 1154
         result_data = self.getResultData()
@@ -1343,14 +1346,15 @@ class AnsibleJob(object):
1343 1346
             jobdir_playbook = self.jobdir.addPrePlaybook()
1344 1347
             self.preparePlaybook(jobdir_playbook, playbook, args)
1345 1348
 
1349
+        job_playbook = None
1346 1350
         for playbook in args['playbooks']:
1347 1351
             jobdir_playbook = self.jobdir.addPlaybook()
1348 1352
             self.preparePlaybook(jobdir_playbook, playbook, args)
1349 1353
             if jobdir_playbook.path is not None:
1350
-                self.jobdir.playbook = jobdir_playbook
1351
-                break
1354
+                if job_playbook is None:
1355
+                    job_playbook = jobdir_playbook
1352 1356
 
1353
-        if self.jobdir.playbook is None:
1357
+        if job_playbook is None:
1354 1358
             raise ExecutorError("No playbook specified")
1355 1359
 
1356 1360
         for playbook in args['post_playbooks']:

Loading…
Cancel
Save