Merge "Add max-nodes-per-job tenant setting" into feature/zuulv3
This commit is contained in:
commit
58680fbe6d
|
@ -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
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
- tenant:
|
||||
name: tenant-two
|
||||
max-nodes-per-job: 10
|
||||
source:
|
||||
gerrit:
|
||||
config-projects:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue