Merge "Add abstract job attribute"

This commit is contained in:
Zuul 2018-02-13 22:12:36 +00:00 committed by Gerrit Code Review
commit ad82bbf09f
8 changed files with 129 additions and 30 deletions

View File

@ -546,6 +546,12 @@ Here is an example of two job definitions:
from this job. Once this is set to ``true`` it cannot be reset to
``false``.
.. attr:: abstract
:default: false
To indicate a job is not intended to be run directly, but
instead must be inherited from, set this attribute to ``true``.
.. attr:: success-message
:default: SUCCESS

View File

@ -0,0 +1,2 @@
- hosts: all
tasks: []

View File

@ -0,0 +1,25 @@
- 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
abstract: true
- job:
name: job-child
parent: job-abstract

View File

@ -0,0 +1,4 @@
- project:
name: org/project
check:
jobs: []

View File

@ -0,0 +1,8 @@
- tenant:
name: tenant-one
source:
gerrit:
config-projects:
- common-config
untrusted-projects:
- org/project

View File

@ -74,44 +74,43 @@ class TestMultipleTenants(AnsibleZuulTestCase):
class TestProtected(ZuulTestCase):
tenant_config_file = 'config/protected/main.yaml'
def test_protected_ok(self):
# test clean usage of final parent job
in_repo_conf = textwrap.dedent(
"""
- job:
name: job-protected
protected: true
run: playbooks/job-protected.yaml
# test clean usage of final parent job
in_repo_conf = textwrap.dedent(
"""
- job:
name: job-protected
protected: true
run: playbooks/job-protected.yaml
- project:
name: org/project
check:
jobs:
- job-child-ok
- project:
name: org/project
check:
jobs:
- job-child-ok
- job:
name: job-child-ok
parent: job-protected
- job:
name: job-child-ok
parent: job-protected
- project:
name: org/project
check:
jobs:
- job-child-ok
- project:
name: org/project
check:
jobs:
- job-child-ok
""")
""")
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()
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.assertEqual(A.reported, 1)
self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1')
def test_protected_reset(self):
# try to reset protected flag
@ -177,6 +176,47 @@ class TestProtected(ZuulTestCase):
"and cannot be inherited from other projects.", A.messages[0])
class TestAbstract(ZuulTestCase):
tenant_config_file = 'config/abstract/main.yaml'
def test_abstract_fail(self):
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- job-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('may not be directly run', A.messages[0])
def test_child_of_abstract(self):
in_repo_conf = textwrap.dedent(
"""
- project:
check:
jobs:
- job-child
""")
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):
tenant_config_file = 'config/final/main.yaml'

View File

@ -474,6 +474,7 @@ class JobParser(object):
# Attributes of a job that can also be used in Project and ProjectTemplate
job_attributes = {'parent': vs.Any(str, None),
'final': bool,
'abstract': bool,
'protected': bool,
'failure-message': str,
'success-message': str,
@ -514,6 +515,7 @@ class JobParser(object):
simple_attributes = [
'final',
'abstract',
'protected',
'timeout',
'workspace',

View File

@ -848,6 +848,7 @@ class Job(object):
semaphore=None,
attempts=3,
final=False,
abstract=False,
protected=None,
roles=(),
required_projects={},
@ -1044,7 +1045,7 @@ class Job(object):
for k in self.execution_attributes:
if (other._get(k) is not None and
k not in set(['final', 'protected'])):
k not in set(['final', 'abstract', 'protected'])):
if self.final:
raise Exception("Unable to modify final job %s attribute "
"%s=%s with variant %s" % (
@ -1070,6 +1071,13 @@ class Job(object):
if other.final != self.attributes['final']:
self.final = other.final
# Abstract may not be reset by a variant, it may only be
# cleared by inheriting.
if other.name != self.name:
self.abstract = other.abstract
elif other.abstract:
self.abstract = True
# Protected may only be set to true
if other.protected is not None:
# don't allow to reset protected flag
@ -2836,6 +2844,10 @@ class Layout(object):
item.debug("No matching pipeline variants for {jobname}".
format(jobname=jobname), indent=2)
continue
if frozen_job.abstract:
raise Exception("Job %s is abstract and may not be "
"directly run" %
(frozen_job.name,))
if (frozen_job.allowed_projects is not None and
change.project.name not in frozen_job.allowed_projects):
raise Exception("Project %s is not allowed to run job %s" %