Merge "Add intermediate flag for jobs"
This commit is contained in:
commit
7c0bd56aa4
|
@ -153,6 +153,21 @@ Here is an example of two job definitions:
|
||||||
limitation does not apply to jobs in a
|
limitation does not apply to jobs in a
|
||||||
:term:`config-project`.
|
:term:`config-project`.
|
||||||
|
|
||||||
|
.. attr:: intermediate
|
||||||
|
:default: false
|
||||||
|
|
||||||
|
An intermediate job must be inherited by an abstract job; it can
|
||||||
|
not be inherited by a final job. All ``intermediate`` jobs
|
||||||
|
*must* also be ``abstract``; a configuration error will be
|
||||||
|
raised if not.
|
||||||
|
|
||||||
|
For example, you may define a base abstract job `foo` and create
|
||||||
|
two abstract jobs that inherit from `foo` called
|
||||||
|
`foo-production` and `foo-development`. If it would be an error
|
||||||
|
to accidentally inherit from the base job `foo` instead of
|
||||||
|
choosing one of the two variants, `foo` could be marked as
|
||||||
|
``intermediate``.
|
||||||
|
|
||||||
.. attr:: success-message
|
.. attr:: success-message
|
||||||
:default: SUCCESS
|
:default: SUCCESS
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- Jobs may specify the new ``intermediate`` flag to note they may only
|
||||||
|
be inherited by abstract jobs. This can be useful if building a job
|
||||||
|
hierarchy where wish to limit where a base job is instantiated.
|
|
@ -0,0 +1,2 @@
|
||||||
|
- hosts: all
|
||||||
|
tasks: []
|
|
@ -0,0 +1,50 @@
|
||||||
|
- pipeline:
|
||||||
|
name: check
|
||||||
|
manager: independent
|
||||||
|
trigger:
|
||||||
|
gerrit:
|
||||||
|
- event: patchset-created
|
||||||
|
success:
|
||||||
|
gerrit:
|
||||||
|
Verified: 1
|
||||||
|
failure:
|
||||||
|
gerrit:
|
||||||
|
Verified: -1
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: base
|
||||||
|
parent: null
|
||||||
|
run: playbooks/base.yaml
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: job-abstract-intermediate
|
||||||
|
abstract: true
|
||||||
|
intermediate: true
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: job-abstract
|
||||||
|
abstract: true
|
||||||
|
parent: job-abstract-intermediate
|
||||||
|
|
||||||
|
# an intermediate, with an intermediate parent also
|
||||||
|
- job:
|
||||||
|
name: job-another-intermediate
|
||||||
|
parent: job-abstract-intermediate
|
||||||
|
abstract: true
|
||||||
|
intermediate: true
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: job-another-abstract
|
||||||
|
parent: job-another-intermediate
|
||||||
|
abstract: true
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: job-actual
|
||||||
|
parent: job-another-abstract
|
||||||
|
run: playbooks/base.yaml
|
||||||
|
|
||||||
|
- project:
|
||||||
|
name: org/project
|
||||||
|
check:
|
||||||
|
jobs: []
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
test
|
|
@ -0,0 +1,8 @@
|
||||||
|
- tenant:
|
||||||
|
name: tenant-one
|
||||||
|
source:
|
||||||
|
gerrit:
|
||||||
|
config-projects:
|
||||||
|
- common-config
|
||||||
|
untrusted-projects:
|
||||||
|
- org/project
|
|
@ -221,6 +221,81 @@ class TestAbstract(ZuulTestCase):
|
||||||
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1')
|
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1')
|
||||||
|
|
||||||
|
|
||||||
|
class TestIntermediate(ZuulTestCase):
|
||||||
|
tenant_config_file = 'config/intermediate/main.yaml'
|
||||||
|
|
||||||
|
def test_intermediate_fail(self):
|
||||||
|
# you can not instantiate from an intermediate job
|
||||||
|
in_repo_conf = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
- job:
|
||||||
|
name: job-instantiate-intermediate
|
||||||
|
parent: job-abstract-intermediate
|
||||||
|
|
||||||
|
- project:
|
||||||
|
check:
|
||||||
|
jobs:
|
||||||
|
- job-instantiate-intermediate
|
||||||
|
""")
|
||||||
|
|
||||||
|
file_dict = {'zuul.yaml': in_repo_conf}
|
||||||
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
|
||||||
|
files=file_dict)
|
||||||
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
||||||
|
self.waitUntilSettled()
|
||||||
|
|
||||||
|
self.assertEqual(A.reported, 1)
|
||||||
|
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
|
||||||
|
self.assertIn('may only inherit to another abstract job',
|
||||||
|
A.messages[0])
|
||||||
|
|
||||||
|
def test_intermediate_config_fail(self):
|
||||||
|
# an intermediate job must also be abstract
|
||||||
|
in_repo_conf = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
- job:
|
||||||
|
name: job-intermediate-but-not-abstract
|
||||||
|
intermediate: true
|
||||||
|
abstract: false
|
||||||
|
|
||||||
|
- project:
|
||||||
|
check:
|
||||||
|
jobs:
|
||||||
|
- job-intermediate-but-not-abstract
|
||||||
|
""")
|
||||||
|
|
||||||
|
file_dict = {'zuul.yaml': in_repo_conf}
|
||||||
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
|
||||||
|
files=file_dict)
|
||||||
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
||||||
|
self.waitUntilSettled()
|
||||||
|
|
||||||
|
self.assertEqual(A.reported, 1)
|
||||||
|
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
|
||||||
|
self.assertIn('An intermediate job must also be abstract',
|
||||||
|
A.messages[0])
|
||||||
|
|
||||||
|
def test_intermediate_several(self):
|
||||||
|
# test passing through several intermediate jobs
|
||||||
|
in_repo_conf = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
- project:
|
||||||
|
name: org/project
|
||||||
|
check:
|
||||||
|
jobs:
|
||||||
|
- job-actual
|
||||||
|
""")
|
||||||
|
|
||||||
|
file_dict = {'.zuul.yaml': in_repo_conf}
|
||||||
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
|
||||||
|
files=file_dict)
|
||||||
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
||||||
|
self.waitUntilSettled()
|
||||||
|
|
||||||
|
self.assertEqual(A.reported, 1)
|
||||||
|
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1')
|
||||||
|
|
||||||
|
|
||||||
class TestFinal(ZuulTestCase):
|
class TestFinal(ZuulTestCase):
|
||||||
|
|
||||||
tenant_config_file = 'config/final/main.yaml'
|
tenant_config_file = 'config/final/main.yaml'
|
||||||
|
|
|
@ -341,6 +341,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'dependencies': [],
|
'dependencies': [],
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'final': False,
|
'final': False,
|
||||||
|
@ -385,6 +386,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'dependencies': [],
|
'dependencies': [],
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'final': False,
|
'final': False,
|
||||||
|
@ -434,6 +436,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
'final': False,
|
'final': False,
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'name': 'test-job',
|
'name': 'test-job',
|
||||||
|
@ -555,6 +558,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
'final': False,
|
'final': False,
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'name': 'project-merge',
|
'name': 'project-merge',
|
||||||
|
@ -592,6 +596,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
'final': False,
|
'final': False,
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'name': 'project-test1',
|
'name': 'project-test1',
|
||||||
|
@ -629,6 +634,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
'final': False,
|
'final': False,
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'name': 'project-test2',
|
'name': 'project-test2',
|
||||||
|
@ -666,6 +672,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
'final': False,
|
'final': False,
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'name': 'project1-project2-integration',
|
'name': 'project1-project2-integration',
|
||||||
|
@ -723,6 +730,7 @@ class TestWeb(BaseTestWeb):
|
||||||
'description': None,
|
'description': None,
|
||||||
'files': [],
|
'files': [],
|
||||||
'final': False,
|
'final': False,
|
||||||
|
'intermediate': False,
|
||||||
'irrelevant_files': [],
|
'irrelevant_files': [],
|
||||||
'match_on_config_updates': True,
|
'match_on_config_updates': True,
|
||||||
'name': 'project-post',
|
'name': 'project-post',
|
||||||
|
|
|
@ -603,6 +603,7 @@ class JobParser(object):
|
||||||
'final': bool,
|
'final': bool,
|
||||||
'abstract': bool,
|
'abstract': bool,
|
||||||
'protected': bool,
|
'protected': bool,
|
||||||
|
'intermediate': bool,
|
||||||
'requires': to_list(str),
|
'requires': to_list(str),
|
||||||
'provides': to_list(str),
|
'provides': to_list(str),
|
||||||
'failure-message': str,
|
'failure-message': str,
|
||||||
|
@ -655,6 +656,7 @@ class JobParser(object):
|
||||||
'final',
|
'final',
|
||||||
'abstract',
|
'abstract',
|
||||||
'protected',
|
'protected',
|
||||||
|
'intermediate',
|
||||||
'timeout',
|
'timeout',
|
||||||
'post-timeout',
|
'post-timeout',
|
||||||
'workspace',
|
'workspace',
|
||||||
|
@ -810,6 +812,9 @@ class JobParser(object):
|
||||||
job.roles, secrets)
|
job.roles, secrets)
|
||||||
job.run = job.run + (run,)
|
job.run = job.run + (run,)
|
||||||
|
|
||||||
|
if conf.get('intermediate', False) and not conf.get('abstract', False):
|
||||||
|
raise Exception("An intermediate job must also be abstract")
|
||||||
|
|
||||||
for k in self.simple_attributes:
|
for k in self.simple_attributes:
|
||||||
a = k.replace('-', '_')
|
a = k.replace('-', '_')
|
||||||
if k in conf:
|
if k in conf:
|
||||||
|
|
|
@ -1205,6 +1205,7 @@ class Job(ConfigObject):
|
||||||
attempts=3,
|
attempts=3,
|
||||||
final=False,
|
final=False,
|
||||||
abstract=False,
|
abstract=False,
|
||||||
|
intermediate=False,
|
||||||
protected=None,
|
protected=None,
|
||||||
roles=(),
|
roles=(),
|
||||||
required_projects={},
|
required_projects={},
|
||||||
|
@ -1268,6 +1269,7 @@ class Job(ConfigObject):
|
||||||
d['group_variables'] = self.group_variables
|
d['group_variables'] = self.group_variables
|
||||||
d['final'] = self.final
|
d['final'] = self.final
|
||||||
d['abstract'] = self.abstract
|
d['abstract'] = self.abstract
|
||||||
|
d['intermediate'] = self.intermediate
|
||||||
d['protected'] = self.protected
|
d['protected'] = self.protected
|
||||||
d['voting'] = self.voting
|
d['voting'] = self.voting
|
||||||
d['timeout'] = self.timeout
|
d['timeout'] = self.timeout
|
||||||
|
@ -1559,7 +1561,8 @@ class Job(ConfigObject):
|
||||||
|
|
||||||
for k in self.execution_attributes:
|
for k in self.execution_attributes:
|
||||||
if (other._get(k) is not None and
|
if (other._get(k) is not None and
|
||||||
k not in set(['final', 'abstract', 'protected'])):
|
k not in set(['final', 'abstract', 'protected',
|
||||||
|
'intermediate'])):
|
||||||
if self.final:
|
if self.final:
|
||||||
raise Exception("Unable to modify final job %s attribute "
|
raise Exception("Unable to modify final job %s attribute "
|
||||||
"%s=%s with variant %s" % (
|
"%s=%s with variant %s" % (
|
||||||
|
@ -1592,6 +1595,19 @@ class Job(ConfigObject):
|
||||||
elif other.abstract:
|
elif other.abstract:
|
||||||
self.abstract = True
|
self.abstract = True
|
||||||
|
|
||||||
|
# An intermediate job may only be inherited by an abstract
|
||||||
|
# job. Note intermediate jobs must be also be abstract, that
|
||||||
|
# has been enforced during config reading. Similar to
|
||||||
|
# abstract, it is cleared by inheriting.
|
||||||
|
if self.intermediate and not other.abstract:
|
||||||
|
raise Exception("Intermediate job %s may only inherit "
|
||||||
|
"to another abstract job" %
|
||||||
|
(repr(self)))
|
||||||
|
if other.name != self.name:
|
||||||
|
self.intermediate = other.intermediate
|
||||||
|
elif other.intermediate:
|
||||||
|
self.intermediate = True
|
||||||
|
|
||||||
# Protected may only be set to true
|
# Protected may only be set to true
|
||||||
if other.protected is not None:
|
if other.protected is not None:
|
||||||
# don't allow to reset protected flag
|
# don't allow to reset protected flag
|
||||||
|
|
Loading…
Reference in New Issue