summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Wienand <iwienand@redhat.com>2022-10-03 15:10:07 +1100
committerIan Wienand <iwienand@redhat.com>2022-10-03 15:29:17 +1100
commit84309edbcb66aee180f41e66dc1ca65d1fd3f37d (patch)
tree9b918edc79e85432916431a53a5d085efae0a3dd
parenta0ba200ba70ce137780db397d681b1c3bd071fb8 (diff)
downloadzuul-84309edbcb66aee180f41e66dc1ca65d1fd3f37d.tar.gz
configloader: Fix error report for job nodesets
In a job nodeset, you do not need a "name", but some of the error logic assumes it. This leads to a rather confusing error return; for example something like job: ... nodeset: nodes: [] groups: - name: a_group nodes: - a_node_that_does_not_exist leads to Zuul encountered a syntax error while parsing its configuration in the repo opendev/system-config on branch master. The error was: 'name' when really it is trying to tell you is that "this_node_not_defined" isn't defined, but it tries to de-reference the name of the nodeset: and fails. This keeps track of the anonymous nodeset flag and if set, then rewords the error to make sense in the job context; for example it will now say: Zuul encountered a syntax error while parsing its configuration in the repo org/project on branch master. The error was: In the nodeset the group "a_group" contains a node named "a_node_that_does_not_exist" which is not defined in the nodeset. The error appears in the following job stanza: job: name: test job nodeset: nodes: [] groups: - name: a_group nodes: - a_node_that_does_not_exist Test cases are added to cover the updated messages. Change-Id: Ia24e66ba9b97618636023cb61d7c82aa7306d678
-rw-r--r--tests/unit/test_v3.py58
-rw-r--r--zuul/configloader.py14
2 files changed, 68 insertions, 4 deletions
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py
index fe5b46e0c..225c6d355 100644
--- a/tests/unit/test_v3.py
+++ b/tests/unit/test_v3.py
@@ -2232,6 +2232,64 @@ class TestInRepoConfig(ZuulTestCase):
self.assertIn('appears multiple times', A.messages[0],
"A should have a syntax error reported")
+ def test_group_in_job_with_invalid_node(self):
+ in_repo_conf = textwrap.dedent(
+ """
+ - job:
+ name: test job
+ nodeset:
+ nodes: []
+ groups:
+ - name: a_group
+ nodes:
+ - a_node_that_does_not_exist
+ """)
+
+ 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('which is not defined in the nodeset', A.messages[0],
+ "A should have a syntax error reported")
+
+ def test_duplicate_group_in_job(self):
+ in_repo_conf = textwrap.dedent(
+ """
+ - job:
+ name: test job
+ nodeset:
+ nodes:
+ - name: controller
+ label: ubuntu-focal
+ groups:
+ - name: a_duplicate_group
+ nodes:
+ - controller
+ - name: a_duplicate_group
+ nodes:
+ - controller
+ """)
+
+ 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(
+ 'Group names must be unique within a nodeset.',
+ A.messages[0], "A should have a syntax error reported")
+
def test_secret_not_found_error(self):
in_repo_conf = textwrap.dedent(
"""
diff --git a/zuul/configloader.py b/zuul/configloader.py
index a92f5337c..2b696a832 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -81,7 +81,7 @@ class ConfigurationSyntaxError(Exception):
class NodeFromGroupNotFoundError(Exception):
def __init__(self, nodeset, node, group):
message = textwrap.dedent("""\
- In nodeset "{nodeset}" the group "{group}" contains a
+ In {nodeset} the group "{group}" contains a
node named "{node}" which is not defined in the nodeset.""")
message = textwrap.fill(message.format(nodeset=nodeset,
node=node, group=group))
@@ -137,7 +137,7 @@ class MaxTimeoutError(Exception):
class DuplicateGroupError(Exception):
def __init__(self, nodeset, group):
message = textwrap.dedent("""\
- In nodeset "{nodeset}" the group "{group}" appears multiple times.
+ In {nodeset} the group "{group}" appears multiple times.
Group names must be unique within a nodeset.""")
message = textwrap.fill(message.format(nodeset=nodeset,
group=group))
@@ -476,6 +476,7 @@ class NodeSetParser(object):
def __init__(self, pcontext):
self.log = logging.getLogger("zuul.NodeSetParser")
self.pcontext = pcontext
+ self.anonymous = False
self.schema = self.getSchema(False)
self.anon_schema = self.getSchema(True)
@@ -513,6 +514,7 @@ class NodeSetParser(object):
def fromYaml(self, conf, anonymous=False):
if anonymous:
self.anon_schema(conf)
+ self.anonymous = True
else:
self.schema(conf)
@@ -565,10 +567,14 @@ class NodeSetParser(object):
raise Exception("Groups named 'localhost' are not allowed.")
for node_name in as_list(conf_group['nodes']):
if node_name not in node_names:
- raise NodeFromGroupNotFoundError(conf['name'], node_name,
+ nodeset_str = 'the nodeset' if self.anonymous else \
+ 'the nodeset "%s"' % conf['name']
+ raise NodeFromGroupNotFoundError(nodeset_str, node_name,
conf_group['name'])
if conf_group['name'] in group_names:
- raise DuplicateGroupError(conf['name'], conf_group['name'])
+ nodeset_str = 'the nodeset' if self.anonymous else \
+ 'the nodeset "%s"' % conf['name']
+ raise DuplicateGroupError(nodeset_str, conf_group['name'])
group = model.Group(conf_group['name'],
as_list(conf_group['nodes']))
ns.addGroup(group)