mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
commit
97c2b2ecc5
7 changed files with 304 additions and 372 deletions
|
@ -512,3 +512,16 @@ class CLI(object):
|
||||||
|
|
||||||
return vault_pass
|
return vault_pass
|
||||||
|
|
||||||
|
def get_opt(self, k, defval=""):
|
||||||
|
"""
|
||||||
|
Returns an option from an Optparse values instance.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = getattr(self.options, k)
|
||||||
|
except:
|
||||||
|
return defval
|
||||||
|
if k == "roles_path":
|
||||||
|
if os.pathsep in data:
|
||||||
|
data = data.split(os.pathsep)[0]
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,6 @@ from distutils.version import LooseVersion
|
||||||
from jinja2 import Environment
|
from jinja2 import Environment
|
||||||
|
|
||||||
import ansible.constants as C
|
import ansible.constants as C
|
||||||
import ansible.utils
|
|
||||||
import ansible.galaxy
|
|
||||||
from ansible.cli import CLI
|
from ansible.cli import CLI
|
||||||
from ansible.errors import AnsibleError, AnsibleOptionsError
|
from ansible.errors import AnsibleError, AnsibleOptionsError
|
||||||
from ansible.galaxy import Galaxy
|
from ansible.galaxy import Galaxy
|
||||||
|
@ -126,19 +124,6 @@ class GalaxyCLI(CLI):
|
||||||
|
|
||||||
self.execute()
|
self.execute()
|
||||||
|
|
||||||
def get_opt(self, k, defval=""):
|
|
||||||
"""
|
|
||||||
Returns an option from an Optparse values instance.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
data = getattr(self.options, k)
|
|
||||||
except:
|
|
||||||
return defval
|
|
||||||
if k == "roles_path":
|
|
||||||
if os.pathsep in data:
|
|
||||||
data = data.split(os.pathsep)[0]
|
|
||||||
return data
|
|
||||||
|
|
||||||
def exit_without_ignore(self, rc=1):
|
def exit_without_ignore(self, rc=1):
|
||||||
"""
|
"""
|
||||||
Exits with the specified return code unless the
|
Exits with the specified return code unless the
|
||||||
|
@ -147,40 +132,6 @@ class GalaxyCLI(CLI):
|
||||||
if not self.get_opt("ignore_errors", False):
|
if not self.get_opt("ignore_errors", False):
|
||||||
raise AnsibleError('- you can use --ignore-errors to skip failed roles and finish processing the list.')
|
raise AnsibleError('- you can use --ignore-errors to skip failed roles and finish processing the list.')
|
||||||
|
|
||||||
def parse_requirements_files(self, role):
|
|
||||||
if 'role' in role:
|
|
||||||
# Old style: {role: "galaxy.role,version,name", other_vars: "here" }
|
|
||||||
role_info = role_spec_parse(role['role'])
|
|
||||||
if isinstance(role_info, dict):
|
|
||||||
# Warning: Slight change in behaviour here. name may be being
|
|
||||||
# overloaded. Previously, name was only a parameter to the role.
|
|
||||||
# Now it is both a parameter to the role and the name that
|
|
||||||
# ansible-galaxy will install under on the local system.
|
|
||||||
if 'name' in role and 'name' in role_info:
|
|
||||||
del role_info['name']
|
|
||||||
role.update(role_info)
|
|
||||||
else:
|
|
||||||
# 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"] = GalaxyRole.url_to_spec(role["src"])
|
|
||||||
|
|
||||||
if 'version' not in role:
|
|
||||||
role['version'] = ''
|
|
||||||
|
|
||||||
if 'scm' not in role:
|
|
||||||
role['scm'] = None
|
|
||||||
|
|
||||||
return role
|
|
||||||
|
|
||||||
|
|
||||||
def _display_role_info(self, role_info):
|
def _display_role_info(self, role_info):
|
||||||
|
|
||||||
text = "\nRole: %s \n" % role_info['name']
|
text = "\nRole: %s \n" % role_info['name']
|
||||||
|
@ -298,9 +249,8 @@ class GalaxyCLI(CLI):
|
||||||
data = ''
|
data = ''
|
||||||
for role in self.args:
|
for role in self.args:
|
||||||
|
|
||||||
role_info = {}
|
role_info = {'role_path': roles_path}
|
||||||
gr = GalaxyRole(self.galaxy, role)
|
gr = GalaxyRole(self.galaxy, role)
|
||||||
#self.galaxy.add_role(gr)
|
|
||||||
|
|
||||||
install_info = gr.install_info
|
install_info = gr.install_info
|
||||||
if install_info:
|
if install_info:
|
||||||
|
@ -351,118 +301,73 @@ class GalaxyCLI(CLI):
|
||||||
|
|
||||||
no_deps = self.get_opt("no_deps", False)
|
no_deps = self.get_opt("no_deps", False)
|
||||||
force = self.get_opt('force', False)
|
force = self.get_opt('force', False)
|
||||||
roles_path = self.get_opt("roles_path")
|
|
||||||
|
|
||||||
roles_done = []
|
|
||||||
roles_left = []
|
roles_left = []
|
||||||
if role_file:
|
if role_file:
|
||||||
self.display.debug('Getting roles from %s' % role_file)
|
|
||||||
try:
|
try:
|
||||||
self.display.debug('Processing role file: %s' % role_file)
|
|
||||||
f = open(role_file, 'r')
|
f = open(role_file, 'r')
|
||||||
if role_file.endswith('.yaml') or role_file.endswith('.yml'):
|
if role_file.endswith('.yaml') or role_file.endswith('.yml'):
|
||||||
try:
|
for role in yaml.safe_load(f.read()):
|
||||||
rolesparsed = map(self.parse_requirements_files, yaml.safe_load(f))
|
self.display.debug('found role %s in yaml file' % str(role))
|
||||||
except Exception as e:
|
if 'name' not in role:
|
||||||
raise AnsibleError("%s does not seem like a valid yaml file: %s" % (role_file, str(e)))
|
if 'src' in role:
|
||||||
roles_left = [GalaxyRole(self.galaxy, **r) for r in rolesparsed]
|
role['name'] = RoleRequirement.repo_url_to_role_name(role['src'])
|
||||||
|
else:
|
||||||
|
raise AnsibleError("Must specify name or src for role")
|
||||||
|
roles_left.append(GalaxyRole(self.galaxy, **role))
|
||||||
else:
|
else:
|
||||||
|
self.display.deprecated("going forward only the yaml format will be supported")
|
||||||
# roles listed in a file, one per line
|
# roles listed in a file, one per line
|
||||||
self.display.deprecated("Non yaml files for role requirements")
|
for rline in f.readlines():
|
||||||
for rname in f.readlines():
|
self.display.debug('found role %s in text file' % str(rline))
|
||||||
if rname.startswith("#") or rname.strip() == '':
|
roles_left.append(GalaxyRole(self.galaxy, **RoleRequirement.role_spec_parse(rline)))
|
||||||
continue
|
|
||||||
roles_left.append(GalaxyRole(self.galaxy, rname.strip()))
|
|
||||||
f.close()
|
f.close()
|
||||||
except (IOError,OSError) as e:
|
except (IOError, OSError) as e:
|
||||||
raise AnsibleError("Unable to read requirements file (%s): %s" % (role_file, str(e)))
|
self.display.error('Unable to open %s: %s' % (role_file, str(e)))
|
||||||
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
|
||||||
# (and their dependencies, unless the user doesn't want us to).
|
# (and their dependencies, unless the user doesn't want us to).
|
||||||
for rname in self.args:
|
for rname in self.args:
|
||||||
roles_left.append(GalaxyRole(self.galaxy, rname.strip()))
|
roles_left.append(GalaxyRole(self.galaxy, rname.strip()))
|
||||||
|
|
||||||
while len(roles_left) > 0:
|
for role in roles_left:
|
||||||
|
self.display.debug('Installing role %s ' % role.name)
|
||||||
# query the galaxy API for the role data
|
# query the galaxy API for the role data
|
||||||
role_data = None
|
role_data = None
|
||||||
role = roles_left.pop(0)
|
role = roles_left.pop(0)
|
||||||
role_path = role.path
|
|
||||||
|
|
||||||
if role.install_info is not None and not force:
|
if role.install_info is not None and not force:
|
||||||
self.display.display('- %s is already installed, skipping.' % role.name)
|
self.display.display('- %s is already installed, skipping.' % role.name)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if role_path:
|
try:
|
||||||
self.options.roles_path = role_path
|
installed = role.install()
|
||||||
else:
|
except AnsibleError as e:
|
||||||
self.options.roles_path = roles_path
|
self.display.warning("- %s was NOT installed successfully: %s " % (role.name, str(e)))
|
||||||
|
self.exit_without_ignore()
|
||||||
|
continue
|
||||||
|
|
||||||
self.display.debug('Installing role %s from %s' % (role.name, self.options.roles_path))
|
# install dependencies, if we want them
|
||||||
|
if not no_deps and installed:
|
||||||
tmp_file = None
|
role_dependencies = role.metadata.get('dependencies', [])
|
||||||
installed = False
|
for dep in role_dependencies:
|
||||||
if role.src and os.path.isfile(role.src):
|
self.display.debug('Installing dep %s' % dep)
|
||||||
# installing a local tar.gz
|
dep_req = RoleRequirement()
|
||||||
tmp_file = role.src
|
__, dep_name, __ = dep_req.parse(dep)
|
||||||
else:
|
dep_role = GalaxyRole(self.galaxy, name=dep_name)
|
||||||
if role.scm:
|
if dep_role.install_info is None or force:
|
||||||
# create tar file from scm url
|
if dep_role not in roles_left:
|
||||||
tmp_file = GalaxyRole.scm_archive_role(role.scm, role.src, role.version, role.name)
|
self.display.display('- adding dependency: %s' % dep_name)
|
||||||
if role.src:
|
roles_left.append(GalaxyRole(self.galaxy, name=dep_name))
|
||||||
if '://' not in role.src:
|
|
||||||
role_data = self.api.lookup_role_by_name(role.src)
|
|
||||||
if not role_data:
|
|
||||||
self.display.warning("- sorry, %s was not found on %s." % (role.src, self.options.api_server))
|
|
||||||
self.exit_without_ignore()
|
|
||||||
continue
|
|
||||||
|
|
||||||
role_versions = self.api.fetch_role_related('versions', role_data['id'])
|
|
||||||
if not role.version:
|
|
||||||
# convert the version names to LooseVersion objects
|
|
||||||
# and sort them to get the latest version. If there
|
|
||||||
# are no versions in the list, we'll grab the head
|
|
||||||
# of the master branch
|
|
||||||
if len(role_versions) > 0:
|
|
||||||
loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions]
|
|
||||||
loose_versions.sort()
|
|
||||||
role.version = str(loose_versions[-1])
|
|
||||||
else:
|
|
||||||
role.version = 'master'
|
|
||||||
elif role.version != 'master':
|
|
||||||
if role_versions and role.version not in [a.get('name', None) for a in role_versions]:
|
|
||||||
self.display.warning('role is %s' % role)
|
|
||||||
self.display.warning("- the specified version (%s) was not found in the list of available versions (%s)." % (role.version, role_versions))
|
|
||||||
self.exit_without_ignore()
|
|
||||||
continue
|
|
||||||
|
|
||||||
# download the role. if --no-deps was specified, we stop here,
|
|
||||||
# otherwise we recursively grab roles and all of their deps.
|
|
||||||
tmp_file = role.fetch(role_data)
|
|
||||||
if tmp_file:
|
|
||||||
installed = role.install(tmp_file)
|
|
||||||
# we're done with the temp file, clean it up
|
|
||||||
if tmp_file != role.src:
|
|
||||||
os.unlink(tmp_file)
|
|
||||||
# install dependencies, if we want them
|
|
||||||
if not no_deps and installed:
|
|
||||||
role_dependencies = role.metadata.get('dependencies', [])
|
|
||||||
for dep in role_dependencies:
|
|
||||||
self.display.debug('Installing dep %s' % dep)
|
|
||||||
dep_req = RoleRequirement()
|
|
||||||
__, dep_name, __ = dep_req.parse(dep)
|
|
||||||
dep_role = GalaxyRole(self.galaxy, name=dep_name)
|
|
||||||
if dep_role.install_info is None or force:
|
|
||||||
if dep_role not in roles_left:
|
|
||||||
self.display.display('- adding dependency: %s' % dep_name)
|
|
||||||
roles_left.append(GalaxyRole(self.galaxy, name=dep_name))
|
|
||||||
else:
|
|
||||||
self.display.display('- dependency %s already pending installation.' % dep_name)
|
|
||||||
else:
|
else:
|
||||||
self.display.display('- dependency %s is already installed, skipping.' % dep_name)
|
self.display.display('- dependency %s already pending installation.' % dep_name)
|
||||||
|
else:
|
||||||
|
self.display.display('- dependency %s is already installed, skipping.' % dep_name)
|
||||||
|
|
||||||
if not tmp_file or not installed:
|
if not installed:
|
||||||
self.display.warning("- %s was NOT installed successfully." % role.name)
|
self.display.warning("- %s was NOT installed successfully." % role.name)
|
||||||
self.exit_without_ignore()
|
self.exit_without_ignore()
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def execute_remove(self):
|
def execute_remove(self):
|
||||||
|
|
|
@ -21,15 +21,16 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import tarfile
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
import yaml
|
import yaml
|
||||||
|
from distutils.version import LooseVersion
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
|
||||||
from ansible import constants as C
|
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.module_utils.urls import open_url
|
from ansible.module_utils.urls import open_url
|
||||||
|
from ansible.playbook.role.requirement import RoleRequirement
|
||||||
|
from ansible.galaxy.api import GalaxyAPI
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from __main__ import display
|
from __main__ import display
|
||||||
|
@ -51,6 +52,7 @@ class GalaxyRole(object):
|
||||||
self._install_info = None
|
self._install_info = None
|
||||||
|
|
||||||
self.options = galaxy.options
|
self.options = galaxy.options
|
||||||
|
self.galaxy = galaxy
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.version = version
|
self.version = version
|
||||||
|
@ -135,9 +137,9 @@ class GalaxyRole(object):
|
||||||
|
|
||||||
def remove(self):
|
def remove(self):
|
||||||
"""
|
"""
|
||||||
Removes the specified role from the roles path. There is a
|
Removes the specified role from the roles path.
|
||||||
sanity check to make sure there's a meta/main.yml file at this
|
There is a sanity check to make sure there's a meta/main.yml file at this
|
||||||
path so the user doesn't blow away random directories
|
path so the user doesn't blow away random directories.
|
||||||
"""
|
"""
|
||||||
if self.metadata:
|
if self.metadata:
|
||||||
try:
|
try:
|
||||||
|
@ -159,6 +161,7 @@ class GalaxyRole(object):
|
||||||
archive_url = 'https://github.com/%s/%s/archive/%s.tar.gz' % (role_data["github_user"], role_data["github_repo"], self.version)
|
archive_url = 'https://github.com/%s/%s/archive/%s.tar.gz' % (role_data["github_user"], role_data["github_repo"], self.version)
|
||||||
else:
|
else:
|
||||||
archive_url = self.src
|
archive_url = self.src
|
||||||
|
|
||||||
display.display("- downloading role from %s" % archive_url)
|
display.display("- downloading role from %s" % archive_url)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -170,87 +173,125 @@ class GalaxyRole(object):
|
||||||
data = url_file.read()
|
data = url_file.read()
|
||||||
temp_file.close()
|
temp_file.close()
|
||||||
return temp_file.name
|
return temp_file.name
|
||||||
except:
|
except Exception as e:
|
||||||
# TODO: better urllib2 error handling for error
|
display.error("failed to download the file: %s" % str(e))
|
||||||
# messages that are more exact
|
|
||||||
display.error("failed to download the file.")
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def install(self, role_filename):
|
def install(self):
|
||||||
# the file is a tar, so open it that way and extract it
|
# the file is a tar, so open it that way and extract it
|
||||||
# to the specified (or default) roles directory
|
# to the specified (or default) roles directory
|
||||||
|
|
||||||
if not tarfile.is_tarfile(role_filename):
|
if self.scm:
|
||||||
display.error("the file downloaded was not a tar.gz")
|
# create tar file from scm url
|
||||||
return False
|
tmp_file = RoleRequirement.scm_archive_role(**self.spec)
|
||||||
else:
|
elif self.src:
|
||||||
if role_filename.endswith('.gz'):
|
if os.path.isfile(self.src):
|
||||||
role_tar_file = tarfile.open(role_filename, "r:gz")
|
# installing a local tar.gz
|
||||||
|
tmp_file = self.src
|
||||||
|
elif '://' in self.src:
|
||||||
|
role_data = self.src
|
||||||
|
tmp_file = self.fetch(role_data)
|
||||||
else:
|
else:
|
||||||
role_tar_file = tarfile.open(role_filename, "r")
|
api = GalaxyAPI(self.galaxy, self.options.api_server)
|
||||||
# verify the role's meta file
|
role_data = api.lookup_role_by_name(self.src)
|
||||||
meta_file = None
|
if not role_data:
|
||||||
members = role_tar_file.getmembers()
|
raise AnsibleError("- sorry, %s was not found on %s." % (self.src, self.options.api_server))
|
||||||
# next find the metadata file
|
|
||||||
for member in members:
|
|
||||||
if self.META_MAIN in member.name:
|
|
||||||
meta_file = member
|
|
||||||
break
|
|
||||||
if not meta_file:
|
|
||||||
display.error("this role does not appear to have a meta/main.yml file.")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self._metadata = yaml.safe_load(role_tar_file.extractfile(meta_file))
|
|
||||||
except:
|
|
||||||
display.error("this role does not appear to have a valid meta/main.yml file.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# we strip off the top-level directory for all of the files contained within
|
role_versions = api.fetch_role_related('versions', role_data['id'])
|
||||||
# the tar file here, since the default is 'github_repo-target', and change it
|
if not self.version:
|
||||||
# to the specified role's name
|
# convert the version names to LooseVersion objects
|
||||||
display.display("- extracting %s to %s" % (self.name, self.path))
|
# and sort them to get the latest version. If there
|
||||||
try:
|
# are no versions in the list, we'll grab the head
|
||||||
if os.path.exists(self.path):
|
# of the master branch
|
||||||
if not os.path.isdir(self.path):
|
if len(role_versions) > 0:
|
||||||
display.error("the specified roles path exists and is not a directory.")
|
loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions]
|
||||||
return False
|
loose_versions.sort()
|
||||||
elif not getattr(self.options, "force", False):
|
self.version = str(loose_versions[-1])
|
||||||
display.error("the specified role %s appears to already exist. Use --force to replace it." % self.name)
|
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
# using --force, remove the old path
|
self.version = 'master'
|
||||||
if not self.remove():
|
elif self.version != 'master':
|
||||||
display.error("%s doesn't appear to contain a role." % self.path)
|
if role_versions and self.version not in [a.get('name', None) for a in role_versions]:
|
||||||
display.error(" please remove this directory manually if you really want to put the role here.")
|
raise AnsibleError("- the specified version (%s) of %s was not found in the list of available versions (%s)." % (self.version, self.name, role_versions))
|
||||||
return False
|
|
||||||
|
tmp_file = self.fetch(role_data)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise AnsibleError("No valid role data found")
|
||||||
|
|
||||||
|
|
||||||
|
if tmp_file:
|
||||||
|
|
||||||
|
display.display("installing from %s" % tmp_file)
|
||||||
|
|
||||||
|
if not tarfile.is_tarfile(tmp_file):
|
||||||
|
raise AnsibleError("the file downloaded was not a tar.gz")
|
||||||
|
else:
|
||||||
|
if tmp_file.endswith('.gz'):
|
||||||
|
role_tar_file = tarfile.open(tmp_file, "r:gz")
|
||||||
else:
|
else:
|
||||||
os.makedirs(self.path)
|
role_tar_file = tarfile.open(tmp_file, "r")
|
||||||
|
# verify the role's meta file
|
||||||
# now we do the actual extraction to the path
|
meta_file = None
|
||||||
|
members = role_tar_file.getmembers()
|
||||||
|
# next find the metadata file
|
||||||
for member in members:
|
for member in members:
|
||||||
# we only extract files, and remove any relative path
|
if self.META_MAIN in member.name:
|
||||||
# bits that might be in the file for security purposes
|
meta_file = member
|
||||||
# and drop the leading directory, as mentioned above
|
break
|
||||||
if member.isreg() or member.issym():
|
if not meta_file:
|
||||||
parts = member.name.split(os.sep)[1:]
|
raise AnsibleError("this role does not appear to have a meta/main.yml file.")
|
||||||
final_parts = []
|
else:
|
||||||
for part in parts:
|
try:
|
||||||
if part != '..' and '~' not in part and '$' not in part:
|
self._metadata = yaml.safe_load(role_tar_file.extractfile(meta_file))
|
||||||
final_parts.append(part)
|
except:
|
||||||
member.name = os.path.join(*final_parts)
|
raise AnsibleError("this role does not appear to have a valid meta/main.yml file.")
|
||||||
role_tar_file.extract(member, self.path)
|
|
||||||
|
|
||||||
# write out the install info file for later use
|
# we strip off the top-level directory for all of the files contained within
|
||||||
self._write_galaxy_install_info()
|
# the tar file here, since the default is 'github_repo-target', and change it
|
||||||
except OSError as e:
|
# to the specified role's name
|
||||||
display.error("Could not update files in %s: %s" % (self.path, str(e)))
|
display.display("- extracting %s to %s" % (self.name, self.path))
|
||||||
return False
|
try:
|
||||||
|
if os.path.exists(self.path):
|
||||||
|
if not os.path.isdir(self.path):
|
||||||
|
raise AnsibleError("the specified roles path exists and is not a directory.")
|
||||||
|
elif not getattr(self.options, "force", False):
|
||||||
|
raise AnsibleError("the specified role %s appears to already exist. Use --force to replace it." % self.name)
|
||||||
|
else:
|
||||||
|
# using --force, remove the old path
|
||||||
|
if not self.remove():
|
||||||
|
raise AnsibleError("%s doesn't appear to contain a role.\n please remove this directory manually if you really want to put the role here." % self.path)
|
||||||
|
else:
|
||||||
|
os.makedirs(self.path)
|
||||||
|
|
||||||
# return the parsed yaml metadata
|
# now we do the actual extraction to the path
|
||||||
display.display("- %s was installed successfully" % self.name)
|
for member in members:
|
||||||
return True
|
# we only extract files, and remove any relative path
|
||||||
|
# bits that might be in the file for security purposes
|
||||||
|
# and drop the leading directory, as mentioned above
|
||||||
|
if member.isreg() or member.issym():
|
||||||
|
parts = member.name.split(os.sep)[1:]
|
||||||
|
final_parts = []
|
||||||
|
for part in parts:
|
||||||
|
if part != '..' and '~' not in part and '$' not in part:
|
||||||
|
final_parts.append(part)
|
||||||
|
member.name = os.path.join(*final_parts)
|
||||||
|
role_tar_file.extract(member, self.path)
|
||||||
|
|
||||||
|
# write out the install info file for later use
|
||||||
|
self._write_galaxy_install_info()
|
||||||
|
except OSError as e:
|
||||||
|
raise AnsibleError("Could not update files in %s: %s" % (self.path, str(e)))
|
||||||
|
|
||||||
|
# return the parsed yaml metadata
|
||||||
|
display.display("- %s was installed successfully" % self.name)
|
||||||
|
try:
|
||||||
|
os.unlink(tmp_file)
|
||||||
|
except (OSError,IOError) as e:
|
||||||
|
display.warning("Unable to remove tmp file (%s): %s" % (tmp_file, str(e)))
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def spec(self):
|
def spec(self):
|
||||||
|
@ -266,65 +307,3 @@ class GalaxyRole(object):
|
||||||
return dict(scm=self.scm, src=self.src, version=self.version, name=self.name)
|
return dict(scm=self.scm, src=self.src, version=self.version, name=self.name)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def url_to_spec(roleurl):
|
|
||||||
# gets the role name out of a repo like
|
|
||||||
# http://git.example.com/repos/repo.git" => "repo"
|
|
||||||
|
|
||||||
if '://' not in roleurl and '@' not in roleurl:
|
|
||||||
return roleurl
|
|
||||||
trailing_path = roleurl.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 scm_archive_role(scm, role_url, role_version, role_name):
|
|
||||||
if scm not in ['hg', 'git']:
|
|
||||||
display.display("- scm %s is not currently supported" % scm)
|
|
||||||
return False
|
|
||||||
tempdir = tempfile.mkdtemp()
|
|
||||||
clone_cmd = [scm, 'clone', role_url, role_name]
|
|
||||||
with open('/dev/null', 'w') as devnull:
|
|
||||||
try:
|
|
||||||
display.display("- executing: %s" % " ".join(clone_cmd))
|
|
||||||
popen = subprocess.Popen(clone_cmd, cwd=tempdir, stdout=devnull, stderr=devnull)
|
|
||||||
except:
|
|
||||||
raise AnsibleError("error executing: %s" % " ".join(clone_cmd))
|
|
||||||
rc = popen.wait()
|
|
||||||
if rc != 0:
|
|
||||||
display.display("- command %s failed" % ' '.join(clone_cmd))
|
|
||||||
display.display(" in directory %s" % tempdir)
|
|
||||||
return False
|
|
||||||
|
|
||||||
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.tar')
|
|
||||||
if scm == 'hg':
|
|
||||||
archive_cmd = ['hg', 'archive', '--prefix', "%s/" % role_name]
|
|
||||||
if role_version:
|
|
||||||
archive_cmd.extend(['-r', role_version])
|
|
||||||
archive_cmd.append(temp_file.name)
|
|
||||||
if scm == 'git':
|
|
||||||
archive_cmd = ['git', 'archive', '--prefix=%s/' % role_name, '--output=%s' % temp_file.name]
|
|
||||||
if role_version:
|
|
||||||
archive_cmd.append(role_version)
|
|
||||||
else:
|
|
||||||
archive_cmd.append('HEAD')
|
|
||||||
|
|
||||||
with open('/dev/null', 'w') as devnull:
|
|
||||||
display.display("- executing: %s" % " ".join(archive_cmd))
|
|
||||||
popen = subprocess.Popen(archive_cmd, cwd=os.path.join(tempdir, role_name),
|
|
||||||
stderr=devnull, stdout=devnull)
|
|
||||||
rc = popen.wait()
|
|
||||||
if rc != 0:
|
|
||||||
display.display("- command %s failed" % ' '.join(archive_cmd))
|
|
||||||
display.display(" in directory %s" % tempdir)
|
|
||||||
return False
|
|
||||||
|
|
||||||
rmtree(tempdir, ignore_errors=True)
|
|
||||||
|
|
||||||
return temp_file.name
|
|
||||||
|
|
||||||
|
|
|
@ -21,19 +21,14 @@ __metaclass__ = type
|
||||||
|
|
||||||
from six import iteritems, string_types
|
from six import iteritems, string_types
|
||||||
|
|
||||||
import inspect
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from hashlib import sha1
|
|
||||||
|
|
||||||
from ansible.errors import AnsibleError, AnsibleParserError
|
from ansible.errors import AnsibleError, AnsibleParserError
|
||||||
from ansible.parsing import DataLoader
|
|
||||||
from ansible.playbook.attribute import FieldAttribute
|
from ansible.playbook.attribute import FieldAttribute
|
||||||
from ansible.playbook.base import Base
|
from ansible.playbook.base import Base
|
||||||
from ansible.playbook.become import Become
|
from ansible.playbook.become import Become
|
||||||
from ansible.playbook.conditional import Conditional
|
from ansible.playbook.conditional import Conditional
|
||||||
from ansible.playbook.helpers import load_list_of_blocks
|
from ansible.playbook.helpers import load_list_of_blocks
|
||||||
from ansible.playbook.role.include import RoleInclude
|
|
||||||
from ansible.playbook.role.metadata import RoleMetadata
|
from ansible.playbook.role.metadata import RoleMetadata
|
||||||
from ansible.playbook.taggable import Taggable
|
from ansible.playbook.taggable import Taggable
|
||||||
from ansible.plugins import get_all_plugin_loaders
|
from ansible.plugins import get_all_plugin_loaders
|
||||||
|
|
|
@ -19,11 +19,14 @@
|
||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
from six import iteritems, string_types
|
from six import string_types
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from ansible.errors import AnsibleError, AnsibleParserError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.playbook.role.definition import RoleDefinition
|
from ansible.playbook.role.definition import RoleDefinition
|
||||||
|
|
||||||
__all__ = ['RoleRequirement']
|
__all__ = ['RoleRequirement']
|
||||||
|
@ -73,7 +76,7 @@ class RoleRequirement(RoleDefinition):
|
||||||
def _preprocess_role_spec(self, ds):
|
def _preprocess_role_spec(self, ds):
|
||||||
if 'role' in ds:
|
if 'role' in ds:
|
||||||
# Old style: {role: "galaxy.role,version,name", other_vars: "here" }
|
# Old style: {role: "galaxy.role,version,name", other_vars: "here" }
|
||||||
role_info = role_spec_parse(ds['role'])
|
role_info = RoleRequirement.role_spec_parse(ds['role'])
|
||||||
if isinstance(role_info, dict):
|
if isinstance(role_info, dict):
|
||||||
# Warning: Slight change in behaviour here. name may be being
|
# Warning: Slight change in behaviour here. name may be being
|
||||||
# overloaded. Previously, name was only a parameter to the role.
|
# overloaded. Previously, name was only a parameter to the role.
|
||||||
|
@ -96,7 +99,7 @@ class RoleRequirement(RoleDefinition):
|
||||||
ds["role"] = ds["name"]
|
ds["role"] = ds["name"]
|
||||||
del ds["name"]
|
del ds["name"]
|
||||||
else:
|
else:
|
||||||
ds["role"] = repo_url_to_role_name(ds["src"])
|
ds["role"] = RoleRequirement.repo_url_to_role_name(ds["src"])
|
||||||
|
|
||||||
# set some values to a default value, if none were specified
|
# set some values to a default value, if none were specified
|
||||||
ds.setdefault('version', '')
|
ds.setdefault('version', '')
|
||||||
|
@ -104,102 +107,137 @@ class RoleRequirement(RoleDefinition):
|
||||||
|
|
||||||
return ds
|
return ds
|
||||||
|
|
||||||
def repo_url_to_role_name(repo_url):
|
@staticmethod
|
||||||
# gets the role name out of a repo like
|
def repo_url_to_role_name(repo_url):
|
||||||
# http://git.example.com/repos/repo.git" => "repo"
|
# 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:
|
if '://' not in repo_url and '@' not in repo_url:
|
||||||
return repo_url
|
return repo_url
|
||||||
trailing_path = repo_url.split('/')[-1]
|
trailing_path = repo_url.split('/')[-1]
|
||||||
if trailing_path.endswith('.git'):
|
if trailing_path.endswith('.git'):
|
||||||
trailing_path = trailing_path[:-4]
|
trailing_path = trailing_path[:-4]
|
||||||
if trailing_path.endswith('.tar.gz'):
|
if trailing_path.endswith('.tar.gz'):
|
||||||
trailing_path = trailing_path[:-7]
|
trailing_path = trailing_path[:-7]
|
||||||
if ',' in trailing_path:
|
if ',' in trailing_path:
|
||||||
trailing_path = trailing_path.split(',')[0]
|
trailing_path = trailing_path.split(',')[0]
|
||||||
return trailing_path
|
return trailing_path
|
||||||
|
|
||||||
def role_spec_parse(role_spec):
|
@staticmethod
|
||||||
# takes a repo and a version like
|
def role_spec_parse(role_spec):
|
||||||
# git+http://git.example.com/repos/repo.git,v1.0
|
# takes a repo and a version like
|
||||||
# and returns a list of properties such as:
|
# git+http://git.example.com/repos/repo.git,v1.0
|
||||||
# {
|
# and returns a list of properties such as:
|
||||||
# 'scm': 'git',
|
# {
|
||||||
# 'src': 'http://git.example.com/repos/repo.git',
|
# 'scm': 'git',
|
||||||
# 'version': 'v1.0',
|
# 'src': 'http://git.example.com/repos/repo.git',
|
||||||
# 'name': 'repo'
|
# 'version': 'v1.0',
|
||||||
# }
|
# 'name': 'repo'
|
||||||
|
# }
|
||||||
|
|
||||||
default_role_versions = dict(git='master', hg='tip')
|
default_role_versions = dict(git='master', hg='tip')
|
||||||
|
|
||||||
role_spec = role_spec.strip()
|
role_spec = role_spec.strip()
|
||||||
role_version = ''
|
role_version = ''
|
||||||
if role_spec == "" or role_spec.startswith("#"):
|
if role_spec == "" or role_spec.startswith("#"):
|
||||||
return (None, None, None, None)
|
return (None, None, None, None)
|
||||||
|
|
||||||
tokens = [s.strip() for s in role_spec.split(',')]
|
tokens = [s.strip() for s in role_spec.split(',')]
|
||||||
|
|
||||||
# assume https://github.com URLs are git+https:// URLs and not
|
# assume https://github.com URLs are git+https:// URLs and not
|
||||||
# tarballs unless they end in '.zip'
|
# tarballs unless they end in '.zip'
|
||||||
if 'github.com/' in tokens[0] and not tokens[0].startswith("git+") and not tokens[0].endswith('.tar.gz'):
|
if 'github.com/' in tokens[0] and not tokens[0].startswith("git+") and not tokens[0].endswith('.tar.gz'):
|
||||||
tokens[0] = 'git+' + tokens[0]
|
tokens[0] = 'git+' + tokens[0]
|
||||||
|
|
||||||
if '+' in tokens[0]:
|
if '+' in tokens[0]:
|
||||||
(scm, role_url) = tokens[0].split('+')
|
(scm, role_url) = tokens[0].split('+')
|
||||||
else:
|
|
||||||
scm = None
|
|
||||||
role_url = tokens[0]
|
|
||||||
|
|
||||||
if len(tokens) >= 2:
|
|
||||||
role_version = tokens[1]
|
|
||||||
|
|
||||||
if len(tokens) == 3:
|
|
||||||
role_name = tokens[2]
|
|
||||||
else:
|
|
||||||
role_name = repo_url_to_role_name(tokens[0])
|
|
||||||
|
|
||||||
if scm and not role_version:
|
|
||||||
role_version = default_role_versions.get(scm, '')
|
|
||||||
|
|
||||||
return dict(scm=scm, src=role_url, version=role_version, role_name=role_name)
|
|
||||||
|
|
||||||
# FIXME: all of these methods need to be cleaned up/reorganized below this
|
|
||||||
def get_opt(options, k, defval=""):
|
|
||||||
"""
|
|
||||||
Returns an option from an Optparse values instance.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
data = getattr(options, k)
|
|
||||||
except:
|
|
||||||
return defval
|
|
||||||
if k == "roles_path":
|
|
||||||
if os.pathsep in data:
|
|
||||||
data = data.split(os.pathsep)[0]
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_role_path(role_name, options):
|
|
||||||
"""
|
|
||||||
Returns the role path based on the roles_path option
|
|
||||||
and the role name.
|
|
||||||
"""
|
|
||||||
roles_path = get_opt(options,'roles_path')
|
|
||||||
roles_path = os.path.join(roles_path, role_name)
|
|
||||||
roles_path = os.path.expanduser(roles_path)
|
|
||||||
return roles_path
|
|
||||||
|
|
||||||
def get_role_metadata(role_name, options):
|
|
||||||
"""
|
|
||||||
Returns the metadata as YAML, if the file 'meta/main.yml'
|
|
||||||
exists in the specified role_path
|
|
||||||
"""
|
|
||||||
role_path = os.path.join(get_role_path(role_name, options), 'meta/main.yml')
|
|
||||||
try:
|
|
||||||
if os.path.isfile(role_path):
|
|
||||||
f = open(role_path, 'r')
|
|
||||||
meta_data = yaml.safe_load(f)
|
|
||||||
f.close()
|
|
||||||
return meta_data
|
|
||||||
else:
|
else:
|
||||||
return None
|
scm = None
|
||||||
except:
|
role_url = tokens[0]
|
||||||
return None
|
|
||||||
|
if len(tokens) >= 2:
|
||||||
|
role_version = tokens[1]
|
||||||
|
|
||||||
|
if len(tokens) == 3:
|
||||||
|
role_name = tokens[2]
|
||||||
|
else:
|
||||||
|
role_name = RoleRequirement.repo_url_to_role_name(tokens[0])
|
||||||
|
|
||||||
|
if scm and not role_version:
|
||||||
|
role_version = default_role_versions.get(scm, '')
|
||||||
|
|
||||||
|
return dict(scm=scm, src=role_url, version=role_version, name=role_name)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def role_yaml_parse(role):
|
||||||
|
|
||||||
|
if 'role' in role:
|
||||||
|
# Old style: {role: "galaxy.role,version,name", other_vars: "here" }
|
||||||
|
role_info = RoleRequirement.role_spec_parse(role['role'])
|
||||||
|
if isinstance(role_info, dict):
|
||||||
|
# Warning: Slight change in behaviour here. name may be being
|
||||||
|
# overloaded. Previously, name was only a parameter to the role.
|
||||||
|
# Now it is both a parameter to the role and the name that
|
||||||
|
# ansible-galaxy will install under on the local system.
|
||||||
|
if 'name' in role and 'name' in role_info:
|
||||||
|
del role_info['name']
|
||||||
|
role.update(role_info)
|
||||||
|
else:
|
||||||
|
# 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
|
||||||
|
|
||||||
|
return role
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def scm_archive_role(src, scm='git', name=None, version='HEAD'):
|
||||||
|
if scm not in ['hg', 'git']:
|
||||||
|
raise AnsibleError("- scm %s is not currently supported" % scm)
|
||||||
|
tempdir = tempfile.mkdtemp()
|
||||||
|
clone_cmd = [scm, 'clone', src, name]
|
||||||
|
with open('/dev/null', 'w') as devnull:
|
||||||
|
try:
|
||||||
|
popen = subprocess.Popen(clone_cmd, cwd=tempdir, stdout=devnull, stderr=devnull)
|
||||||
|
except:
|
||||||
|
raise AnsibleError("error executing: %s" % " ".join(clone_cmd))
|
||||||
|
rc = popen.wait()
|
||||||
|
if rc != 0:
|
||||||
|
raise AnsibleError ("- command %s failed in directory %s" % (' '.join(clone_cmd), tempdir))
|
||||||
|
|
||||||
|
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.tar')
|
||||||
|
if scm == 'hg':
|
||||||
|
archive_cmd = ['hg', 'archive', '--prefix', "%s/" % name]
|
||||||
|
if version:
|
||||||
|
archive_cmd.extend(['-r', version])
|
||||||
|
archive_cmd.append(temp_file.name)
|
||||||
|
if scm == 'git':
|
||||||
|
archive_cmd = ['git', 'archive', '--prefix=%s/' % name, '--output=%s' % temp_file.name]
|
||||||
|
if version:
|
||||||
|
archive_cmd.append(version)
|
||||||
|
else:
|
||||||
|
archive_cmd.append('HEAD')
|
||||||
|
|
||||||
|
with open('/dev/null', 'w') as devnull:
|
||||||
|
popen = subprocess.Popen(archive_cmd, cwd=os.path.join(tempdir, name),
|
||||||
|
stderr=devnull, stdout=devnull)
|
||||||
|
rc = popen.wait()
|
||||||
|
if rc != 0:
|
||||||
|
raise AnsibleError("- command %s failed in directory %s" % (' '.join(archive_cmd), tempdir))
|
||||||
|
|
||||||
|
shutil.rmtree(tempdir, ignore_errors=True)
|
||||||
|
return temp_file.name
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ def get_docstring(filename, verbose=False):
|
||||||
theid = t.id
|
theid = t.id
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
# skip errors can happen when trying to use the normal code
|
# skip errors can happen when trying to use the normal code
|
||||||
|
display.warning("Failed to assign id for %t on %s, skipping" % (t, filename))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if 'DOCUMENTATION' in theid:
|
if 'DOCUMENTATION' in theid:
|
||||||
|
@ -119,6 +120,7 @@ def get_docstring(filename, verbose=False):
|
||||||
except:
|
except:
|
||||||
display.error("unable to parse %s" % filename)
|
display.error("unable to parse %s" % filename)
|
||||||
if verbose == True:
|
if verbose == True:
|
||||||
|
display.display("unable to parse %s" % filename)
|
||||||
raise
|
raise
|
||||||
return doc, plainexamples, returndocs
|
return doc, plainexamples, returndocs
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ test_galaxy: test_galaxy_spec test_galaxy_yaml
|
||||||
|
|
||||||
test_galaxy_spec:
|
test_galaxy_spec:
|
||||||
mytmpdir=$(MYTMPDIR) ; \
|
mytmpdir=$(MYTMPDIR) ; \
|
||||||
ansible-galaxy install -r galaxy_rolesfile -p $$mytmpdir/roles ; \
|
ansible-galaxy install -r galaxy_rolesfile -p $$mytmpdir/roles -vvvv ; \
|
||||||
cp galaxy_playbook.yml $$mytmpdir ; \
|
cp galaxy_playbook.yml $$mytmpdir ; \
|
||||||
ansible-playbook -i $(INVENTORY) $$mytmpdir/galaxy_playbook.yml -v $(TEST_FLAGS) ; \
|
ansible-playbook -i $(INVENTORY) $$mytmpdir/galaxy_playbook.yml -v $(TEST_FLAGS) ; \
|
||||||
RC=$$? ; \
|
RC=$$? ; \
|
||||||
|
@ -181,7 +181,7 @@ test_galaxy_spec:
|
||||||
|
|
||||||
test_galaxy_yaml:
|
test_galaxy_yaml:
|
||||||
mytmpdir=$(MYTMPDIR) ; \
|
mytmpdir=$(MYTMPDIR) ; \
|
||||||
ansible-galaxy install -r galaxy_roles.yml -p $$mytmpdir/roles ; \
|
ansible-galaxy install -r galaxy_roles.yml -p $$mytmpdir/roles -vvvv; \
|
||||||
cp galaxy_playbook.yml $$mytmpdir ; \
|
cp galaxy_playbook.yml $$mytmpdir ; \
|
||||||
ansible-playbook -i $(INVENTORY) $$mytmpdir/galaxy_playbook.yml -v $(TEST_FLAGS) ; \
|
ansible-playbook -i $(INVENTORY) $$mytmpdir/galaxy_playbook.yml -v $(TEST_FLAGS) ; \
|
||||||
RC=$$? ; \
|
RC=$$? ; \
|
||||||
|
|
Loading…
Reference in a new issue