Browse Source

Tolerate missing project

Zuul fails to load if the tenant config references an inexistent or
inaccessible project. This should not happen and also be added to the
loading errors. This is especially important with github where users
can freely rename, create and delete repos in their organizations.

Change-Id: I99bc50e98c7edfd2767f950d4898ea8298d7ca94
changes/72/579872/11
Tobias Henkel 3 years ago
committed by Tobias Henkel
parent
commit
9f7c642ae1
  1. 1
      tests/fixtures/config/broken/main.yaml
  2. 18
      tests/unit/test_v3.py
  3. 17
      tests/unit/test_web.py
  4. 34
      zuul/configloader.py
  5. 17
      zuul/model.py

1
tests/fixtures/config/broken/main.yaml

@ -7,3 +7,4 @@
untrusted-projects:
- org/project
- org/project2
- org/project3

18
tests/unit/test_v3.py

@ -2860,12 +2860,16 @@ class TestBrokenConfig(ZuulTestCase):
def test_broken_config_on_startup(self):
# verify get the errors at tenant level.
tenant = self.sched.abide.tenants.get('tenant-one')
loading_errors = tenant.layout.loading_errors
self.assertEquals(
len(tenant.layout.loading_errors), 1,
len(tenant.layout.loading_errors), 2,
"An error should have been stored")
self.assertIn(
"Zuul encountered an error while accessing the repo org/project3",
str(loading_errors[0].error))
self.assertIn(
"Zuul encountered a syntax error",
str(tenant.layout.loading_errors[0].error))
str(loading_errors[1].error))
@simple_layout('layouts/broken-template.yaml')
def test_broken_config_on_startup_template(self):
@ -2895,7 +2899,7 @@ class TestBrokenConfig(ZuulTestCase):
tenant = self.sched.abide.tenants.get('tenant-one')
# There is a configuration error
self.assertEquals(
len(tenant.layout.loading_errors), 1,
len(tenant.layout.loading_errors), 2,
"An error should have been stored")
# Inside a broken tenant configuration environment,
@ -2927,7 +2931,7 @@ class TestBrokenConfig(ZuulTestCase):
tenant = self.sched.abide.tenants.get('tenant-one')
# There is a configuration error
self.assertEquals(
len(tenant.layout.loading_errors), 1,
len(tenant.layout.loading_errors), 2,
"An error should have been stored")
# Inside a broken tenant configuration environment,
@ -2961,7 +2965,7 @@ class TestBrokenConfig(ZuulTestCase):
tenant = self.sched.abide.tenants.get('tenant-one')
# There is a configuration error
self.assertEquals(
len(tenant.layout.loading_errors), 1,
len(tenant.layout.loading_errors), 2,
"An error should have been stored")
# Inside a broken tenant configuration environment,
@ -2995,7 +2999,7 @@ class TestBrokenConfig(ZuulTestCase):
tenant = self.sched.abide.tenants.get('tenant-one')
# There is a configuration error
self.assertEquals(
len(tenant.layout.loading_errors), 1,
len(tenant.layout.loading_errors), 2,
"An error should have been stored")
# Inside a broken tenant configuration environment,
@ -3027,7 +3031,7 @@ class TestBrokenConfig(ZuulTestCase):
tenant = self.sched.abide.tenants.get('tenant-one')
# There is a configuration error
self.assertEquals(
len(tenant.layout.loading_errors), 1,
len(tenant.layout.loading_errors), 2,
"An error should have been stored")
# Inside a broken tenant configuration environment, remove a

17
tests/unit/test_web.py

@ -830,15 +830,22 @@ class TestTenantInfoConfigBroken(BaseTestWeb):
config_errors = self.get_url(
"api/tenant/tenant-one/config-errors").json()
self.assertEqual(
len(config_errors), 1)
len(config_errors), 2)
self.assertEqual(
config_errors[0]['source_context']['project'], 'org/project2')
config_errors[0]['source_context']['project'], 'org/project3')
self.assertIn('Zuul encountered an error while accessing the repo '
'org/project3',
config_errors[0]['error'])
self.assertEqual(
config_errors[0]['source_context']['branch'], 'master')
config_errors[1]['source_context']['project'], 'org/project2')
self.assertEqual(
config_errors[0]['source_context']['path'], '.zuul.yaml')
config_errors[1]['source_context']['branch'], 'master')
self.assertEqual(
config_errors[1]['source_context']['path'], '.zuul.yaml')
self.assertIn('Zuul encountered a syntax error',
config_errors[0]['error'])
config_errors[1]['error'])
resp = self.get_url("api/tenant/non-tenant/config-errors")
self.assertEqual(404, resp.status_code)

34
zuul/configloader.py

@ -195,6 +195,29 @@ def indent(s):
return '\n'.join([' ' + x for x in s.split('\n')])
@contextmanager
def project_configuration_exceptions(context, accumulator):
try:
yield
except ConfigurationSyntaxError:
raise
except Exception as e:
intro = textwrap.fill(textwrap.dedent("""\
Zuul encountered an error while accessing the repo {repo}. The error
was:""".format(
repo=context.project.name,
)))
m = textwrap.dedent("""\
{intro}
{error}""")
m = m.format(intro=intro,
error=indent(str(e)))
accumulator.addError(context, None, m)
@contextmanager
def early_configuration_exceptions(context):
try:
@ -1396,13 +1419,16 @@ class TenantParser(object):
for tpc in untrusted_tpcs:
tenant.addUntrustedProject(tpc)
for tpc in config_tpcs + untrusted_tpcs:
self._getProjectBranches(tenant, tpc)
self._resolveShadowProjects(tenant, tpc)
# We prepare a stack to store config loading issues
loading_errors = model.LoadingErrors()
for tpc in config_tpcs + untrusted_tpcs:
source_context = model.ProjectContext(tpc.project)
with project_configuration_exceptions(source_context,
loading_errors):
self._getProjectBranches(tenant, tpc)
self._resolveShadowProjects(tenant, tpc)
# Set default ansible version
default_ansible_version = conf.get('default-ansible-version')
if default_ansible_version is not None:

17
zuul/model.py

@ -866,6 +866,23 @@ class SecretUse(ConfigObject):
self.pass_to_parent = False
class ProjectContext(ConfigObject):
def __init__(self, project):
super().__init__()
self.project = project
self.branch = None
self.path = None
def __str__(self):
return self.project.name
def toDict(self):
return dict(
project=self.project.name,
)
class SourceContext(ConfigObject):
"""A reference to the branch of a project in configuration.

Loading…
Cancel
Save