Add allowed-triggers and allowed-reporters tenant settings

This changes adds new tenant settings to limit the connection a tenant can
use to trigger from or report to.

Change-Id: I1793ec9c8a249b3a1ce90868086421c8d349d7aa
This commit is contained in:
Tristan Cacqueray 2018-03-19 01:29:18 +00:00
parent 71f60674b9
commit 489812e041
7 changed files with 116 additions and 1 deletions

View File

@ -233,3 +233,17 @@ configuration. Some examples of tenant definitions are:
This allows an administrator to configure a default base job to
implement local policies such as node setup and artifact
publishing.
.. attr:: allowed-triggers
:default: all connections
The list of connections a tenant can trigger from. When set, this setting
can be used to restrict what connections a tenant can use as trigger.
Without this setting, the tenant can use any connection as a trigger.
.. attr:: allowed-reporters
:default: all connections
The list of connections a tenant can report to. When set, this setting
can be used to restrict what connections a tenant can use as reporter.
Without this setting, the tenant can report to any connection.

View File

@ -0,0 +1,6 @@
---
features:
- |
New tenant options :attr:`tenant.allowed-triggers` and
:attr:`tenant.allowed-reporters` can be used to restrict
what connections a tenant has access to.

View File

@ -39,3 +39,9 @@
jobs:
- python27
- project1-test1
- project:
name: tenant-one-config
check:
jobs:
- noop

View File

@ -39,3 +39,9 @@
jobs:
- python27
- project2-test1
- project:
name: tenant-two-config
check:
jobs:
- noop

View File

@ -1,6 +1,8 @@
- tenant:
name: tenant-one
max-job-timeout: 1800
allowed-reporters:
- gerrit
source:
gerrit:
config-projects:
@ -12,6 +14,7 @@
- tenant:
name: tenant-two
max-nodes-per-job: 10
allowed-triggers: gerrit
source:
gerrit:
config-projects:

View File

@ -3176,6 +3176,65 @@ class TestMaxTimeout(ZuulTestCase):
"B should not fail because of timeout limit")
class TestAllowedConnection(AnsibleZuulTestCase):
config_file = 'zuul-connections-gerrit-and-github.conf'
tenant_config_file = 'config/multi-tenant/main.yaml'
def test_allowed_triggers(self):
in_repo_conf = textwrap.dedent(
"""
- pipeline:
name: test
manager: independent
trigger:
github:
- event: pull_request
""")
file_dict = {'zuul.d/test.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange(
'tenant-two-config', 'master', 'A', files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertIn(
'Unknown connection named "github"', A.messages[0],
"A should fail because of allowed-trigger")
B = self.fake_gerrit.addFakeChange(
'tenant-one-config', 'master', 'A', files=file_dict)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertNotIn(
'Unknown connection named "github"', B.messages[0],
"B should not fail because of allowed-trigger")
def test_allowed_reporters(self):
in_repo_conf = textwrap.dedent(
"""
- pipeline:
name: test
manager: independent
success:
outgoing_smtp:
to: you@example.com
""")
file_dict = {'zuul.d/test.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange(
'tenant-one-config', 'master', 'A', files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertIn(
'Unknown connection named "outgoing_smtp"', A.messages[0],
"A should fail because of allowed-reporters")
B = self.fake_gerrit.addFakeChange(
'tenant-two-config', 'master', 'A', files=file_dict)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertNotIn(
'Unknown connection named "outgoing_smtp"', B.messages[0],
"B should not fail because of allowed-reporters")
class TestPragma(ZuulTestCase):
tenant_config_file = 'config/pragma/main.yaml'

View File

@ -71,6 +71,14 @@ class DuplicateNodeError(Exception):
super(DuplicateNodeError, self).__init__(message)
class UnknownConnection(Exception):
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 MaxTimeoutError(Exception):
def __init__(self, job, tenant):
message = textwrap.dedent("""\
@ -1085,9 +1093,13 @@ class PipelineParser(object):
for conf_key, action in self.reporter_actions.items():
reporter_set = []
allowed_reporters = self.pcontext.tenant.allowed_reporters
if conf.get(conf_key):
for reporter_name, params \
in conf.get(conf_key).items():
if allowed_reporters is not None and \
reporter_name not in allowed_reporters:
raise UnknownConnection(reporter_name)
reporter = self.pcontext.connections.getReporter(
reporter_name, pipeline, params)
reporter.setAction(conf_key)
@ -1136,6 +1148,9 @@ class PipelineParser(object):
source.getRejectFilters(reject_config))
for trigger_name, trigger_config in conf.get('trigger').items():
if self.pcontext.tenant.allowed_triggers is not None and \
trigger_name not in self.pcontext.tenant.allowed_triggers:
raise UnknownConnection(trigger_name)
trigger = self.pcontext.connections.getTrigger(
trigger_name, trigger_config)
pipeline.triggers.append(trigger)
@ -1265,6 +1280,8 @@ class TenantParser(object):
'max-job-timeout': int,
'source': self.validateTenantSources(),
'exclude-unprotected-branches': bool,
'allowed-triggers': to_list(str),
'allowed-reporters': to_list(str),
'default-parent': str,
}
return vs.Schema(tenant)
@ -1279,6 +1296,8 @@ class TenantParser(object):
if conf.get('exclude-unprotected-branches') is not None:
tenant.exclude_unprotected_branches = \
conf['exclude-unprotected-branches']
tenant.allowed_triggers = conf.get('allowed-triggers')
tenant.allowed_reporters = conf.get('allowed-reporters')
tenant.default_base_job = conf.get('default-parent', 'base')
tenant.unparsed_config = conf
@ -1736,7 +1755,9 @@ class TenantParser(object):
# reference_exceptions has it; add tests if needed.
if not skip_pipelines:
for pipeline in parsed_config.pipelines:
layout.addPipeline(pipeline)
with reference_exceptions(
'pipeline', pipeline, layout.loading_errors):
layout.addPipeline(pipeline)
for nodeset in parsed_config.nodesets:
with reference_exceptions(