Use NodesetNotFoundError class

This error exception class went unused, likely due to complications
from circular imports.

To resolve this, move all of the configuration error exceptions
into the exceptions.py file so they can be imported in both
model.py and configloader.py.

Change-Id: I19b0f078f4d215a2e14c2c7ed893ab225d1e1084
This commit is contained in:
James E. Blair 2024-03-15 10:08:35 -07:00
parent 4680c58a27
commit 1350ce8ad6
7 changed files with 239 additions and 216 deletions

View File

@ -5199,7 +5199,7 @@ class TestValidateAllBroken(ZuulTestCase):
scheduler_count = 1
def setUp(self):
self.assertRaises(zuul.configloader.ConfigurationSyntaxError,
self.assertRaises(zuul.exceptions.ConfigurationSyntaxError,
super().setUp)
def test_validate_all_tenants_broken(self):
@ -5223,7 +5223,7 @@ class TestValidateBroken(ZuulTestCase):
scheduler_count = 1
def setUp(self):
self.assertRaises(zuul.configloader.ConfigurationSyntaxError,
self.assertRaises(zuul.exceptions.ConfigurationSyntaxError,
super().setUp)
def test_validate_tenant_broken(self):

View File

@ -43,6 +43,23 @@ from zuul.lib.re2util import filter_allowed_disallowed, ZuulRegex
from zuul.lib.varnames import check_varnames
from zuul.zk.components import COMPONENT_REGISTRY
from zuul.zk.semaphore import SemaphoreHandler
from zuul.exceptions import (
SEVERITY_ERROR,
DuplicateGroupError,
DuplicateNodeError,
GlobalSemaphoreNotFoundError,
LabelForbiddenError,
MaxTimeoutError,
MultipleProjectConfigurations,
NodeFromGroupNotFoundError,
PipelineNotPermittedError,
ProjectNotFoundError,
ProjectNotPermittedError,
RegexDeprecation,
TemplateNotFoundError,
UnknownConnection,
YAMLDuplicateKeyError,
)
ZUUL_CONF_ROOT = ('zuul.yaml', 'zuul.d', '.zuul.yaml', '.zuul.d')
@ -104,207 +121,6 @@ def indent(s):
return '\n'.join([' ' + x for x in s.split('\n')])
class ConfigurationSyntaxError(Exception):
zuul_error_name = 'Unknown Configuration Error'
zuul_error_severity = model.SEVERITY_ERROR
class NodeFromGroupNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Node From Group Not Found'
def __init__(self, nodeset, node, group):
message = textwrap.dedent("""\
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))
super(NodeFromGroupNotFoundError, self).__init__(message)
class DuplicateNodeError(ConfigurationSyntaxError):
zuul_error_name = 'Duplicate Node'
def __init__(self, nodeset, node):
message = textwrap.dedent("""\
In nodeset "{nodeset}" the node "{node}" appears multiple times.
Node names must be unique within a nodeset.""")
message = textwrap.fill(message.format(nodeset=nodeset,
node=node))
super(DuplicateNodeError, self).__init__(message)
class UnknownConnection(ConfigurationSyntaxError):
zuul_error_name = 'Unknown Connection'
def __init__(self, connection_name):
message = textwrap.dedent("""\
Unknown connection named "{connection}".""")
message = textwrap.fill(message.format(connection=connection_name))
super(UnknownConnection, self).__init__(message)
class LabelForbiddenError(ConfigurationSyntaxError):
zuul_error_name = 'Label Forbidden'
def __init__(self, label, allowed_labels, disallowed_labels):
message = textwrap.dedent("""\
Label named "{label}" is not part of the allowed
labels ({allowed_labels}) for this tenant.""")
# Make a string that looks like "a, b and not c, d" if we have
# both allowed and disallowed labels.
labels = ", ".join(allowed_labels or [])
if allowed_labels and disallowed_labels:
labels += ' and '
if disallowed_labels:
labels += 'not '
labels += ", ".join(disallowed_labels)
message = textwrap.fill(message.format(
label=label,
allowed_labels=labels))
super(LabelForbiddenError, self).__init__(message)
class MaxTimeoutError(ConfigurationSyntaxError):
zuul_error_name = 'Max Timeout Exceeded'
def __init__(self, job, tenant):
message = textwrap.dedent("""\
The job "{job}" exceeds tenant max-job-timeout {maxtimeout}.""")
message = textwrap.fill(message.format(
job=job.name, maxtimeout=tenant.max_job_timeout))
super(MaxTimeoutError, self).__init__(message)
class DuplicateGroupError(ConfigurationSyntaxError):
zuul_error_name = 'Duplicate Nodeset Group'
def __init__(self, nodeset, group):
message = textwrap.dedent("""\
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))
super(DuplicateGroupError, self).__init__(message)
class ProjectNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Project Not Found'
def __init__(self, project):
message = textwrap.dedent("""\
The project "{project}" was not found. All projects
referenced within a Zuul configuration must first be
added to the main configuration file by the Zuul
administrator.""")
message = textwrap.fill(message.format(project=project))
super(ProjectNotFoundError, self).__init__(message)
class TemplateNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Template Not Found'
def __init__(self, template):
message = textwrap.dedent("""\
The project template "{template}" was not found.
""")
message = textwrap.fill(message.format(template=template))
super(TemplateNotFoundError, self).__init__(message)
class NodesetNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Nodeset Not Found'
def __init__(self, nodeset):
message = textwrap.dedent("""\
The nodeset "{nodeset}" was not found.
""")
message = textwrap.fill(message.format(nodeset=nodeset))
super(NodesetNotFoundError, self).__init__(message)
class PipelineNotPermittedError(ConfigurationSyntaxError):
zuul_error_name = 'Pipeline Forbidden'
def __init__(self):
message = textwrap.dedent("""\
Pipelines may not be defined in untrusted repos,
they may only be defined in config repos.""")
message = textwrap.fill(message)
super(PipelineNotPermittedError, self).__init__(message)
class ProjectNotPermittedError(ConfigurationSyntaxError):
zuul_error_name = 'Project Forbidden'
def __init__(self):
message = textwrap.dedent("""\
Within an untrusted project, the only project definition
permitted is that of the project itself.""")
message = textwrap.fill(message)
super(ProjectNotPermittedError, self).__init__(message)
class GlobalSemaphoreNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Global Semaphore Not Found'
def __init__(self, semaphore):
message = textwrap.dedent("""\
The global semaphore "{semaphore}" was not found. All
global semaphores must be added to the main configuration
file by the Zuul administrator.""")
message = textwrap.fill(message.format(semaphore=semaphore))
super(GlobalSemaphoreNotFoundError, self).__init__(message)
class YAMLDuplicateKeyError(ConfigurationSyntaxError):
def __init__(self, key, source_context, start_mark):
self.source_context = source_context
self.start_mark = start_mark
message = (f'The key "{key}" appears more than once; '
'duplicate keys are not permitted.')
super(YAMLDuplicateKeyError, self).__init__(message)
class ConfigurationSyntaxWarning:
zuul_error_name = 'Unknown Configuration Warning'
zuul_error_severity = model.SEVERITY_WARNING
zuul_error_message = 'Unknown Configuration Warning'
def __init__(self, message=None):
if message:
self.zuul_error_message = message
class MultipleProjectConfigurations(ConfigurationSyntaxWarning):
zuul_error_name = 'Multiple Project Configurations'
zuul_error_problem = 'configuration error'
def __init__(self, source_context):
message = textwrap.dedent(f"""\
Configuration in {source_context.path} ignored because project-branch
is already configured.""")
message = textwrap.fill(message)
super().__init__(message)
class DeprecationWarning(ConfigurationSyntaxWarning):
zuul_error_problem = 'deprecated syntax'
class RegexDeprecation(DeprecationWarning):
zuul_error_name = 'Regex Deprecation'
zuul_error_message = """\
All regular expressions must conform to RE2 syntax, but an
expression using the deprecated Perl-style syntax has been detected.
Adjust the configuration to conform to RE2 syntax."""
def __init__(self, message=None):
if message:
message = (self.zuul_error_message +
f"\n\nThe RE2 syntax error is: {message}")
super().__init__(message)
class LocalAccumulator:
"""An error accumulator that wraps another accumulator (like
LoadingErrors) while holding local context information.
@ -430,7 +246,7 @@ class LocalAccumulator:
error_message = '\n\n'.join(msg)
error_severity = getattr(error, 'zuul_error_severity',
model.SEVERITY_ERROR)
SEVERITY_ERROR)
error_name = getattr(error, 'zuul_error_name', 'Unknown')
config_error = model.ConfigurationError(

View File

@ -24,7 +24,7 @@ from zuul.driver.util import (
make_regex,
ZUUL_REGEX,
)
from zuul.configloader import DeprecationWarning
from zuul.exceptions import DeprecationWarning
class GerritRequireApprovalDeprecation(DeprecationWarning):

View File

@ -19,7 +19,7 @@ from zuul.trigger import BaseTrigger
from zuul.driver.github.githubmodel import GithubEventFilter
from zuul.driver.github import githubsource
from zuul.driver.util import scalar_or_list, to_list, make_regex, ZUUL_REGEX
from zuul.configloader import DeprecationWarning
from zuul.exceptions import DeprecationWarning
class GithubUnlabelDeprecation(DeprecationWarning):

View File

@ -1,4 +1,5 @@
# Copyright 2015 Rackspace Australia
# Copyright 2023 Acme Gating, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -12,6 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import textwrap
# Error severity
SEVERITY_ERROR = 'error'
SEVERITY_WARNING = 'warning'
class ChangeNotFound(Exception):
def __init__(self, number, ps):
@ -120,3 +127,204 @@ class IncorrectZuulAdminClaimError(AuthTokenUnauthorizedException):
class UnauthorizedZuulAdminClaimError(AuthTokenUnauthorizedException):
defaultMsg = 'Issuer is not allowed to set "zuul.admin" claim'
class ConfigurationSyntaxError(Exception):
zuul_error_name = 'Unknown Configuration Error'
zuul_error_severity = SEVERITY_ERROR
class NodeFromGroupNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Node From Group Not Found'
def __init__(self, nodeset, node, group):
message = textwrap.dedent("""\
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))
super(NodeFromGroupNotFoundError, self).__init__(message)
class DuplicateNodeError(ConfigurationSyntaxError):
zuul_error_name = 'Duplicate Node'
def __init__(self, nodeset, node):
message = textwrap.dedent("""\
In nodeset "{nodeset}" the node "{node}" appears multiple times.
Node names must be unique within a nodeset.""")
message = textwrap.fill(message.format(nodeset=nodeset,
node=node))
super(DuplicateNodeError, self).__init__(message)
class UnknownConnection(ConfigurationSyntaxError):
zuul_error_name = 'Unknown Connection'
def __init__(self, connection_name):
message = textwrap.dedent("""\
Unknown connection named "{connection}".""")
message = textwrap.fill(message.format(connection=connection_name))
super(UnknownConnection, self).__init__(message)
class LabelForbiddenError(ConfigurationSyntaxError):
zuul_error_name = 'Label Forbidden'
def __init__(self, label, allowed_labels, disallowed_labels):
message = textwrap.dedent("""\
Label named "{label}" is not part of the allowed
labels ({allowed_labels}) for this tenant.""")
# Make a string that looks like "a, b and not c, d" if we have
# both allowed and disallowed labels.
labels = ", ".join(allowed_labels or [])
if allowed_labels and disallowed_labels:
labels += ' and '
if disallowed_labels:
labels += 'not '
labels += ", ".join(disallowed_labels)
message = textwrap.fill(message.format(
label=label,
allowed_labels=labels))
super(LabelForbiddenError, self).__init__(message)
class MaxTimeoutError(ConfigurationSyntaxError):
zuul_error_name = 'Max Timeout Exceeded'
def __init__(self, job, tenant):
message = textwrap.dedent("""\
The job "{job}" exceeds tenant max-job-timeout {maxtimeout}.""")
message = textwrap.fill(message.format(
job=job.name, maxtimeout=tenant.max_job_timeout))
super(MaxTimeoutError, self).__init__(message)
class DuplicateGroupError(ConfigurationSyntaxError):
zuul_error_name = 'Duplicate Nodeset Group'
def __init__(self, nodeset, group):
message = textwrap.dedent("""\
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))
super(DuplicateGroupError, self).__init__(message)
class ProjectNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Project Not Found'
def __init__(self, project):
message = textwrap.dedent("""\
The project "{project}" was not found. All projects
referenced within a Zuul configuration must first be
added to the main configuration file by the Zuul
administrator.""")
message = textwrap.fill(message.format(project=project))
super(ProjectNotFoundError, self).__init__(message)
class TemplateNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Template Not Found'
def __init__(self, template):
message = textwrap.dedent("""\
The project template "{template}" was not found.
""")
message = textwrap.fill(message.format(template=template))
super(TemplateNotFoundError, self).__init__(message)
class NodesetNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Nodeset Not Found'
def __init__(self, nodeset):
message = textwrap.dedent("""\
The nodeset "{nodeset}" was not found.
""")
message = textwrap.fill(message.format(nodeset=nodeset))
super(NodesetNotFoundError, self).__init__(message)
class PipelineNotPermittedError(ConfigurationSyntaxError):
zuul_error_name = 'Pipeline Forbidden'
def __init__(self):
message = textwrap.dedent("""\
Pipelines may not be defined in untrusted repos,
they may only be defined in config repos.""")
message = textwrap.fill(message)
super(PipelineNotPermittedError, self).__init__(message)
class ProjectNotPermittedError(ConfigurationSyntaxError):
zuul_error_name = 'Project Forbidden'
def __init__(self):
message = textwrap.dedent("""\
Within an untrusted project, the only project definition
permitted is that of the project itself.""")
message = textwrap.fill(message)
super(ProjectNotPermittedError, self).__init__(message)
class GlobalSemaphoreNotFoundError(ConfigurationSyntaxError):
zuul_error_name = 'Global Semaphore Not Found'
def __init__(self, semaphore):
message = textwrap.dedent("""\
The global semaphore "{semaphore}" was not found. All
global semaphores must be added to the main configuration
file by the Zuul administrator.""")
message = textwrap.fill(message.format(semaphore=semaphore))
super(GlobalSemaphoreNotFoundError, self).__init__(message)
class YAMLDuplicateKeyError(ConfigurationSyntaxError):
def __init__(self, key, source_context, start_mark):
self.source_context = source_context
self.start_mark = start_mark
message = (f'The key "{key}" appears more than once; '
'duplicate keys are not permitted.')
super(YAMLDuplicateKeyError, self).__init__(message)
class ConfigurationSyntaxWarning:
zuul_error_name = 'Unknown Configuration Warning'
zuul_error_severity = SEVERITY_WARNING
zuul_error_message = 'Unknown Configuration Warning'
def __init__(self, message=None):
if message:
self.zuul_error_message = message
class MultipleProjectConfigurations(ConfigurationSyntaxWarning):
zuul_error_name = 'Multiple Project Configurations'
zuul_error_problem = 'configuration error'
def __init__(self, source_context):
message = textwrap.dedent(f"""\
Configuration in {source_context.path} ignored because project-branch
is already configured.""")
message = textwrap.fill(message)
super().__init__(message)
class DeprecationWarning(ConfigurationSyntaxWarning):
zuul_error_problem = 'deprecated syntax'
class RegexDeprecation(DeprecationWarning):
zuul_error_name = 'Regex Deprecation'
zuul_error_message = """\
All regular expressions must conform to RE2 syntax, but an
expression using the deprecated Perl-style syntax has been detected.
Adjust the configuration to conform to RE2 syntax."""
def __init__(self, message=None):
if message:
message = (self.zuul_error_message +
f"\n\nThe RE2 syntax error is: {message}")
super().__init__(message)

View File

@ -52,6 +52,11 @@ from zuul.lib import tracing
from zuul.zk import zkobject
from zuul.zk.blob_store import BlobStore
from zuul.zk.change_cache import ChangeKey
from zuul.exceptions import (
SEVERITY_ERROR,
SEVERITY_WARNING,
NodesetNotFoundError,
)
MERGER_MERGE = 1 # "git merge"
MERGER_MERGE_RESOLVE = 2 # "git merge -s resolve"
@ -118,10 +123,6 @@ SCHEME_GOLANG = 'golang'
SCHEME_FLAT = 'flat'
SCHEME_UNIQUE = 'unique'
# Error severity
SEVERITY_ERROR = 'error'
SEVERITY_WARNING = 'warning'
def add_debug_line(debug_messages, msg, indent=0):
if debug_messages is None:
@ -1620,7 +1621,7 @@ class NodeSet(ConfigObject):
# This references an existing named nodeset in the layout.
ns = layout.nodesets.get(nodeset)
if ns is None:
raise Exception(f'The nodeset "{nodeset}" was not found.')
raise NodesetNotFoundError(nodeset)
else:
ns = nodeset
if ns in history:
@ -2902,7 +2903,7 @@ class Job(ConfigObject):
# This references an existing named nodeset in the layout.
ns = layout.nodesets.get(nodeset)
if ns is None:
raise Exception(f'The nodeset "{nodeset}" was not found.')
raise NodesetNotFoundError(nodeset)
else:
ns = nodeset
return ns.flattenAlternatives(layout)
@ -3044,9 +3045,7 @@ class Job(ConfigObject):
# This references an existing named nodeset in the layout.
ns = layout.nodesets.get(self.nodeset)
if ns is None:
raise Exception(
'The nodeset "{nodeset}" was not found.'.format(
nodeset=self.nodeset))
raise NodesetNotFoundError(self.nodeset)
return ns
return self.nodeset

View File

@ -1438,7 +1438,7 @@ class Scheduler(threading.Thread):
loading_errors.append(repr(error))
if loading_errors:
summary = '\n\n\n'.join(loading_errors)
raise configloader.ConfigurationSyntaxError(
raise exceptions.ConfigurationSyntaxError(
f"Configuration errors: {summary}")
duration = round(time.monotonic() - start, 3)