/lib/ansible/playbook/role/requirement.py
https://github.com/debfx/ansible · Python · 191 lines · 133 code · 34 blank · 24 comment · 44 complexity · 359be95acbab8e2e06c39fa3ad8f9ca3 MD5 · raw file
- # (c) 2014 Michael DeHaan, <michael@ansible.com>
- #
- # This file is part of Ansible
- #
- # Ansible is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # Ansible is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
- # Make coding more python3-ish
- from __future__ import (absolute_import, division, print_function)
- __metaclass__ = type
- import os
- import tempfile
- import tarfile
- from subprocess import Popen, PIPE
- from ansible import constants as C
- from ansible.errors import AnsibleError
- from ansible.module_utils._text import to_native
- from ansible.module_utils.common.process import get_bin_path
- from ansible.module_utils.six import string_types
- from ansible.playbook.role.definition import RoleDefinition
- from ansible.utils.display import Display
- __all__ = ['RoleRequirement']
- VALID_SPEC_KEYS = [
- 'name',
- 'role',
- 'scm',
- 'src',
- 'version',
- ]
- display = Display()
- class RoleRequirement(RoleDefinition):
- """
- Helper class for Galaxy, which is used to parse both dependencies
- specified in meta/main.yml and requirements.yml files.
- """
- def __init__(self):
- pass
- @staticmethod
- def repo_url_to_role_name(repo_url):
- # gets the role name out of a repo like
- # http://git.example.com/repos/repo.git" => "repo"
- if '://' not in repo_url and '@' not in repo_url:
- return repo_url
- trailing_path = repo_url.split('/')[-1]
- if trailing_path.endswith('.git'):
- trailing_path = trailing_path[:-4]
- if trailing_path.endswith('.tar.gz'):
- trailing_path = trailing_path[:-7]
- if ',' in trailing_path:
- trailing_path = trailing_path.split(',')[0]
- return trailing_path
- @staticmethod
- def role_yaml_parse(role):
- if isinstance(role, string_types):
- name = None
- scm = None
- src = None
- version = None
- if ',' in role:
- if role.count(',') == 1:
- (src, version) = role.strip().split(',', 1)
- elif role.count(',') == 2:
- (src, version, name) = role.strip().split(',', 2)
- else:
- raise AnsibleError("Invalid role line (%s). Proper format is 'role_name[,version[,name]]'" % role)
- else:
- src = role
- if name is None:
- name = RoleRequirement.repo_url_to_role_name(src)
- if '+' in src:
- (scm, src) = src.split('+', 1)
- return dict(name=name, src=src, scm=scm, version=version)
- if 'role' in role:
- name = role['role']
- if ',' in name:
- raise AnsibleError("Invalid old style role requirement: %s" % name)
- else:
- del role['role']
- role['name'] = name
- else:
- role = role.copy()
- if 'src'in role:
- # New style: { src: 'galaxy.role,version,name', other_vars: "here" }
- if 'github.com' in role["src"] and 'http' in role["src"] and '+' not in role["src"] and not role["src"].endswith('.tar.gz'):
- role["src"] = "git+" + role["src"]
- if '+' in role["src"]:
- (scm, src) = role["src"].split('+')
- role["scm"] = scm
- role["src"] = src
- if 'name' not in role:
- role["name"] = RoleRequirement.repo_url_to_role_name(role["src"])
- if 'version' not in role:
- role['version'] = ''
- if 'scm' not in role:
- role['scm'] = None
- for key in list(role.keys()):
- if key not in VALID_SPEC_KEYS:
- role.pop(key)
- return role
- @staticmethod
- def scm_archive_role(src, scm='git', name=None, version='HEAD', keep_scm_meta=False):
- def run_scm_cmd(cmd, tempdir):
- try:
- stdout = ''
- stderr = ''
- popen = Popen(cmd, cwd=tempdir, stdout=PIPE, stderr=PIPE)
- stdout, stderr = popen.communicate()
- except Exception as e:
- ran = " ".join(cmd)
- display.debug("ran %s:" % ran)
- display.debug("\tstdout: " + stdout)
- display.debug("\tstderr: " + stderr)
- raise AnsibleError("when executing %s: %s" % (ran, to_native(e)))
- if popen.returncode != 0:
- raise AnsibleError("- command %s failed in directory %s (rc=%s)" % (' '.join(cmd), tempdir, popen.returncode))
- if scm not in ['hg', 'git']:
- raise AnsibleError("- scm %s is not currently supported" % scm)
- try:
- scm_path = get_bin_path(scm, required=True)
- except (ValueError, OSError, IOError):
- raise AnsibleError("could not find/use %s, it is required to continue with installing %s" % (scm, src))
- tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP)
- clone_cmd = [scm_path, 'clone', src, name]
- run_scm_cmd(clone_cmd, tempdir)
- if scm == 'git' and version:
- checkout_cmd = [scm_path, 'checkout', version]
- run_scm_cmd(checkout_cmd, os.path.join(tempdir, name))
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.tar', dir=C.DEFAULT_LOCAL_TMP)
- archive_cmd = None
- if keep_scm_meta:
- display.vvv('tarring %s from %s to %s' % (name, tempdir, temp_file.name))
- with tarfile.open(temp_file.name, "w") as tar:
- tar.add(os.path.join(tempdir, name), arcname=name)
- elif scm == 'hg':
- archive_cmd = [scm_path, 'archive', '--prefix', "%s/" % name]
- if version:
- archive_cmd.extend(['-r', version])
- archive_cmd.append(temp_file.name)
- elif scm == 'git':
- archive_cmd = [scm_path, 'archive', '--prefix=%s/' % name, '--output=%s' % temp_file.name]
- if version:
- archive_cmd.append(version)
- else:
- archive_cmd.append('HEAD')
- if archive_cmd is not None:
- display.vvv('archiving %s' % archive_cmd)
- run_scm_cmd(archive_cmd, os.path.join(tempdir, name))
- return temp_file.name