Add support for job allowed-projects
In order to allow users to create jobs which are only available to run on certain projects, add an 'allowed-projects' attribute to jobs. This is especially useful for jobs with secrets. For example, consider a job which performs external API testing with credentials. The user defining that job may well want to restrict its use to a single project. Change-Id: I4457842ea293baf20b83f7b8b86fba2b7d26d2be
This commit is contained in:
@@ -27,11 +27,24 @@ from zuul.lib import encryption
|
||||
from tests.base import BaseTestCase, FIXTURE_DIR
|
||||
|
||||
|
||||
class FakeSource(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class TestJob(BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestJob, self).setUp()
|
||||
self.project = model.Project('project', None)
|
||||
self.tenant = model.Tenant('tenant')
|
||||
self.layout = model.Layout()
|
||||
self.project = model.Project('project', 'connection')
|
||||
self.source = FakeSource('connection')
|
||||
self.tenant.addProjectRepo(self.source, self.project)
|
||||
self.pipeline = model.Pipeline('gate', self.layout)
|
||||
self.layout.addPipeline(self.pipeline)
|
||||
self.queue = model.ChangeQueue(self.pipeline)
|
||||
|
||||
private_key_file = os.path.join(FIXTURE_DIR, 'private.pem')
|
||||
with open(private_key_file, "rb") as f:
|
||||
self.project.private_key, self.project.public_key = \
|
||||
@@ -566,6 +579,43 @@ class TestJob(BaseTestCase):
|
||||
"to shadow job base in base_project"):
|
||||
layout.addJob(base2)
|
||||
|
||||
def test_job_allowed_projects(self):
|
||||
job = configloader.JobParser.fromYaml(self.tenant, self.layout, {
|
||||
'_source_context': self.context,
|
||||
'_start_mark': self.start_mark,
|
||||
'name': 'job',
|
||||
'allowed-projects': ['project'],
|
||||
})
|
||||
self.layout.addJob(job)
|
||||
|
||||
project2 = model.Project('project2', None)
|
||||
context2 = model.SourceContext(project2, 'master',
|
||||
'test', True)
|
||||
|
||||
project2_config = configloader.ProjectParser.fromYaml(
|
||||
self.tenant, self.layout, [{
|
||||
'_source_context': context2,
|
||||
'_start_mark': self.start_mark,
|
||||
'name': 'project2',
|
||||
'gate': {
|
||||
'jobs': [
|
||||
'job'
|
||||
]
|
||||
}
|
||||
}]
|
||||
)
|
||||
self.layout.addProjectConfig(project2_config)
|
||||
|
||||
change = model.Change(project2)
|
||||
# Test master
|
||||
change.branch = 'master'
|
||||
item = self.queue.enqueueChange(change)
|
||||
item.current_build_set.layout = self.layout
|
||||
with testtools.ExpectedException(
|
||||
Exception,
|
||||
"Project project2 is not allowed to run job job"):
|
||||
item.freezeJobGraph()
|
||||
|
||||
|
||||
class TestJobTimeData(BaseTestCase):
|
||||
def setUp(self):
|
||||
|
||||
@@ -240,6 +240,7 @@ class JobParser(object):
|
||||
'repos': to_list(str),
|
||||
'vars': dict,
|
||||
'dependencies': to_list(str),
|
||||
'allowed-projects': to_list(str),
|
||||
}
|
||||
|
||||
return vs.Schema(job)
|
||||
@@ -349,6 +350,19 @@ class JobParser(object):
|
||||
if variables:
|
||||
job.updateVariables(variables)
|
||||
|
||||
allowed_projects = conf.get('allowed-projects', None)
|
||||
if allowed_projects:
|
||||
allowed = []
|
||||
for p in as_list(allowed_projects):
|
||||
# TODOv3(jeblair): this limits allowed_projects to the same
|
||||
# source; we should remove that limitation.
|
||||
source = job.source_context.project.connection_name
|
||||
(trusted, project) = tenant.getRepo(source, p)
|
||||
if project is None:
|
||||
raise Exception("Unknown project %s" % (p,))
|
||||
allowed.append(project.name)
|
||||
job.allowed_projects = frozenset(allowed)
|
||||
|
||||
# If the definition for this job came from a project repo,
|
||||
# implicitly apply a branch matcher for the branch it was on.
|
||||
if (not job.source_context.trusted):
|
||||
|
||||
@@ -764,6 +764,7 @@ class Job(object):
|
||||
final=False,
|
||||
roles=frozenset(),
|
||||
repos=frozenset(),
|
||||
allowed_projects=None,
|
||||
)
|
||||
|
||||
# These are generally internal attributes which are not
|
||||
@@ -2355,6 +2356,10 @@ class Layout(object):
|
||||
# A change must match at least one project pipeline
|
||||
# job variant.
|
||||
continue
|
||||
if (frozen_job.allowed_projects and
|
||||
change.project.name not in frozen_job.allowed_projects):
|
||||
raise Exception("Project %s is not allowed to run job %s" %
|
||||
(change.project.name, frozen_job.name))
|
||||
job_graph.addJob(frozen_job)
|
||||
|
||||
def createJobGraph(self, item):
|
||||
|
||||
Reference in New Issue
Block a user