diff options
author | Tobias Henkel <tobias.henkel@bmw.de> | 2017-11-26 20:27:59 +0100 |
---|---|---|
committer | Tobias Henkel <tobias.henkel@bmw.de> | 2017-12-15 19:31:02 +0100 |
commit | 130b00064f035d2f99dc7eec57f7781bfc3bd555 (patch) | |
tree | ff8a9a3f4d3ebffd11fe88ad2cac498a51764519 | |
parent | efd8e827791c513a181d4b5dde149a65f8e60131 (diff) | |
download | zuul-130b00064f035d2f99dc7eec57f7781bfc3bd555.tar.gz |
Add support for protected jobs
For some use cases protected jobs can be useful. Protected jobs can
only be inherited by jobs defined in the same project. This adds
support for these protected jobs.
Change-Id: I62a8ecbbfa9eec54ab599bb34148976dcabfd40a
-rw-r--r-- | doc/source/user/config.rst | 7 | ||||
-rw-r--r-- | tests/fixtures/config/protected/git/common-config/zuul.yaml | 16 | ||||
-rw-r--r-- | tests/fixtures/config/protected/git/org_project/playbooks/job-protected.yaml | 2 | ||||
-rw-r--r-- | tests/fixtures/config/protected/git/org_project/zuul.yaml | 9 | ||||
-rw-r--r-- | tests/fixtures/config/protected/git/org_project1/README | 1 | ||||
-rw-r--r-- | tests/fixtures/config/protected/git/org_project1/playbooks/job-child-notok.yaml | 2 | ||||
-rw-r--r-- | tests/fixtures/config/protected/git/org_project1/playbooks/placeholder | 0 | ||||
-rw-r--r-- | tests/fixtures/config/protected/main.yaml | 9 | ||||
-rwxr-xr-x | tests/unit/test_v3.py | 104 | ||||
-rw-r--r-- | zuul/configloader.py | 2 | ||||
-rw-r--r-- | zuul/model.py | 24 |
11 files changed, 175 insertions, 1 deletions
diff --git a/doc/source/user/config.rst b/doc/source/user/config.rst index 96e55a8f0..d1711078f 100644 --- a/doc/source/user/config.rst +++ b/doc/source/user/config.rst @@ -539,6 +539,13 @@ Here is an example of two job definitions: specified in a project's pipeline, set this attribute to ``true``. + .. attr:: protected + :default: false + + When set to ``true`` only jobs defined in the same project may inherit + from this job. Once this is set to ``true`` it cannot be reset to + ``false``. + .. attr:: success-message :default: SUCCESS diff --git a/tests/fixtures/config/protected/git/common-config/zuul.yaml b/tests/fixtures/config/protected/git/common-config/zuul.yaml new file mode 100644 index 000000000..c941573e6 --- /dev/null +++ b/tests/fixtures/config/protected/git/common-config/zuul.yaml @@ -0,0 +1,16 @@ +- pipeline: + name: check + manager: independent + trigger: + gerrit: + - event: patchset-created + success: + gerrit: + Verified: 1 + failure: + gerrit: + Verified: -1 + +- job: + name: base + parent: null diff --git a/tests/fixtures/config/protected/git/org_project/playbooks/job-protected.yaml b/tests/fixtures/config/protected/git/org_project/playbooks/job-protected.yaml new file mode 100644 index 000000000..f679dceae --- /dev/null +++ b/tests/fixtures/config/protected/git/org_project/playbooks/job-protected.yaml @@ -0,0 +1,2 @@ +- hosts: all + tasks: [] diff --git a/tests/fixtures/config/protected/git/org_project/zuul.yaml b/tests/fixtures/config/protected/git/org_project/zuul.yaml new file mode 100644 index 000000000..95f33df6f --- /dev/null +++ b/tests/fixtures/config/protected/git/org_project/zuul.yaml @@ -0,0 +1,9 @@ +- job: + name: job-protected + protected: true + run: playbooks/job-protected.yaml + +- project: + name: org/project + check: + jobs: [] diff --git a/tests/fixtures/config/protected/git/org_project1/README b/tests/fixtures/config/protected/git/org_project1/README new file mode 100644 index 000000000..9daeafb98 --- /dev/null +++ b/tests/fixtures/config/protected/git/org_project1/README @@ -0,0 +1 @@ +test diff --git a/tests/fixtures/config/protected/git/org_project1/playbooks/job-child-notok.yaml b/tests/fixtures/config/protected/git/org_project1/playbooks/job-child-notok.yaml new file mode 100644 index 000000000..f679dceae --- /dev/null +++ b/tests/fixtures/config/protected/git/org_project1/playbooks/job-child-notok.yaml @@ -0,0 +1,2 @@ +- hosts: all + tasks: [] diff --git a/tests/fixtures/config/protected/git/org_project1/playbooks/placeholder b/tests/fixtures/config/protected/git/org_project1/playbooks/placeholder new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/fixtures/config/protected/git/org_project1/playbooks/placeholder diff --git a/tests/fixtures/config/protected/main.yaml b/tests/fixtures/config/protected/main.yaml new file mode 100644 index 000000000..5f57245cc --- /dev/null +++ b/tests/fixtures/config/protected/main.yaml @@ -0,0 +1,9 @@ +- tenant: + name: tenant-one + source: + gerrit: + config-projects: + - common-config + untrusted-projects: + - org/project + - org/project1 diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py index b9c9b3253..c5d19ceb4 100755 --- a/tests/unit/test_v3.py +++ b/tests/unit/test_v3.py @@ -73,6 +73,110 @@ class TestMultipleTenants(AnsibleZuulTestCase): "not affect tenant one") +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 + + - project: + name: org/project + check: + jobs: + - job-child-ok + + - job: + name: job-child-ok + parent: job-protected + + - 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() + + self.assertEqual(A.reported, 1) + self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '1') + + def test_protected_reset(self): + # try to reset protected flag + in_repo_conf = textwrap.dedent( + """ + - job: + name: job-protected + protected: true + run: playbooks/job-protected.yaml + + - job: + name: job-child-reset-protected + parent: job-protected + protected: false + + - project: + name: org/project + check: + jobs: + - job-child-reset-protected + + """) + + 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() + + # The second patch tried to override some variables. + # Thus it should fail. + self.assertEqual(A.reported, 1) + self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1') + self.assertIn('Unable to reset protected attribute', A.messages[0]) + + def test_protected_inherit_not_ok(self): + # try to inherit from a protected job in different project + in_repo_conf = textwrap.dedent( + """ + - job: + name: job-child-notok + run: playbooks/job-child-notok.yaml + parent: job-protected + + - project: + name: org/project1 + check: + jobs: + - job-child-notok + + """) + + 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.assertEqual(A.reported, 1) + self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1') + self.assertIn( + "which is defined in review.example.com/org/project is protected " + "and cannot be inherited from other projects.", A.messages[0]) + + class TestFinal(ZuulTestCase): tenant_config_file = 'config/final/main.yaml' diff --git a/zuul/configloader.py b/zuul/configloader.py index fb1695c18..669cd8bbd 100644 --- a/zuul/configloader.py +++ b/zuul/configloader.py @@ -428,6 +428,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, + 'protected': bool, 'failure-message': str, 'success-message': str, 'failure-url': str, @@ -467,6 +468,7 @@ class JobParser(object): simple_attributes = [ 'final', + 'protected', 'timeout', 'workspace', 'voting', diff --git a/zuul/model.py b/zuul/model.py index 56d08a16f..04df7a80f 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -843,6 +843,7 @@ class Job(object): semaphore=None, attempts=3, final=False, + protected=None, roles=(), required_projects={}, allowed_projects=None, @@ -860,6 +861,7 @@ class Job(object): inheritance_path=(), parent_data=None, description=None, + protected_origin=None, ) self.inheritable_attributes = {} @@ -1037,12 +1039,21 @@ class Job(object): for k in self.execution_attributes: if (other._get(k) is not None and - k not in set(['final'])): + k not in set(['final', 'protected'])): if self.final: raise Exception("Unable to modify final job %s attribute " "%s=%s with variant %s" % ( repr(self), k, other._get(k), repr(other))) + if self.protected_origin: + # this is a protected job, check origin of job definition + this_origin = self.protected_origin + other_origin = other.source_context.project.canonical_name + if this_origin != other_origin: + raise Exception("Job %s which is defined in %s is " + "protected and cannot be inherited " + "from other projects." + % (repr(self), this_origin)) if k not in set(['pre_run', 'run', 'post_run', 'roles', 'variables', 'required_projects']): # TODO(jeblair): determine if deepcopy is required @@ -1053,6 +1064,17 @@ class Job(object): if other.final != self.attributes['final']: self.final = other.final + # Protected may only be set to true + if other.protected is not None: + # don't allow to reset protected flag + if not other.protected and self.protected_origin: + raise Exception("Unable to reset protected attribute of job" + " %s by job %s" % ( + repr(self), repr(other))) + if not self.protected_origin: + self.protected_origin = \ + other.source_context.project.canonical_name + # We must update roles before any playbook contexts if other._get('roles') is not None: self.addRoles(other.roles) |