Merge "Add job's project as implicit role project" into feature/zuulv3
This commit is contained in:
commit
ade95e2970
|
@ -747,6 +747,12 @@ unless otherwise specified:
|
|||
be installed (and therefore referenced from Ansible), the `name`
|
||||
attribute may be used to specify an alternate.
|
||||
|
||||
A job automatically has the project in which it is defined added to
|
||||
the roles path if that project appears to contain a role or `roles/`
|
||||
directory. By default, the project is added to the path under its
|
||||
own name, however, that may be changed by explicitly listing the
|
||||
project in the roles list in the usual way.
|
||||
|
||||
.. note:: galaxy roles are not yet implemented
|
||||
|
||||
**galaxy**
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
- pipeline:
|
||||
name: check
|
||||
manager: independent
|
||||
trigger:
|
||||
gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
gerrit:
|
||||
verified: -1
|
|
@ -0,0 +1,15 @@
|
|||
- job:
|
||||
name: implicit-role-fail
|
||||
|
||||
- job:
|
||||
name: explicit-role-fail
|
||||
attempts: 1
|
||||
roles:
|
||||
- zuul: org/norole-project
|
||||
|
||||
- project:
|
||||
name: org/norole-project
|
||||
check:
|
||||
jobs:
|
||||
- implicit-role-fail
|
||||
- explicit-role-fail
|
|
@ -0,0 +1 @@
|
|||
test
|
|
@ -0,0 +1,2 @@
|
|||
- hosts: all
|
||||
tasks: []
|
|
@ -0,0 +1,2 @@
|
|||
- hosts: all
|
||||
tasks: []
|
|
@ -0,0 +1,15 @@
|
|||
- job:
|
||||
name: implicit-role-ok
|
||||
|
||||
- job:
|
||||
name: explicit-role-ok
|
||||
roles:
|
||||
- zuul: org/role-project
|
||||
name: role-name
|
||||
|
||||
- project:
|
||||
name: org/role-project
|
||||
check:
|
||||
jobs:
|
||||
- implicit-role-ok
|
||||
- explicit-role-ok
|
|
@ -0,0 +1 @@
|
|||
test
|
2
tests/fixtures/config/implicit-roles/git/org_role-project/playbooks/explicit-role-ok.yaml
vendored
Normal file
2
tests/fixtures/config/implicit-roles/git/org_role-project/playbooks/explicit-role-ok.yaml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
- hosts: all
|
||||
tasks: []
|
2
tests/fixtures/config/implicit-roles/git/org_role-project/playbooks/implicit-role-ok.yaml
vendored
Normal file
2
tests/fixtures/config/implicit-roles/git/org_role-project/playbooks/implicit-role-ok.yaml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
- hosts: all
|
||||
tasks: []
|
|
@ -0,0 +1,9 @@
|
|||
- tenant:
|
||||
name: tenant-one
|
||||
source:
|
||||
gerrit:
|
||||
config-projects:
|
||||
- common-config
|
||||
untrusted-projects:
|
||||
- org/norole-project
|
||||
- org/role-project
|
|
@ -716,9 +716,7 @@ class TestProjectKeys(ZuulTestCase):
|
|||
self.assertEqual(4096, private_key.key_size)
|
||||
|
||||
|
||||
class TestRoles(ZuulTestCase):
|
||||
tenant_config_file = 'config/roles/main.yaml'
|
||||
|
||||
class RoleTestCase(ZuulTestCase):
|
||||
def _assertRolePath(self, build, playbook, content):
|
||||
path = os.path.join(self.test_root, build.uuid,
|
||||
'ansible', playbook, 'ansible.cfg')
|
||||
|
@ -738,6 +736,10 @@ class TestRoles(ZuulTestCase):
|
|||
"Should have no roles_path line in %s" %
|
||||
(playbook,))
|
||||
|
||||
|
||||
class TestRoles(RoleTestCase):
|
||||
tenant_config_file = 'config/roles/main.yaml'
|
||||
|
||||
def test_role(self):
|
||||
# This exercises a proposed change to a role being checked out
|
||||
# and used.
|
||||
|
@ -822,6 +824,57 @@ class TestRoles(ZuulTestCase):
|
|||
A.messages[-1])
|
||||
|
||||
|
||||
class TestImplicitRoles(RoleTestCase):
|
||||
tenant_config_file = 'config/implicit-roles/main.yaml'
|
||||
|
||||
def test_missing_roles(self):
|
||||
# Test implicit and explicit roles for a project which does
|
||||
# not have roles. The implicit role should be silently
|
||||
# ignored since the project doesn't supply roles, but if a
|
||||
# user declares an explicit role, it should error.
|
||||
self.executor_server.hold_jobs_in_build = True
|
||||
A = self.fake_gerrit.addFakeChange('org/norole-project', 'master', 'A')
|
||||
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
||||
self.waitUntilSettled()
|
||||
|
||||
self.assertEqual(len(self.builds), 2)
|
||||
build = self.getBuildByName('implicit-role-fail')
|
||||
self._assertRolePath(build, 'playbook_0', None)
|
||||
|
||||
self.executor_server.hold_jobs_in_build = False
|
||||
self.executor_server.release()
|
||||
self.waitUntilSettled()
|
||||
# The retry_limit doesn't get recorded
|
||||
self.assertHistory([
|
||||
dict(name='implicit-role-fail', result='SUCCESS', changes='1,1'),
|
||||
])
|
||||
|
||||
def test_roles(self):
|
||||
# Test implicit and explicit roles for a project which does
|
||||
# have roles. In both cases, we should end up with the role
|
||||
# in the path. In the explicit case, ensure we end up with
|
||||
# the name we specified.
|
||||
self.executor_server.hold_jobs_in_build = True
|
||||
A = self.fake_gerrit.addFakeChange('org/role-project', 'master', 'A')
|
||||
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
||||
self.waitUntilSettled()
|
||||
|
||||
self.assertEqual(len(self.builds), 2)
|
||||
build = self.getBuildByName('implicit-role-ok')
|
||||
self._assertRolePath(build, 'playbook_0', 'role_0')
|
||||
|
||||
build = self.getBuildByName('explicit-role-ok')
|
||||
self._assertRolePath(build, 'playbook_0', 'role_0')
|
||||
|
||||
self.executor_server.hold_jobs_in_build = False
|
||||
self.executor_server.release()
|
||||
self.waitUntilSettled()
|
||||
self.assertHistory([
|
||||
dict(name='implicit-role-ok', result='SUCCESS', changes='1,1'),
|
||||
dict(name='explicit-role-ok', result='SUCCESS', changes='1,1'),
|
||||
], ordered=False)
|
||||
|
||||
|
||||
class TestShadow(ZuulTestCase):
|
||||
tenant_config_file = 'config/shadow/main.yaml'
|
||||
|
||||
|
|
|
@ -425,13 +425,18 @@ class JobParser(object):
|
|||
|
||||
# Roles are part of the playbook context so we must establish
|
||||
# them earlier than playbooks.
|
||||
if 'roles' in conf:
|
||||
roles = []
|
||||
if 'roles' in conf:
|
||||
for role in conf.get('roles', []):
|
||||
if 'zuul' in role:
|
||||
r = JobParser._makeZuulRole(tenant, job, role)
|
||||
if r:
|
||||
roles.append(r)
|
||||
# A job's repo should be an implicit role source for that job,
|
||||
# but not in a project-pipeline variant.
|
||||
if not project_pipeline:
|
||||
r = JobParser._makeImplicitRole(job)
|
||||
roles.insert(0, r)
|
||||
job.addRoles(roles)
|
||||
|
||||
for pre_run_name in as_list(conf.get('pre-run')):
|
||||
|
@ -554,6 +559,15 @@ class JobParser(object):
|
|||
project.connection_name,
|
||||
project.name)
|
||||
|
||||
@staticmethod
|
||||
def _makeImplicitRole(job):
|
||||
project = job.source_context.project
|
||||
name = project.name.split('/')[-1]
|
||||
return model.ZuulRole(name,
|
||||
project.connection_name,
|
||||
project.name,
|
||||
implicit=True)
|
||||
|
||||
|
||||
class ProjectTemplateParser(object):
|
||||
log = logging.getLogger("zuul.ProjectTemplateParser")
|
||||
|
|
|
@ -51,6 +51,10 @@ class ExecutorError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class RoleNotFoundError(ExecutorError):
|
||||
pass
|
||||
|
||||
|
||||
class Watchdog(object):
|
||||
def __init__(self, timeout, function, args):
|
||||
self.timeout = timeout
|
||||
|
@ -1137,12 +1141,14 @@ class AnsibleJob(object):
|
|||
if os.path.isdir(d):
|
||||
# This repo has a collection of roles
|
||||
if not trusted:
|
||||
self._blockPluginDirs(d)
|
||||
for entry in os.listdir(d):
|
||||
if os.path.isdir(os.path.join(d, entry)):
|
||||
self._blockPluginDirs(os.path.join(d, entry))
|
||||
entry_path = os.path.join(d, entry)
|
||||
if os.path.isdir(entry_path):
|
||||
self._blockPluginDirs(entry_path)
|
||||
return d
|
||||
# It is neither a bare role, nor a collection of roles
|
||||
raise ExecutorError("Unable to find role in %s" % (path,))
|
||||
raise RoleNotFoundError("Unable to find role in %s" % (path,))
|
||||
|
||||
def prepareZuulRole(self, jobdir_playbook, role, args, root):
|
||||
self.log.debug("Prepare zuul role for %s" % (role,))
|
||||
|
@ -1183,10 +1189,17 @@ class AnsibleJob(object):
|
|||
raise ExecutorError("Invalid role name %s", name)
|
||||
os.symlink(path, link)
|
||||
|
||||
try:
|
||||
role_path = self.findRole(link, trusted=jobdir_playbook.trusted)
|
||||
except RoleNotFoundError:
|
||||
if role['implicit']:
|
||||
self.log.info("Implicit role not found in %s", link)
|
||||
return
|
||||
raise
|
||||
if role_path is None:
|
||||
# In the case of a bare role, add the containing directory
|
||||
role_path = root
|
||||
self.log.debug("Adding role path %s", role_path)
|
||||
jobdir_playbook.roles_path.append(role_path)
|
||||
|
||||
def prepareAnsibleFiles(self, args):
|
||||
|
|
|
@ -702,10 +702,12 @@ class Role(object, metaclass=abc.ABCMeta):
|
|||
class ZuulRole(Role):
|
||||
"""A reference to an ansible role in a Zuul project."""
|
||||
|
||||
def __init__(self, target_name, connection_name, project_name):
|
||||
def __init__(self, target_name, connection_name, project_name,
|
||||
implicit=False):
|
||||
super(ZuulRole, self).__init__(target_name)
|
||||
self.connection_name = connection_name
|
||||
self.project_name = project_name
|
||||
self.implicit = implicit
|
||||
|
||||
def __repr__(self):
|
||||
return '<ZuulRole %s %s>' % (self.project_name, self.target_name)
|
||||
|
@ -715,6 +717,8 @@ class ZuulRole(Role):
|
|||
def __eq__(self, other):
|
||||
if not isinstance(other, ZuulRole):
|
||||
return False
|
||||
# Implicit is not consulted for equality so that we can handle
|
||||
# implicit to explicit conversions.
|
||||
return (super(ZuulRole, self).__eq__(other) and
|
||||
self.connection_name == other.connection_name and
|
||||
self.project_name == other.project_name)
|
||||
|
@ -725,6 +729,7 @@ class ZuulRole(Role):
|
|||
d['type'] = 'zuul'
|
||||
d['connection'] = self.connection_name
|
||||
d['project'] = self.project_name
|
||||
d['implicit'] = self.implicit
|
||||
return d
|
||||
|
||||
|
||||
|
@ -867,10 +872,30 @@ class Job(object):
|
|||
self.run = self.implied_run
|
||||
|
||||
def addRoles(self, roles):
|
||||
newroles = list(self.roles)
|
||||
newroles = []
|
||||
# Start with a copy of the existing roles, but if any of them
|
||||
# are implicit roles which are identified as explicit in the
|
||||
# new roles list, replace them with the explicit version.
|
||||
changed = False
|
||||
for existing_role in self.roles:
|
||||
if existing_role in roles:
|
||||
new_role = roles[roles.index(existing_role)]
|
||||
else:
|
||||
new_role = None
|
||||
if (new_role and
|
||||
isinstance(new_role, ZuulRole) and
|
||||
isinstance(existing_role, ZuulRole) and
|
||||
existing_role.implicit and not new_role.implicit):
|
||||
newroles.append(new_role)
|
||||
changed = True
|
||||
else:
|
||||
newroles.append(existing_role)
|
||||
# Now add the new roles.
|
||||
for role in reversed(roles):
|
||||
if role not in newroles:
|
||||
newroles.insert(0, role)
|
||||
changed = True
|
||||
if changed:
|
||||
self.roles = tuple(newroles)
|
||||
|
||||
def updateVariables(self, other_vars):
|
||||
|
|
Loading…
Reference in New Issue