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
This commit is contained in:
parent
87a44e08f6
commit
9f7c642ae1
|
@ -7,3 +7,4 @@
|
|||
untrusted-projects:
|
||||
- org/project
|
||||
- org/project2
|
||||
- org/project3
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -830,16 +830,23 @@ 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')
|
||||
self.assertEqual(
|
||||
config_errors[0]['source_context']['branch'], 'master')
|
||||
self.assertEqual(
|
||||
config_errors[0]['source_context']['path'], '.zuul.yaml')
|
||||
self.assertIn('Zuul encountered a syntax error',
|
||||
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[1]['source_context']['project'], 'org/project2')
|
||||
self.assertEqual(
|
||||
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[1]['error'])
|
||||
|
||||
resp = self.get_url("api/tenant/non-tenant/config-errors")
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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…
Reference in New Issue