Allow duplicate queue definitions on same project branches

Just like secrets and semaphores, queues can be defined in-repo in
unstrusted projects with multiple branches.  To aid the branching
workflow in these cases, we will now ignore duplicate queue definitions
on multiple branches of the same project if they are identical.

Change-Id: Ib74e71f425f8e2835ac0000fd76fde478b9d1653
This commit is contained in:
James E. Blair 2023-03-09 15:29:15 -08:00
parent d6d592f5a7
commit 6caba8e057
3 changed files with 64 additions and 4 deletions

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Duplicate :attr:`queue` configuration items are now permitted on
multiple branches of the same project as long as their
configuration is identical. Similar to secrets and semaphores,
this can help avoid spurious configuration errors when branching a
project with in-repo configuration.

View File

@ -6732,6 +6732,44 @@ class TestChangeQueues(ZuulTestCase):
"""
self._test_dependent_queues_per_branch('org/project4')
def test_duplicate_definition_on_branches(self):
project = 'org/project3'
self.create_branch(project, 'stable')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(project, 'stable'))
self.waitUntilSettled()
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
self.assertEquals(
len(tenant.layout.loading_errors), 1,
"No error should have been accumulated")
# This error is expected and unrelated to this test (the
# ignored configuration is used by other tests in this class):
self.assertIn('Queue integrated already defined',
tenant.layout.loading_errors[0].error)
# At this point we've verified that we can have identical
# queue definitions on multiple branches without conflict.
# Next, let's try to change the queue def on one branch so it
# doesn't match (flip the per-branch boolean):
conf = textwrap.dedent(
"""
- queue:
name: integrated-untrusted
per-branch: false
""")
file_dict = {'zuul.d/queue.yaml': conf}
A = self.fake_gerrit.addFakeChange(project, 'stable', 'A',
files=file_dict)
A.addApproval('Code-Review', 2)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(len(A.messages), 1)
self.assertTrue(
'Queue integrated-untrusted does not match '
'existing definition in branch master' in A.messages[0])
self.assertEqual(A.data['status'], 'NEW')
class TestJobUpdateBrokenConfig(ZuulTestCase):
tenant_config_file = 'config/job-update-broken/main.yaml'

View File

@ -7898,10 +7898,24 @@ class Layout(object):
return semaphore
def addQueue(self, queue):
# Change queues must be unique and cannot be overridden.
if queue.name in self.queues:
raise Exception('Queue %s is already defined' % queue.name)
# It's ok to have a duplicate queue definition, but only if
# they are in different branches of the same repo, and have
# the same values.
other = self.queues.get(queue.name)
if other is not None:
if not queue.source_context.isSameProject(other.source_context):
raise Exception(
"Queue %s already defined in project %s" %
(queue.name, other.source_context.project_name))
if queue.source_context.branch == other.source_context.branch:
raise Exception("Queue %s already defined" % (queue.name,))
if queue != other:
raise Exception("Queue %s does not match existing definition"
" in branch %s" %
(queue.name, other.source_context.branch))
# Identical data in a different branch of the same project;
# ignore the duplicate definition
return
self.queues[queue.name] = queue
def addPipeline(self, pipeline):