Merge "Report layout config errors for config repos" into feature/zuulv3
This commit is contained in:
commit
df1f701cb7
2
tests/fixtures/config/in-repo/git/common-config/playbooks/common-config-test.yaml
vendored
Normal file
2
tests/fixtures/config/in-repo/git/common-config/playbooks/common-config-test.yaml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
- hosts: all
|
||||||
|
tasks: []
|
|
@ -35,3 +35,12 @@
|
||||||
gerrit:
|
gerrit:
|
||||||
verified: 0
|
verified: 0
|
||||||
precedence: high
|
precedence: high
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: common-config-test
|
||||||
|
|
||||||
|
- project:
|
||||||
|
name: common-config
|
||||||
|
tenant-one-gate:
|
||||||
|
jobs:
|
||||||
|
- common-config-test
|
||||||
|
|
|
@ -185,7 +185,7 @@ class TestInRepoConfig(ZuulTestCase):
|
||||||
dict(name='project-test1', result='SUCCESS', changes='2,1'),
|
dict(name='project-test1', result='SUCCESS', changes='2,1'),
|
||||||
dict(name='project-test2', result='SUCCESS', changes='3,1')])
|
dict(name='project-test2', result='SUCCESS', changes='3,1')])
|
||||||
|
|
||||||
def test_dynamic_syntax_error(self):
|
def test_untrusted_syntax_error(self):
|
||||||
in_repo_conf = textwrap.dedent(
|
in_repo_conf = textwrap.dedent(
|
||||||
"""
|
"""
|
||||||
- job:
|
- job:
|
||||||
|
@ -206,6 +206,27 @@ class TestInRepoConfig(ZuulTestCase):
|
||||||
self.assertIn('syntax error', A.messages[1],
|
self.assertIn('syntax error', A.messages[1],
|
||||||
"A should have a syntax error reported")
|
"A should have a syntax error reported")
|
||||||
|
|
||||||
|
def test_trusted_syntax_error(self):
|
||||||
|
in_repo_conf = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
- job:
|
||||||
|
name: project-test2
|
||||||
|
foo: error
|
||||||
|
""")
|
||||||
|
|
||||||
|
file_dict = {'zuul.yaml': in_repo_conf}
|
||||||
|
A = self.fake_gerrit.addFakeChange('common-config', 'master', 'A',
|
||||||
|
files=file_dict)
|
||||||
|
A.addApproval('code-review', 2)
|
||||||
|
self.fake_gerrit.addEvent(A.addApproval('approved', 1))
|
||||||
|
self.waitUntilSettled()
|
||||||
|
|
||||||
|
self.assertEqual(A.data['status'], 'NEW')
|
||||||
|
self.assertEqual(A.reported, 2,
|
||||||
|
"A should report start and failure")
|
||||||
|
self.assertIn('syntax error', A.messages[1],
|
||||||
|
"A should have a syntax error reported")
|
||||||
|
|
||||||
|
|
||||||
class TestAnsible(AnsibleZuulTestCase):
|
class TestAnsible(AnsibleZuulTestCase):
|
||||||
# A temporary class to hold new tests while others are disabled
|
# A temporary class to hold new tests while others are disabled
|
||||||
|
|
|
@ -841,21 +841,41 @@ class ConfigLoader(object):
|
||||||
new_abide.tenants[tenant.name] = new_tenant
|
new_abide.tenants[tenant.name] = new_tenant
|
||||||
return new_abide
|
return new_abide
|
||||||
|
|
||||||
def createDynamicLayout(self, tenant, files):
|
def _loadDynamicProjectData(self, config, source, project, files,
|
||||||
config = tenant.config_repos_config.copy()
|
config_repo):
|
||||||
for source, project in tenant.project_repos:
|
for branch in source.getProjectBranches(project):
|
||||||
for branch in source.getProjectBranches(project):
|
data = None
|
||||||
|
if config_repo:
|
||||||
|
data = files.getFile(project.name, branch, 'zuul.yaml')
|
||||||
|
if not data:
|
||||||
data = files.getFile(project.name, branch, '.zuul.yaml')
|
data = files.getFile(project.name, branch, '.zuul.yaml')
|
||||||
if data:
|
if data:
|
||||||
source_context = model.SourceContext(project,
|
source_context = model.SourceContext(project, branch,
|
||||||
branch, False)
|
config_repo)
|
||||||
incdata = TenantParser._parseProjectRepoLayout(
|
if config_repo:
|
||||||
|
incdata = TenantParser._parseConfigRepoLayout(
|
||||||
data, source_context)
|
data, source_context)
|
||||||
else:
|
else:
|
||||||
incdata = project.unparsed_branch_config[branch]
|
incdata = TenantParser._parseProjectRepoLayout(
|
||||||
if not incdata:
|
data, source_context)
|
||||||
continue
|
else:
|
||||||
config.extend(incdata)
|
incdata = project.unparsed_branch_config.get(branch)
|
||||||
|
if not incdata:
|
||||||
|
continue
|
||||||
|
config.extend(incdata)
|
||||||
|
|
||||||
|
def createDynamicLayout(self, tenant, files, include_config_repos=False):
|
||||||
|
if include_config_repos:
|
||||||
|
config = model.UnparsedTenantConfig()
|
||||||
|
for source, project in tenant.config_repos:
|
||||||
|
self._loadDynamicProjectData(config, source, project,
|
||||||
|
files, True)
|
||||||
|
else:
|
||||||
|
config = tenant.config_repos_config.copy()
|
||||||
|
for source, project in tenant.project_repos:
|
||||||
|
self._loadDynamicProjectData(config, source, project,
|
||||||
|
files, False)
|
||||||
|
|
||||||
layout = model.Layout()
|
layout = model.Layout()
|
||||||
# TODOv3(jeblair): copying the pipelines could be dangerous/confusing.
|
# TODOv3(jeblair): copying the pipelines could be dangerous/confusing.
|
||||||
layout.pipelines = tenant.layout.pipelines
|
layout.pipelines = tenant.layout.pipelines
|
||||||
|
|
|
@ -466,6 +466,43 @@ class PipelineManager(object):
|
||||||
newrev=newrev,
|
newrev=newrev,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _loadDynamicLayout(self, item):
|
||||||
|
# Load layout
|
||||||
|
# Late import to break an import loop
|
||||||
|
import zuul.configloader
|
||||||
|
loader = zuul.configloader.ConfigLoader()
|
||||||
|
|
||||||
|
build_set = item.current_build_set
|
||||||
|
self.log.debug("Load dynamic layout with %s" % build_set.files)
|
||||||
|
try:
|
||||||
|
# First parse the config with as it will land with the
|
||||||
|
# full set of config and project repos. This lets us
|
||||||
|
# catch syntax errors in config repos even though we won't
|
||||||
|
# actually run with that config.
|
||||||
|
loader.createDynamicLayout(
|
||||||
|
item.pipeline.layout.tenant,
|
||||||
|
build_set.files,
|
||||||
|
include_config_repos=True)
|
||||||
|
|
||||||
|
# Then create the config a second time but without changes
|
||||||
|
# to config repos so that we actually use this config.
|
||||||
|
layout = loader.createDynamicLayout(
|
||||||
|
item.pipeline.layout.tenant,
|
||||||
|
build_set.files,
|
||||||
|
include_config_repos=False)
|
||||||
|
except zuul.configloader.ConfigurationSyntaxError as e:
|
||||||
|
self.log.info("Configuration syntax error "
|
||||||
|
"in dynamic layout %s" %
|
||||||
|
build_set.files)
|
||||||
|
item.setConfigError(str(e))
|
||||||
|
return None
|
||||||
|
except Exception:
|
||||||
|
self.log.exception("Error in dynamic layout %s" %
|
||||||
|
build_set.files)
|
||||||
|
item.setConfigError("Unknown configuration error")
|
||||||
|
return None
|
||||||
|
return layout
|
||||||
|
|
||||||
def getLayout(self, item):
|
def getLayout(self, item):
|
||||||
if not item.change.updatesConfig():
|
if not item.change.updatesConfig():
|
||||||
if item.item_ahead:
|
if item.item_ahead:
|
||||||
|
@ -479,27 +516,7 @@ class PipelineManager(object):
|
||||||
if build_set.merge_state == build_set.COMPLETE:
|
if build_set.merge_state == build_set.COMPLETE:
|
||||||
if build_set.unable_to_merge:
|
if build_set.unable_to_merge:
|
||||||
return None
|
return None
|
||||||
# Load layout
|
return self._loadDynamicLayout(item)
|
||||||
# Late import to break an import loop
|
|
||||||
import zuul.configloader
|
|
||||||
loader = zuul.configloader.ConfigLoader()
|
|
||||||
self.log.debug("Load dynamic layout with %s" % build_set.files)
|
|
||||||
try:
|
|
||||||
layout = loader.createDynamicLayout(
|
|
||||||
item.pipeline.layout.tenant,
|
|
||||||
build_set.files)
|
|
||||||
except zuul.configloader.ConfigurationSyntaxError as e:
|
|
||||||
self.log.info("Configuration syntax error "
|
|
||||||
"in dynamic layout %s" %
|
|
||||||
build_set.files)
|
|
||||||
item.setConfigError(str(e))
|
|
||||||
return None
|
|
||||||
except Exception:
|
|
||||||
self.log.exception("Error in dynamic layout %s" %
|
|
||||||
build_set.files)
|
|
||||||
item.setConfigError("Unknown configuration error")
|
|
||||||
return None
|
|
||||||
return layout
|
|
||||||
build_set.merge_state = build_set.PENDING
|
build_set.merge_state = build_set.PENDING
|
||||||
self.log.debug("Preparing dynamic layout for: %s" % item.change)
|
self.log.debug("Preparing dynamic layout for: %s" % item.change)
|
||||||
dependent_items = self.getDependentItems(item)
|
dependent_items = self.getDependentItems(item)
|
||||||
|
@ -508,7 +525,7 @@ class PipelineManager(object):
|
||||||
merger_items = map(self._makeMergerItem, all_items)
|
merger_items = map(self._makeMergerItem, all_items)
|
||||||
self.sched.merger.mergeChanges(merger_items,
|
self.sched.merger.mergeChanges(merger_items,
|
||||||
item.current_build_set,
|
item.current_build_set,
|
||||||
['.zuul.yaml'],
|
['zuul.yaml', '.zuul.yaml'],
|
||||||
self.pipeline.precedence)
|
self.pipeline.precedence)
|
||||||
|
|
||||||
def prepareLayout(self, item):
|
def prepareLayout(self, item):
|
||||||
|
|
Loading…
Reference in New Issue