diff --git a/tests/fixtures/layouts/timer-template.yaml b/tests/fixtures/layouts/timer-template.yaml new file mode 100644 index 0000000000..8903f03062 --- /dev/null +++ b/tests/fixtures/layouts/timer-template.yaml @@ -0,0 +1,61 @@ +- pipeline: + name: check + manager: independent + trigger: + gerrit: + - event: patchset-created + success: + gerrit: + Verified: 1 + failure: + gerrit: + Verified: -1 + +- pipeline: + name: periodic + manager: independent + trigger: + timer: + - time: '* * * * * */1' + +- job: + name: base + parent: null + run: playbooks/base.yaml + +- job: + name: project-test1 + run: playbooks/project-test1.yaml + +- job: + name: project-test2 + run: playbooks/project-test2.yaml + +- job: + name: project-bitrot + nodeset: + nodes: + - name: static + label: ubuntu-xenial + run: playbooks/project-bitrot.yaml + +- project: + name: org/project1 + check: + jobs: + - project-test1 + +- project-template: + name: periodic-jobs + periodic: + jobs: + - project-bitrot + +- project: + name: org/project + templates: + - periodic-jobs + check: + jobs: + - project-test1 + - project-test2 diff --git a/tests/unit/test_scheduler.py b/tests/unit/test_scheduler.py index 3d450fd679..8495df05d4 100644 --- a/tests/unit/test_scheduler.py +++ b/tests/unit/test_scheduler.py @@ -3396,6 +3396,57 @@ class TestScheduler(ZuulTestCase): self.assertEqual(results.get(build.uuid, ''), build.parameters['zuul'].get('jobtags')) + def test_timer_template(self): + "Test that a periodic job is triggered" + # This test can not use simple_layout because it must start + # with a configuration which does not include a + # timer-triggered job so that we have an opportunity to set + # the hold flag before the first job. + self.create_branch('org/project', 'stable') + self.executor_server.hold_jobs_in_build = True + self.commitConfigUpdate('common-config', 'layouts/timer-template.yaml') + self.sched.reconfigure(self.config) + + # The pipeline triggers every second, so we should have seen + # several by now. + time.sleep(5) + self.waitUntilSettled() + + self.assertEqual(len(self.builds), 2) + + merge_count_project1 = 0 + for job in self.gearman_server.jobs_history: + if job.name == b'merger:refstate': + args = job.arguments + if isinstance(args, bytes): + args = args.decode('utf-8') + args = json.loads(args) + if args["items"][0]["project"] == "org/project1": + merge_count_project1 += 1 + self.assertEquals(merge_count_project1, 0, + "project1 shouldn't have any refstate call") + + self.executor_server.hold_jobs_in_build = False + # Stop queuing timer triggered jobs so that the assertions + # below don't race against more jobs being queued. + self.commitConfigUpdate('common-config', 'layouts/no-timer.yaml') + self.sched.reconfigure(self.config) + self.waitUntilSettled() + # If APScheduler is in mid-event when we remove the job, we + # can end up with one more event firing, so give it an extra + # second to settle. + time.sleep(1) + self.waitUntilSettled() + self.executor_server.release() + self.waitUntilSettled() + + self.assertHistory([ + dict(name='project-bitrot', result='SUCCESS', + ref='refs/heads/master'), + dict(name='project-bitrot', result='SUCCESS', + ref='refs/heads/stable'), + ], ordered=False) + def test_timer(self): "Test that a periodic job is triggered" # This test can not use simple_layout because it must start diff --git a/zuul/driver/timer/__init__.py b/zuul/driver/timer/__init__.py index f3273425d1..b5c6087c87 100644 --- a/zuul/driver/timer/__init__.py +++ b/zuul/driver/timer/__init__.py @@ -83,7 +83,8 @@ class TimerDriver(Driver, TriggerInterface): # timer operates on branch heads and doesn't need speculative # layouts to decide if it should be enqueued or not. # So it can be decided on cached data if it needs to run or not. - if not [True for pc in pcs if pipeline_name in pc.pipelines]: + pcst = tenant.layout.getAllProjectConfigs(project_name) + if not [True for pc in pcst if pipeline_name in pc.pipelines]: continue (trusted, project) = tenant.getProject(project_name)