Merge "Prevent jobs from overriding jobs in other repos" into feature/zuulv3

This commit is contained in:
Jenkins 2016-07-19 20:29:28 +00:00 committed by Gerrit Code Review
commit 960d1a2592
3 changed files with 55 additions and 10 deletions

View File

@ -17,6 +17,7 @@ import os
import random
import fixtures
import testtools
from zuul import model
from zuul import configloader
@ -55,19 +56,23 @@ class TestJob(BaseTestCase):
pipeline = model.Pipeline('gate', layout)
layout.addPipeline(pipeline)
queue = model.ChangeQueue(pipeline)
project = model.Project('project')
base = configloader.JobParser.fromYaml(layout, {
'_source_project': project,
'name': 'base',
'timeout': 30,
})
layout.addJob(base)
python27 = configloader.JobParser.fromYaml(layout, {
'_source_project': project,
'name': 'python27',
'parent': 'base',
'timeout': 40,
})
layout.addJob(python27)
python27diablo = configloader.JobParser.fromYaml(layout, {
'_source_project': project,
'name': 'python27',
'branches': [
'stable/diablo'
@ -86,7 +91,6 @@ class TestJob(BaseTestCase):
})
layout.addProjectConfig(project_config, update_pipeline=False)
project = model.Project('project')
change = model.Change(project)
change.branch = 'master'
item = queue.enqueueChange(change)
@ -122,19 +126,23 @@ class TestJob(BaseTestCase):
pipeline = model.Pipeline('gate', layout)
layout.addPipeline(pipeline)
queue = model.ChangeQueue(pipeline)
project = model.Project('project')
base = configloader.JobParser.fromYaml(layout, {
'_source_project': project,
'name': 'base',
'timeout': 30,
})
layout.addJob(base)
python27 = configloader.JobParser.fromYaml(layout, {
'_source_project': project,
'name': 'python27',
'parent': 'base',
'timeout': 40,
})
layout.addJob(python27)
python27diablo = configloader.JobParser.fromYaml(layout, {
'_source_project': project,
'name': 'python27',
'branches': [
'stable/diablo'
@ -153,7 +161,6 @@ class TestJob(BaseTestCase):
})
layout.addProjectConfig(project_config, update_pipeline=False)
project = model.Project('project')
change = model.Change(project)
change.branch = 'master'
item = queue.enqueueChange(change)
@ -183,6 +190,26 @@ class TestJob(BaseTestCase):
self.assertEqual(job.name, 'python27')
self.assertEqual(job.timeout, 70)
def test_job_source_project(self):
layout = model.Layout()
base_project = model.Project('base_project')
base = configloader.JobParser.fromYaml(layout, {
'_source_project': base_project,
'name': 'base',
})
layout.addJob(base)
other_project = model.Project('other_project')
base2 = configloader.JobParser.fromYaml(layout, {
'_source_project': other_project,
'name': 'base',
})
with testtools.ExpectedException(
Exception,
"Job base in other_project is not permitted "
"to shadow job base in base_project"):
layout.addJob(base2)
class TestJobTimeData(BaseTestCase):
def setUp(self):

View File

@ -72,6 +72,7 @@ class JobParser(object):
'irrelevant-files': to_list(str),
'nodes': [node],
'timeout': int,
'_source_project': model.Project,
}
return vs.Schema(job)
@ -96,6 +97,10 @@ class JobParser(object):
# accumulate onto any previously applied tags from
# metajobs.
job.tags = job.tags.union(set(tags))
# This attribute may not be overridden -- it is always
# supplied by the config loader and is the Project instance of
# the repo where it originated.
job.source_project = conf.get('_source_project')
job.failure_message = conf.get('failure-message', job.failure_message)
job.success_message = conf.get('success-message', job.success_message)
job.failure_url = conf.get('failure-url', job.failure_url)
@ -521,29 +526,29 @@ class TenantParser(object):
(job.project, fn))
if job.config_repo:
incdata = TenantParser._parseConfigRepoLayout(
job.files[fn])
job.files[fn], job.project)
config_repos_config.extend(incdata)
else:
incdata = TenantParser._parseProjectRepoLayout(
job.files[fn])
job.files[fn], job.project)
project_repos_config.extend(incdata)
job.project.unparsed_config = incdata
return config_repos_config, project_repos_config
@staticmethod
def _parseConfigRepoLayout(data):
def _parseConfigRepoLayout(data, project):
# This is the top-level configuration for a tenant.
config = model.UnparsedTenantConfig()
config.extend(yaml.load(data))
config.extend(yaml.load(data), project)
return config
@staticmethod
def _parseProjectRepoLayout(data):
def _parseProjectRepoLayout(data, project):
# TODOv3(jeblair): this should implement some rules to protect
# aspects of the config that should not be changed in-repo
config = model.UnparsedTenantConfig()
config.extend(yaml.load(data))
config.extend(yaml.load(data), project)
return config
@ -610,7 +615,7 @@ class ConfigLoader(object):
data = project.unparsed_config
if not data:
continue
incdata = TenantParser._parseProjectRepoLayout(data)
incdata = TenantParser._parseProjectRepoLayout(data, project)
config.extend(incdata)
layout = model.Layout()

View File

@ -525,6 +525,7 @@ class Job(object):
def __init__(self, name):
self.name = name
self.project_source = None
for k, v in self.attributes.items():
setattr(self, k, v)
@ -1545,7 +1546,7 @@ class UnparsedTenantConfig(object):
r.projects = copy.deepcopy(self.projects)
return r
def extend(self, conf):
def extend(self, conf, source_project=None):
if isinstance(conf, UnparsedTenantConfig):
self.pipelines.extend(conf.pipelines)
self.jobs.extend(conf.jobs)
@ -1570,6 +1571,8 @@ class UnparsedTenantConfig(object):
if key == 'project':
self.projects.append(value)
elif key == 'job':
if source_project is not None:
value['_source_project'] = source_project
self.jobs.append(value)
elif key == 'project-template':
self.project_templates.append(value)
@ -1605,6 +1608,16 @@ class Layout(object):
return self.jobs.get(name, [])
def addJob(self, job):
# We can have multiple variants of a job all with the same
# name, but these variants must all be defined in the same repo.
prior_jobs = [j for j in self.getJobs(job.name)
if j.source_project != job.source_project]
if prior_jobs:
raise Exception("Job %s in %s is not permitted to shadow "
"job %s in %s" % (job, job.source_project,
prior_jobs[0],
prior_jobs[0].source_project))
if job.name in self.jobs:
self.jobs[job.name].append(job)
else: