Merge "Add implied-branch-matchers to tenant config"

This commit is contained in:
Zuul 2023-06-28 07:09:31 +00:00 committed by Gerrit Code Review
commit 640d65a647
12 changed files with 166 additions and 8 deletions

View File

@ -1036,7 +1036,9 @@ Here is an example of two job definitions:
only affect it.
See :attr:`pragma.implied-branch-matchers` for how to override
this behavior on a per-file basis.
this behavior on a per-file basis. The behavior may also be
configured by a Zuul administrator using
:attr:`tenant.untrusted-projects.<project>.implied-branch-matchers`.
.. attr:: files

View File

@ -25,7 +25,7 @@ pragma directives may not be set and then unset within the same file.
.. attr:: implied-branch-matchers
This is a boolean, which, if set, may be used to enable
(``True``) or disable (``False``) the addition of implied branch
(``true``) or disable (``false``) the addition of implied branch
matchers to job and project-template definitions. Normally Zuul
decides whether to add these based on heuristics described in
:attr:`job.branches`. This attribute overrides that behavior.
@ -33,6 +33,11 @@ pragma directives may not be set and then unset within the same file.
This can be useful if a project has multiple branches, yet the
jobs defined in the master branch should apply to all branches.
The behavior may also be configured by a Zuul administrator
using
:attr:`tenant.untrusted-projects.<project>.implied-branch-matchers`.
This pragma overrides that setting if both are present.
Note that if a job contains an explicit branch matcher, it will
be used regardless of the value supplied here.

View File

@ -270,6 +270,27 @@ configuration. Some examples of tenant definitions are:
circumstances and only calculate the configuration of a
single additional branch when it is used.
.. attr:: implied-branch-matchers
This is a boolean, which, if set, may be used to enable
(``true``) or disable (``false``) the addition of implied
branch matchers to job and project-template definitions.
Normally Zuul decides whether to add these based on
heuristics described in :attr:`job.branches`. This
attribute overrides that behavior.
This can be useful if branch settings for this project may
produce an unpredictable number of branches to load from.
Setting this value explicitly here can avoid unexpected
behavior changes as branches are added or removed from the
load set.
The :attr:`pragma.implied-branch-matchers` pragma will
override the setting here if present.
Note that if a job contains an explicit branch matcher, it
will be used regardless of the value supplied here.
.. attr:: extra-config-paths
Normally Zuul loads in-repo configuration from the first

View File

@ -0,0 +1,10 @@
---
features:
- |
A new tenant project configuration option,
`implied-branch-matchers` has been added. This is useful in
conjunction with the branch exclusion options which may cause Zuul
to change behavior as the set of load branches switches between
one and more than one. The operator can fix the appropriate
behavior in the tenant config file rather than relying on the
heuristic in these cases.

View File

@ -0,0 +1,53 @@
- pipeline:
name: check
manager: independent
trigger:
gerrit:
- event: patchset-created
success:
gerrit:
Verified: 1
failure:
gerrit:
Verified: -1
- pipeline:
name: gate
manager: dependent
post-review: True
trigger:
gerrit:
- event: comment-added
approval:
- Approved: 1
success:
gerrit:
Verified: 2
submit: true
failure:
gerrit:
Verified: -2
start:
gerrit:
Verified: 0
precedence: high
- job:
name: base
parent: null
- project:
name: common-config
check:
jobs: []
gate:
jobs:
- noop
- project:
name: org/project
check:
jobs: []
gate:
jobs:
- noop

View File

@ -0,0 +1 @@
test

View File

@ -0,0 +1,2 @@
- hosts: all
tasks: []

View File

@ -0,0 +1,2 @@
- job:
name: test-job

View File

@ -0,0 +1,9 @@
- tenant:
name: tenant-one
source:
gerrit:
config-projects:
- common-config
untrusted-projects:
- org/project:
implied-branch-matchers: true

View File

@ -6105,6 +6105,48 @@ class TestPragmaMultibranch(ZuulTestCase):
], ordered=False)
class TestTenantImpliedBranchMatchers(ZuulTestCase):
tenant_config_file = 'config/tenant-implied-branch-matchers/main.yaml'
def test_tenant_implied_branch_matchers(self):
# Test that we can force implied branch matchers in the tenant
# config even in the case where a project only has one branch.
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
jobs = tenant.layout.getJobs('test-job')
self.assertEqual(len(jobs), 1)
for job in tenant.layout.getJobs('test-job'):
self.assertIsNotNone(job.branch_matcher)
def test_pragma_overrides_tenant_implied_branch_matchers(self):
# Test that we can force implied branch matchers off with a pragma
# even if the tenant config has it set on.
config = textwrap.dedent(
"""
- job:
name: test-job
- pragma:
implied-branch-matchers: False
""")
file_dict = {'zuul.yaml': config}
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
files=file_dict)
A.setMerged()
self.fake_gerrit.addEvent(A.getChangeMergedEvent())
self.waitUntilSettled()
self.create_branch('org/project', 'stable/pike')
self.fake_gerrit.addEvent(
self.fake_gerrit.getFakeBranchCreatedEvent(
'org/project', 'stable/pike'))
self.waitUntilSettled()
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
jobs = tenant.layout.getJobs('test-job')
self.assertEqual(len(jobs), 2)
for job in tenant.layout.getJobs('test-job'):
self.assertIsNone(job.branch_matcher)
class TestBaseJobs(ZuulTestCase):
tenant_config_file = 'config/base-jobs/main.yaml'

View File

@ -1690,7 +1690,7 @@ class ParseContext(object):
def getImpliedBranches(self, source_context):
# If the user has set a pragma directive for this, use the
# value (if unset, the value is None).
# value (ixf unset, the value is None).
if source_context.implied_branch_matchers is True:
if source_context.implied_branches is not None:
return source_context.implied_branches
@ -1742,6 +1742,7 @@ class TenantParser(object):
'exclude-branches': to_list(str),
'always-dynamic-branches': to_list(str),
'allow-circular-dependencies': bool,
'implied-branch-matchers': bool,
}}
project = vs.Any(str, project_dict)
@ -1985,6 +1986,7 @@ class TenantParser(object):
project_exclude_branches = None
project_always_dynamic_branches = None
project_load_branch = None
project_implied_branch_matchers = None
else:
project_name = list(conf.keys())[0]
project = source.getProject(project_name)
@ -2039,6 +2041,8 @@ class TenantParser(object):
if x.endswith('/')])
project_load_branch = conf[project_name].get(
'load-branch', None)
project_implied_branch_matchers = conf[project_name].get(
'implied-branch-matchers', None)
tenant_project_config = model.TenantProjectConfig(project)
tenant_project_config.load_classes = frozenset(project_include)
@ -2052,6 +2056,8 @@ class TenantParser(object):
tenant_project_config.extra_config_files = extra_config_files
tenant_project_config.extra_config_dirs = extra_config_dirs
tenant_project_config.load_branch = project_load_branch
tenant_project_config.implied_branch_matchers = \
project_implied_branch_matchers
return tenant_project_config
@ -2231,7 +2237,8 @@ class TenantParser(object):
# accumulates a list of all merger jobs submitted.
source_context = model.SourceContext(
project.canonical_name, project.name,
project.connection_name, branch, '', False)
project.connection_name, branch, '', False,
tpc.implied_branch_matchers)
if min_ltimes is not None:
files_cache = self.unparsed_config_cache.getFilesCache(
project.canonical_name, branch)
@ -2966,7 +2973,8 @@ class ConfigLoader(object):
if data:
source_context = model.SourceContext(
project.canonical_name, project.name,
project.connection_name, branch, fn, trusted)
project.connection_name, branch, fn, trusted,
tpc.implied_branch_matchers)
# Prevent mixing configuration source
conf_root = fn.split('/')[0]

View File

@ -1914,7 +1914,8 @@ class SourceContext(ConfigObject):
originate."""
def __init__(self, project_canonical_name, project_name,
project_connection_name, branch, path, trusted):
project_connection_name, branch, path, trusted,
implied_branch_matchers=None):
super(SourceContext, self).__init__()
self.project_canonical_name = project_canonical_name
self.project_name = project_name
@ -1922,7 +1923,7 @@ class SourceContext(ConfigObject):
self.branch = branch
self.path = path
self.trusted = trusted
self.implied_branch_matchers = None
self.implied_branch_matchers = implied_branch_matchers
self.implied_branches = None
def __str__(self):
@ -1939,7 +1940,8 @@ class SourceContext(ConfigObject):
def copy(self):
return self.__class__(
self.project_canonical_name, self.project_name,
self.project_connection_name, self.branch, self.path, self.trusted)
self.project_connection_name, self.branch, self.path, self.trusted,
self.implied_branch_matchers)
def isSameProject(self, other):
if not isinstance(other, SourceContext):
@ -7263,6 +7265,7 @@ class TenantProjectConfig(object):
# Load config from a different branch if this is a config project
self.load_branch = None
self.merge_modes = None
self.implied_branch_matchers = None
def isAlwaysDynamicBranch(self, branch):
if self.always_dynamic_branches is None: