Provide error message on malformed job list
In project and project-template definitions, the existing voluptuous schema for the jobs in the job list was vs.Any(str, dict). The contents of the dict itself need to be validated though, the job being a dict that looks like: check: jobs: - project-test1: - required-projects: org/project2 Is invalid as the contents of the build-openstack-sphinx-docs job dict should themselves be a string or a dict rather than a list. This updates the error to be: Zuul encountered a syntax error while parsing its configuration in the repo org/project on branch master. The error was: expected str for dictionary value @ data['check']['jobs'][0]['project-test1'] The error appears in the following project stanza: project: name: org/project1 check: jobs: - project-test1: - required-projects: org/project2 in "org/project/.zuul.yaml@master", line 4, column 3 The error, 'expected str for dictionary value' could probably be improved at some point, but this is at least an error with a message which is way better than 'Unknown configuration error'. Split out the attributes of the job in the JobParser voluptuous schema that can be used in job lists from the ones that can't. For now it's only name that can't be used. Also fix a test fixture that had a trailing : in it. Change-Id: I217eb5d6befbed51b220d47afa18997a87982389
This commit is contained in:
parent
950c6b1e2d
commit
8be3c0c5a3
@ -67,7 +67,7 @@
|
||||
dependencies: project-merge
|
||||
gate:
|
||||
jobs:
|
||||
- project-merge:
|
||||
- project-merge
|
||||
- project-test1:
|
||||
dependencies: project-merge
|
||||
- project-test2:
|
||||
|
@ -816,6 +816,60 @@ class TestInRepoConfig(ZuulTestCase):
|
||||
A.messages[0],
|
||||
"A should have a syntax error reported")
|
||||
|
||||
def test_job_list_in_project_template_not_dict_error(self):
|
||||
in_repo_conf = textwrap.dedent(
|
||||
"""
|
||||
- job:
|
||||
name: project-test1
|
||||
- project-template:
|
||||
name: some-jobs
|
||||
check:
|
||||
jobs:
|
||||
- project-test1:
|
||||
- required-projects:
|
||||
org/project2
|
||||
""")
|
||||
|
||||
file_dict = {'.zuul.yaml': in_repo_conf}
|
||||
A = self.fake_gerrit.addFakeChange('org/project', '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, 1,
|
||||
"A should report failure")
|
||||
self.assertIn('expected str for dictionary value',
|
||||
A.messages[0], "A should have a syntax error reported")
|
||||
|
||||
def test_job_list_in_project_not_dict_error(self):
|
||||
in_repo_conf = textwrap.dedent(
|
||||
"""
|
||||
- job:
|
||||
name: project-test1
|
||||
- project:
|
||||
name: org/project1
|
||||
check:
|
||||
jobs:
|
||||
- project-test1:
|
||||
- required-projects:
|
||||
org/project2
|
||||
""")
|
||||
|
||||
file_dict = {'.zuul.yaml': in_repo_conf}
|
||||
A = self.fake_gerrit.addFakeChange('org/project', '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, 1,
|
||||
"A should report failure")
|
||||
self.assertIn('expected str for dictionary value',
|
||||
A.messages[0], "A should have a syntax error reported")
|
||||
|
||||
def test_multi_repo(self):
|
||||
downstream_repo_conf = textwrap.dedent(
|
||||
"""
|
||||
|
@ -11,6 +11,7 @@
|
||||
# under the License.
|
||||
|
||||
import base64
|
||||
import collections
|
||||
from contextlib import contextmanager
|
||||
import copy
|
||||
import os
|
||||
@ -397,39 +398,42 @@ class JobParser(object):
|
||||
secret = {vs.Required('name'): str,
|
||||
vs.Required('secret'): str}
|
||||
|
||||
job = {vs.Required('name'): str,
|
||||
'parent': vs.Any(str, None),
|
||||
'final': bool,
|
||||
'failure-message': str,
|
||||
'success-message': str,
|
||||
'failure-url': str,
|
||||
'success-url': str,
|
||||
'hold-following-changes': bool,
|
||||
'voting': bool,
|
||||
'semaphore': str,
|
||||
'tags': to_list(str),
|
||||
'branches': to_list(str),
|
||||
'files': to_list(str),
|
||||
'secrets': to_list(vs.Any(secret, str)),
|
||||
'irrelevant-files': to_list(str),
|
||||
# validation happens in NodeSetParser
|
||||
'nodeset': vs.Any(dict, str),
|
||||
'timeout': int,
|
||||
'attempts': int,
|
||||
'pre-run': to_list(str),
|
||||
'post-run': to_list(str),
|
||||
'run': str,
|
||||
'_source_context': model.SourceContext,
|
||||
'_start_mark': ZuulMark,
|
||||
'roles': to_list(role),
|
||||
'required-projects': to_list(vs.Any(job_project, str)),
|
||||
'vars': dict,
|
||||
'dependencies': to_list(str),
|
||||
'allowed-projects': to_list(str),
|
||||
'override-branch': str,
|
||||
'description': str,
|
||||
'post-review': bool
|
||||
}
|
||||
# Attributes of a job that can also be used in Project and ProjectTemplate
|
||||
job_attributes = {'parent': vs.Any(str, None),
|
||||
'final': bool,
|
||||
'failure-message': str,
|
||||
'success-message': str,
|
||||
'failure-url': str,
|
||||
'success-url': str,
|
||||
'hold-following-changes': bool,
|
||||
'voting': bool,
|
||||
'semaphore': str,
|
||||
'tags': to_list(str),
|
||||
'branches': to_list(str),
|
||||
'files': to_list(str),
|
||||
'secrets': to_list(vs.Any(secret, str)),
|
||||
'irrelevant-files': to_list(str),
|
||||
# validation happens in NodeSetParser
|
||||
'nodeset': vs.Any(dict, str),
|
||||
'timeout': int,
|
||||
'attempts': int,
|
||||
'pre-run': to_list(str),
|
||||
'post-run': to_list(str),
|
||||
'run': str,
|
||||
'_source_context': model.SourceContext,
|
||||
'_start_mark': ZuulMark,
|
||||
'roles': to_list(role),
|
||||
'required-projects': to_list(vs.Any(job_project, str)),
|
||||
'vars': dict,
|
||||
'dependencies': to_list(str),
|
||||
'allowed-projects': to_list(str),
|
||||
'override-branch': str,
|
||||
'description': str,
|
||||
'post-review': bool}
|
||||
|
||||
job_name = {vs.Required('name'): str}
|
||||
|
||||
job = dict(collections.ChainMap(job_name, job_attributes))
|
||||
|
||||
schema = vs.Schema(job)
|
||||
|
||||
@ -725,9 +729,12 @@ class ProjectTemplateParser(object):
|
||||
'_start_mark': ZuulMark,
|
||||
}
|
||||
|
||||
job = {str: vs.Any(str, JobParser.job_attributes)}
|
||||
job_list = [vs.Any(str, job)]
|
||||
pipeline_contents = {'queue': str, 'jobs': job_list}
|
||||
|
||||
for p in self.layout.pipelines.values():
|
||||
project_template[p.name] = {'queue': str,
|
||||
'jobs': [vs.Any(str, dict)]}
|
||||
project_template[p.name] = pipeline_contents
|
||||
return vs.Schema(project_template)
|
||||
|
||||
def fromYaml(self, conf, validate=True):
|
||||
@ -796,9 +803,12 @@ class ProjectParser(object):
|
||||
'_start_mark': ZuulMark,
|
||||
}
|
||||
|
||||
job = {str: vs.Any(str, JobParser.job_attributes)}
|
||||
job_list = [vs.Any(str, job)]
|
||||
pipeline_contents = {'queue': str, 'jobs': job_list}
|
||||
|
||||
for p in self.layout.pipelines.values():
|
||||
project[p.name] = {'queue': str,
|
||||
'jobs': [vs.Any(str, dict)]}
|
||||
project[p.name] = pipeline_contents
|
||||
return vs.Schema(project)
|
||||
|
||||
def fromYaml(self, conf_list):
|
||||
|
Loading…
x
Reference in New Issue
Block a user