Allow installation of roles from yaml roles file

Added docs
Added more tests
Improved how roles are returned from the parsers
This commit is contained in:
Will Thames 2014-08-18 21:27:41 +10:00 committed by Michael DeHaan
parent 46b59b02ed
commit ada9074276
7 changed files with 71 additions and 28 deletions

View file

@ -696,10 +696,12 @@ def execute_install(args, options, parser):
roles_done = [] roles_done = []
if role_file: if role_file:
# roles listed in a file, one per line
# so we'll go through and grab them all
f = open(role_file, 'r') f = open(role_file, 'r')
roles_left = f.readlines() if role_file.endswith('.yaml') or role_file.endswith('.yml'):
roles_left = map(ansible.utils.role_yaml_parse, yaml.safe_load(f))
else:
# roles listed in a file, one per line
roles_left = map(ansible.utils.role_spec_parse, f.readlines())
f.close() f.close()
else: else:
# roles were specified directly, so we'll just go out grab them # roles were specified directly, so we'll just go out grab them
@ -708,16 +710,18 @@ def execute_install(args, options, parser):
while len(roles_left) > 0: while len(roles_left) > 0:
# query the galaxy API for the role data # query the galaxy API for the role data
(scm, role_src, role_version, role_name) = ansible.utils.role_spec_parse(roles_left.pop(0))
role_data = None role_data = None
role = roles_left.pop(0)
role_src = role.get("src")
role_scm = role.get("scm")
if os.path.isfile(role_src): if os.path.isfile(role_src):
# installing a local tar.gz # installing a local tar.gz
tmp_file = role_src tmp_file = role_src
else: else:
if scm: if role_scm:
# create tar file from scm url # create tar file from scm url
tmp_file = scm_archive_role(scm, role_src, role_version, role_name) tmp_file = scm_archive_role(role_scm, role_src, role.get("version"), role.get("name"))
elif '://' in role_src: elif '://' in role_src:
# just download a URL - version will probably be in the URL # just download a URL - version will probably be in the URL
tmp_file = fetch_role(role_src, None, None, options) tmp_file = fetch_role(role_src, None, None, options)
@ -729,7 +733,7 @@ def execute_install(args, options, parser):
continue continue
role_versions = api_fetch_role_related(api_server, 'versions', role_data['id']) role_versions = api_fetch_role_related(api_server, 'versions', role_data['id'])
if not role_version: if "version" not in role:
# convert the version names to LooseVersion objects # convert the version names to LooseVersion objects
# and sort them to get the latest version. If there # and sort them to get the latest version. If there
# are no versions in the list, we'll grab the head # are no versions in the list, we'll grab the head
@ -737,40 +741,43 @@ def execute_install(args, options, parser):
if len(role_versions) > 0: if len(role_versions) > 0:
loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions] loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions]
loose_versions.sort() loose_versions.sort()
role_version = str(loose_versions[-1]) role["version"] = str(loose_versions[-1])
else: else:
role_version = 'master' role["version"] = 'master'
print " no version specified, installing %s" % role_version print " no version specified, installing %s" % role.version
else: else:
if role_versions and role_version not in [a.get('name',None) for a in role_versions]: if role_versions and role["version"] not in [a.get('name',None) for a in role_versions]:
print "The specified version (%s) was not found in the list of available versions." % role_version print "The specified version (%s) was not found in the list of available versions." % role.version
exit_without_ignore(options) exit_without_ignore(options)
continue continue
# download the role. if --no-deps was specified, we stop here, # download the role. if --no-deps was specified, we stop here,
# otherwise we recursively grab roles and all of their deps. # otherwise we recursively grab roles and all of their deps.
tmp_file = fetch_role(role_src, role_version, role_data, options) tmp_file = fetch_role(role_src, role["version"], role_data, options)
if tmp_file and install_role(role_name, role_version, tmp_file, options): if tmp_file and install_role(role.get("name"), role.get("version"), tmp_file, options):
# we're done with the temp file, clean it up # we're done with the temp file, clean it up
os.unlink(tmp_file) os.unlink(tmp_file)
# install dependencies, if we want them # install dependencies, if we want them
if not no_deps: if not no_deps:
if not role_data: if not role_data:
role_data = get_role_metadata(role_name, options) role_data = get_role_metadata(role.get("name"), options)
role_dependencies = role_data['dependencies'] role_dependencies = role_data['dependencies']
else: else:
role_dependencies = role_data['summary_fields']['dependencies'] # api_fetch_role_related(api_server, 'dependencies', role_data['id']) role_dependencies = role_data['summary_fields']['dependencies'] # api_fetch_role_related(api_server, 'dependencies', role_data['id'])
for dep_name in role_dependencies: for dep in role_dependencies:
#dep_name = "%s.%s" % (dep['owner'], dep['name']) if isinstance(dep, str):
if not get_role_metadata(dep_name.split('/')[-1], options): dep = ansible.utils.role_spec_parse(dep)
print ' adding dependency: %s' % dep_name
roles_left.append(dep_name)
else: else:
print ' dependency %s is already installed, skipping.' % dep_name dep = ansible.utils.role_yaml_parse(dep)
if not get_role_metadata(dep["name"], options):
print ' adding dependency: %s' % dep["name"]
roles_left.append(dep)
else:
print ' dependency %s is already installed, skipping.' % dep["name"]
else: else:
if tmp_file: if tmp_file:
os.unlink(tmp_file) os.unlink(tmp_file)
print "%s was NOT installed successfully." % role_name print "%s was NOT installed successfully." % role.get("name")
exit_without_ignore(options) exit_without_ignore(options)
sys.exit(0) sys.exit(0)

View file

@ -299,6 +299,13 @@ Role dependencies can also be specified as a full path, just like top level role
dependencies: dependencies:
- { role: '/path/to/common/roles/foo', x: 1 } - { role: '/path/to/common/roles/foo', x: 1 }
Role dependencies can also be installed from source control repos or tar files, using a comma separated format of path, an optional version (tag, commit, branch etc) and optional friendly role name (an attempt is made to derive a role name from the repo name or archive filename)::
---
dependencies:
- { role: 'git+http://git.example.com/repos/role-foo,v1.1,foo' }
- { role: '/path/to/tar/file.tgz,,friendly-name' }
Roles dependencies are always executed before the role that includes them, and are recursive. By default, Roles dependencies are always executed before the role that includes them, and are recursive. By default,
roles can also only be added as a dependency once - if another role also lists it as a dependency it will roles can also only be added as a dependency once - if another role also lists it as a dependency it will
not be run again. This behavior can be overridden by adding `allow_duplicates: yes` to the `meta/main.yml` file. not be run again. This behavior can be overridden by adding `allow_duplicates: yes` to the `meta/main.yml` file.

View file

@ -187,7 +187,7 @@ class Play(object):
raise errors.AnsibleError("expected a role name in dictionary: %s" % orig_path) raise errors.AnsibleError("expected a role name in dictionary: %s" % orig_path)
role_vars = orig_path role_vars = orig_path
else: else:
(scm, role_src, role_version, role_name) = utils.role_spec_parse(orig_path) role_name = utils.role_spec_parse(orig_path)["name"]
role_path = None role_path = None
@ -412,8 +412,7 @@ class Play(object):
if isinstance(role, dict): if isinstance(role, dict):
role_name = role['role'] role_name = role['role']
else: else:
role_name = role role_name = utils.role_spec_parse(role)["name"]
(scm, role_src, role_version, role_name) = utils.role_spec_parse(role_name)
role_names.append(role_name) role_names.append(role_name)
if os.path.isfile(task): if os.path.isfile(task):

View file

@ -380,7 +380,17 @@ def role_spec_parse(role_spec):
role_name = tokens[2] role_name = tokens[2]
else: else:
role_name = repo_url_to_role_name(tokens[0]) role_name = repo_url_to_role_name(tokens[0])
return (scm, role_url, role_version, role_name) return dict(scm=scm, src=role_url, version=role_version, name=role_name)
def role_yaml_parse(role):
if '+' in role["src"]:
(scm, src) = role["src"].split('+')
role["scm"] = scm
role["src"] = src
if 'name' not in role:
role["name"] = repo_url_to_role_name(role["src"])
return role
def json_loads(data): def json_loads(data):

View file

@ -105,7 +105,9 @@ rackspace: $(CREDENTIALS_FILE)
CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make rackspace_cleanup ; \ CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make rackspace_cleanup ; \
exit $$RC; exit $$RC;
test_galaxy: test_galaxy: test_galaxy_spec test_galaxy_yaml
test_galaxy_spec:
mytmpdir=$(TMPDIR) ; \ mytmpdir=$(TMPDIR) ; \
ansible-galaxy install -r galaxy_rolesfile -p $$mytmpdir/roles ; \ ansible-galaxy install -r galaxy_rolesfile -p $$mytmpdir/roles ; \
cp galaxy_playbook.yml $$mytmpdir ; \ cp galaxy_playbook.yml $$mytmpdir ; \
@ -113,3 +115,12 @@ test_galaxy:
RC=$$? ; \ RC=$$? ; \
rm -rf $$mytmpdir ; \ rm -rf $$mytmpdir ; \
exit $$RC exit $$RC
test_galaxy_yaml:
mytmpdir=$(TMPDIR) ; \
ansible-galaxy install -r galaxy_roles.yml -p $$mytmpdir/roles ; \
cp galaxy_playbook.yml $$mytmpdir ; \
ansible-playbook -i $(INVENTORY) $$mytmpdir/galaxy_playbook.yml -v $(TEST_FLAGS) ; \
RC=$$? ; \
rm -rf $$mytmpdir ; \
exit $$RC

View file

@ -4,3 +4,4 @@
roles: roles:
- "git-ansible-galaxy" - "git-ansible-galaxy"
- "http-role" - "http-role"
- "hg-ansible-galaxy"

View file

@ -0,0 +1,8 @@
- src: git+http://bitbucket.org/willthames/git-ansible-galaxy
version: v1.4
- src: ssh://hg@bitbucket.org/willthames/hg-ansible-galaxy
scm: hg
- src: https://bitbucket.org/willthames/http-ansible-galaxy/get/master.tar.gz
name: http-role