diff --git a/tests/fixtures/layouts/broken-template.yaml b/tests/fixtures/layouts/broken-template.yaml new file mode 100644 index 0000000000..1d56cf7cc1 --- /dev/null +++ b/tests/fixtures/layouts/broken-template.yaml @@ -0,0 +1,27 @@ +- pipeline: + name: gate + manager: dependent + trigger: + gerrit: + - event: comment-added + approval: + - Approved: 1 + success: + gerrit: + Verified: 2 + submit: true + failure: + gerrit: + Verified: -2 + start: + gerrit: + Verified: 0 + +- job: + name: base + parent: null + +- project: + name: org/project + templates: + - does-not-exist diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py index 0aa6aea7ea..284a4fe14e 100755 --- a/tests/unit/test_v3.py +++ b/tests/unit/test_v3.py @@ -25,7 +25,12 @@ from unittest import skip import zuul.configloader from zuul.lib import encryption -from tests.base import AnsibleZuulTestCase, ZuulTestCase, FIXTURE_DIR +from tests.base import ( + AnsibleZuulTestCase, + ZuulTestCase, + FIXTURE_DIR, + simple_layout, +) class TestMultipleTenants(AnsibleZuulTestCase): @@ -2462,6 +2467,18 @@ class TestBrokenConfig(ZuulTestCase): "Zuul encountered a syntax error", str(tenant.layout.loading_errors[0].error)) + @simple_layout('layouts/broken-template.yaml') + def test_broken_config_on_startup_template(self): + # Verify that a missing project-template doesn't break gate + # pipeline construction. + tenant = self.sched.abide.tenants.get('tenant-one') + self.assertEquals( + len(tenant.layout.loading_errors), 1, + "An error should have been stored") + self.assertIn( + "Zuul encountered a syntax error", + str(tenant.layout.loading_errors[0].error)) + def test_dynamic_ignore(self): # Verify dynamic config behaviors inside a tenant broken config tenant = self.sched.abide.tenants.get('tenant-one') diff --git a/zuul/model.py b/zuul/model.py index 4e41dd2907..24162c6942 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -159,6 +159,11 @@ class NoMatchingParentError(Exception): pass +class TemplateNotFoundError(Exception): + """A project referenced a template that does not exist.""" + pass + + class Attributes(object): """A class to hold attributes for string formatting.""" @@ -3098,7 +3103,7 @@ class Layout(object): def getProjectTemplates(self, name): pt = self.project_templates.get(name, None) if pt is None: - raise Exception("Project template %s not found" % name) + raise TemplateNotFoundError("Project template %s not found" % name) return pt def addProjectConfig(self, project_config): @@ -3120,13 +3125,17 @@ class Layout(object): def getAllProjectConfigs(self, name): # Get all the project configs (project and project-template # stanzas) for a project. - ret = [] - for pc in self.getProjectConfigs(name): - ret.append(pc) - for template_name in pc.templates: - templates = self.getProjectTemplates(template_name) - ret.extend(templates) - return ret + try: + ret = [] + for pc in self.getProjectConfigs(name): + ret.append(pc) + for template_name in pc.templates: + templates = self.getProjectTemplates(template_name) + ret.extend(templates) + return ret + except TemplateNotFoundError as e: + self.log.warning("%s for project %s" % (e, name)) + return [] def getProjectMetadata(self, name): if name in self.project_metadata: