Merge "Add max-nodes-per-job tenant setting" into feature/zuulv3

This commit is contained in:
Jenkins 2017-08-02 01:14:44 +00:00 committed by Gerrit Code Review
commit 58680fbe6d
5 changed files with 63 additions and 0 deletions

View File

@ -28,6 +28,7 @@ configuration. An example tenant definition is::
- tenant:
name: my-tenant
max-nodes-per-job: 5
source:
gerrit:
config-projects:
@ -48,6 +49,10 @@ The following attributes are supported:
characters (ASCII letters, numbers, hyphen and underscore) and you
should avoid changing it unless necessary.
**max-nodes-per-job** (optional)
The maximum number of nodes a job can request, default to 5.
A '-1' value removes the limit.
**source** (required)
A dictionary of sources to consult for projects. A tenant may
contain projects from multiple sources; each of those sources must

View File

@ -10,6 +10,7 @@
- tenant:
name: tenant-two
max-nodes-per-job: 10
source:
gerrit:
config-projects:

38
tests/unit/test_v3.py Normal file → Executable file
View File

@ -948,3 +948,41 @@ class TestDiskAccounting(AnsibleZuulTestCase):
self.waitUntilSettled()
self.assertHistory([
dict(name='dd-big-empty-file', result='ABORTED', changes='1,1')])
class TestMaxNodesPerJob(AnsibleZuulTestCase):
tenant_config_file = 'config/multi-tenant/main.yaml'
def test_max_nodes_reached(self):
in_repo_conf = textwrap.dedent(
"""
- job:
name: test-job
nodes:
- name: node01
label: fake
- name: node02
label: fake
- name: node03
label: fake
- name: node04
label: fake
- name: node05
label: fake
- name: node06
label: fake
""")
file_dict = {'.zuul.yaml': in_repo_conf}
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertIn('The job "test-job" exceeds tenant max-nodes-per-job 5.',
A.messages[0], "A should fail because of nodes limit")
B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertNotIn("exceeds tenant max-nodes", B.messages[0],
"B should not fail because of nodes limit")

View File

@ -67,6 +67,15 @@ class DuplicateNodeError(Exception):
super(DuplicateNodeError, self).__init__(message)
class MaxNodeError(Exception):
def __init__(self, job, tenant):
message = textwrap.dedent("""\
The job "{job}" exceeds tenant max-nodes-per-job {maxnodes}.""")
message = textwrap.fill(message.format(
job=job.name, maxnodes=tenant.max_nodes_per_job))
super(MaxNodeError, self).__init__(message)
class DuplicateGroupError(Exception):
def __init__(self, nodeset, group):
message = textwrap.dedent("""\
@ -475,6 +484,9 @@ class JobParser(object):
for conf_node in conf_nodes:
node = model.Node(conf_node['name'], conf_node['label'])
ns.addNode(node)
if tenant.max_nodes_per_job != -1 and \
len(ns) > tenant.max_nodes_per_job:
raise MaxNodeError(job, tenant)
job.nodeset = ns
if 'required-projects' in conf:
@ -952,6 +964,7 @@ class TenantParser(object):
@staticmethod
def getSchema(connections=None):
tenant = {vs.Required('name'): str,
'max-nodes-per-job': int,
'source': TenantParser.validateTenantSources(connections)}
return vs.Schema(tenant)
@ -960,6 +973,8 @@ class TenantParser(object):
cached):
TenantParser.getSchema(connections)(conf)
tenant = model.Tenant(conf['name'])
if conf.get('max-nodes-per-job') is not None:
tenant.max_nodes_per_job = conf['max-nodes-per-job']
tenant.unparsed_config = conf
unparsed_config = model.UnparsedTenantConfig()
# tpcs is TenantProjectConfigs

View File

@ -501,6 +501,9 @@ class NodeSet(object):
name = ''
return '<NodeSet %s%s%s>' % (name, self.nodes, self.groups)
def __len__(self):
return len(self.nodes)
class NodeRequest(object):
"""A request for a set of nodes."""
@ -2447,6 +2450,7 @@ class SemaphoreHandler(object):
class Tenant(object):
def __init__(self, name):
self.name = name
self.max_nodes_per_job = 5
self.layout = None
# The unparsed configuration from the main zuul config for
# this tenant.