Merge "Provide error message on malformed job list" into feature/zuulv3
diff --git a/tests/fixtures/layouts/delayed-repo-init.yaml b/tests/fixtures/layouts/delayed-repo-init.yaml
index e97d37a..c89e2fa 100644
--- a/tests/fixtures/layouts/delayed-repo-init.yaml
+++ b/tests/fixtures/layouts/delayed-repo-init.yaml
@@ -67,7 +67,7 @@
dependencies: project-merge
gate:
jobs:
- - project-merge:
+ - project-merge
- project-test1:
dependencies: project-merge
- project-test2:
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py
index 78524f2..4b0bfc5 100755
--- a/tests/unit/test_v3.py
+++ b/tests/unit/test_v3.py
@@ -851,6 +851,60 @@
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(
"""
diff --git a/zuul/configloader.py b/zuul/configloader.py
index 6a9ba01..2b91966 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -11,6 +11,7 @@
# under the License.
import base64
+import collections
from contextlib import contextmanager
import copy
import os
@@ -397,39 +398,42 @@
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 @@
'_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 @@
'_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):