def test_galaxy_cli_error_with_message(): msg = "there was an error in the cli" galaxy_cli_error = cli_exceptions.GalaxyCliError(msg) assert isinstance(galaxy_cli_error, exceptions.GalaxyError) with pytest.raises(cli_exceptions.GalaxyCliError, match=msg) as exc_info: raise cli_exceptions.GalaxyCliError(msg) log.debug("exc_info: %s", exc_info)
def execute_setup(self): """ Setup an integration from Github or Travis for Ansible Galaxy roles""" if self.options.setup_list: # List existing integration secrets secrets = self.api.list_secrets() if len(secrets) == 0: # None found self.display("No integrations found.") return 0 self.display(u'\n' + "ID Source Repo", color=runtime.COLOR_OK) self.display("---------- ---------- ----------", color=runtime.COLOR_OK) for secret in secrets: self.display("%-10s %-10s %s/%s" % (secret['id'], secret['source'], secret['github_user'], secret['github_repo']), color=runtime.COLOR_OK) return 0 if self.options.remove_id: # Remove a secret self.api.remove_secret(self.options.remove_id) self.display("Secret removed. Integrations using this secret will not longer work.", color=runtime.COLOR_OK) return 0 if len(self.args) < 4: raise cli_exceptions.GalaxyCliError("Missing one or more arguments. Expecting: source github_user github_repo secret") secret = self.args.pop() github_repo = self.args.pop() github_user = self.args.pop() source = self.args.pop() resp = self.api.add_secret(source, github_user, github_repo, secret) self.display("Added integration for %s %s/%s" % (resp['source'], resp['github_user'], resp['github_repo'])) return 0
def exit_without_ignore(self, rc=1): """ Exits with the specified return code unless the option --ignore-errors was specified """ if not self.options.ignore_errors: raise cli_exceptions.GalaxyCliError( '- you can use --ignore-errors to skip failed roles and finish processing the list.' )
def execute_import(self): """ used to import a role into Ansible Galaxy """ # FIXME/TODO(alikins): replace with logging or display callback colors = { 'INFO': 'normal', 'WARNING': runtime.COLOR_WARN, 'ERROR': runtime.COLOR_ERROR, 'SUCCESS': runtime.COLOR_OK, 'FAILED': runtime.COLOR_ERROR, 'DEBUG': runtime.COLOR_DEBUG, } if len(self.args) < 2: raise cli_exceptions.GalaxyCliError("Expected a github_username and github_repository. Use --help.") github_repo = to_text(self.args.pop(), errors='surrogate_or_strict') github_user = to_text(self.args.pop(), errors='surrogate_or_strict') if self.options.check_status: task = self.api.get_import_task(github_user=github_user, github_repo=github_repo) else: # Submit an import request task = self.api.create_import_task(github_user, github_repo, reference=self.options.reference, role_name=self.options.role_name) if len(task) > 1: # found multiple roles associated with github_user/github_repo self.display("WARNING: More than one Galaxy role associated with Github repo %s/%s." % (github_user, github_repo), color='yellow') self.display("The following Galaxy roles are being updated:" + u'\n', color=runtime.COLOR_CHANGED) for t in task: self.display('%s.%s' % (t['summary_fields']['role']['namespace'], t['summary_fields']['role']['name']), color=runtime.COLOR_CHANGED) self.display(u'\nTo properly namespace this role, remove each of the above and re-import %s/%s from scratch' % (github_user, github_repo), color=runtime.COLOR_CHANGED) return 0 # found a single role as expected self.display("Successfully submitted import request %d" % task[0]['id']) if not self.options.wait: self.display("Role name: %s" % task[0]['summary_fields']['role']['name']) self.display("Repo: %s/%s" % (task[0]['github_user'], task[0]['github_repo'])) if self.options.check_status or self.options.wait: # Get the status of the import msg_list = [] finished = False while not finished: task = self.api.get_import_task(task_id=task[0]['id']) for msg in task[0]['summary_fields']['task_messages']: if msg['id'] not in msg_list: self.display(msg['message_text'], color=colors[msg['message_type']]) msg_list.append(msg['id']) if task[0]['state'] in ['SUCCESS', 'FAILED']: finished = True else: time.sleep(10) return 0
def exit_without_ignore(ignore_errors, msg=None, rc=1): """ Exits with the specified return code unless the option --ignore-errors was specified """ ignore_error_blurb = '- you can use --ignore-errors to skip failed content items and finish processing the list.' if not ignore_errors: message = ignore_error_blurb if msg: message = '%s:\n%s' % (msg, ignore_error_blurb) raise cli_exceptions.GalaxyCliError(message)
def execute_search(self): ''' searches for roles on the Ansible Galaxy server''' page_size = 1000 search = None if len(self.args): terms = [] for i in range(len(self.args)): terms.append(self.args.pop()) search = '+'.join(terms[::-1]) if not search and not self.options.platforms and not self.options.galaxy_tags and not self.options.author: raise cli_exceptions.GalaxyCliError( "Invalid query. At least one search term, platform, galaxy tag or author must be provided." ) response = self.api.search_roles(search, platforms=self.options.platforms, tags=self.options.galaxy_tags, author=self.options.author, page_size=page_size) if response['count'] == 0: self.display("No roles match your search.") return True data = [u''] if response['count'] > page_size: data.append( u"Found %d roles matching your search. Showing first %s." % (response['count'], page_size)) else: data.append(u"Found %d roles matching your search:" % response['count']) max_len = [] for role in response['results']: max_len.append(len(role['username'] + '.' + role['name'])) name_len = max(max_len) format_str = u" %%-%ds %%s" % name_len data.append(u'') data.append(format_str % (u"Name", u"Description")) data.append(format_str % (u"----", u"-----------")) for role in response['results']: data.append( format_str % (u'%s.%s' % (role['username'], role['name']), role['description'])) data = u'\n'.join(data) self.display(data) return True
def remove_repository(installed_repository, display_callback=None): log.debug('looking for repository %s to remove', installed_repository) log.debug('repository to remove: %s %s', installed_repository, type(installed_repository)) try: res = repository.remove(installed_repository) if res: display_callback('- successfully removed %s' % installed_repository.label) else: display_callback('- %s is not installed, skipping.' % installed_repository.label) except Exception as e: log.exception(e) raise cli_exceptions.GalaxyCliError( "Failed to remove installed repository %s: %s" % (installed_repository.label, str(e)))
def execute_delete(self): """ Delete a role from Ansible Galaxy. """ if len(self.args) < 2: raise cli_exceptions.GalaxyCliError("Missing one or more arguments. Expected: github_user github_repo") github_repo = self.args.pop() github_user = self.args.pop() resp = self.api.delete_role(github_user, github_repo) if len(resp['deleted_roles']) > 1: self.display("Deleted the following roles:") self.display("ID User Name") self.display("------ --------------- ----------") for role in resp['deleted_roles']: self.display("%-8s %-15s %s" % (role.id, role.namespace, role.name)) self.display(resp['status']) return True
def execute_remove(self): """ removes the list of roles passed as arguments from the local system. """ if len(self.args) == 0: raise cli_exceptions.CliOptionsError( '- you must specify at least one role to remove.') for role_name in self.args: role = GalaxyContent(self.galaxy, role_name) try: if role.remove(): self.display('- successfully removed %s' % role_name) else: self.display('- %s is not installed, skipping.' % role_name) except Exception as e: raise cli_exceptions.GalaxyCliError( "Failed to remove role %s: %s" % (role_name, str(e))) return 0
def test_galaxy_cli_error(): galaxy_cli_error = cli_exceptions.GalaxyCliError() assert isinstance(galaxy_cli_error, exceptions.GalaxyError)
def execute_install(self): """ uses the args list of roles to be installed, unless -f was specified. The list of roles can be a name (which will be downloaded via the galaxy API and github), or it can be a local .tar.gz file. """ role_file = self.options.role_file if len(self.args) == 0 and role_file is None: # the user needs to specify one of either --role-file or specify a single user/role name raise cli_exceptions.CliOptionsError( "- you must specify a user/role name or a roles file") no_deps = self.options.no_deps force = self.options.force roles_left = [] if role_file: try: f = open(role_file, 'r') if role_file.endswith('.yaml') or role_file.endswith('.yml'): try: required_roles = yaml.safe_load(f.read()) except Exception as e: raise cli_exceptions.GalaxyCliError( "Unable to load data from the requirements file: %s" % role_file) if required_roles is None: raise cli_exceptions.GalaxyCliError( "No roles found in file: %s" % role_file) for role in required_roles: if "include" not in role: role = GalaxyContent.yaml_parse(role) log.info("found role %s in yaml file", str(role)) if "name" not in role and "scm" not in role: raise cli_exceptions.GalaxyCliError( "Must specify name or src for role") roles_left.append( GalaxyContent(self.galaxy, **role)) else: with open(role["include"]) as f_include: try: roles_left += [ GalaxyContent(self.galaxy, **r) for r in ( GalaxyContent.yaml_parse(i) for i in yaml.safe_load(f_include)) ] except Exception as e: msg = "Unable to load data from the include requirements file: %s %s" raise cli_exceptions.GalaxyCliError( msg % (role_file, e)) else: log.warn( "DEPRECATED going forward only the yaml format will be supported (version='%s')", "2.6") # roles listed in a file, one per line for rline in f.readlines(): if rline.startswith("#") or rline.strip() == '': continue log.debug('found role %s in text file', str(rline)) role = GalaxyContent.yaml_parse(rline.strip()) roles_left.append(GalaxyContent(self.galaxy, **role)) f.close() except (IOError, OSError) as e: raise cli_exceptions.GalaxyCliError('Unable to open %s: %s' % (role_file, str(e))) else: # roles were specified directly, so we'll just go out grab them # (and their dependencies, unless the user doesn't want us to). for rname in self.args: role = GalaxyContent.yaml_parse(rname.strip()) roles_left.append(GalaxyContent(self.galaxy, **role)) for role in roles_left: # only process roles in roles files when names matches if given if role_file and self.args and role.name not in self.args: log.info('Skipping role %s', role.name) continue log.info('Processing role %s ', role.name) # query the galaxy API for the role data if role.install_info is not None: if role.install_info['version'] != role.version or force: if force: self.display( '- changing role %s from %s to %s' % role.name, role.install_info['version'], role.version or "unspecified") role.remove() else: log.warn( '- %s (%s) is already installed - use --force to change version to %s', role.name, role.install_info['version'], role.version or "unspecified") continue else: if not force: self.display('- %s is already installed, skipping.' % str(role)) continue try: installed = role.install() except exceptions.GalaxyError as e: self.log.exception(e) log.warn("- %s was NOT installed successfully: %s ", role.name, str(e)) self.exit_without_ignore() continue # install dependencies, if we want them if not no_deps and installed: if not role.metadata: log.warn("Meta file %s is empty. Skipping dependencies.", role.path) else: role_dependencies = role.metadata.get('dependencies') or [] for dep in role_dependencies: log.debug('Installing dep %s', dep) dep_info = GalaxyContent.yaml_parse(dep) dep_role = GalaxyContent(self.galaxy, **dep_info) if '.' not in dep_role.name and '.' not in dep_role.src and dep_role.scm is None: # we know we can skip this, as it's not going to # be found on galaxy.ansible.com continue if dep_role.install_info is None: if dep_role not in roles_left: self.display('- adding dependency: %s' % str(dep_role)) roles_left.append(dep_role) else: self.display( '- dependency %s already pending installation.' % dep_role.name) else: if dep_role.install_info[ 'version'] != dep_role.version: log.warning( '- dependency %s from role %s differs from already installed version (%s), skipping' % str(dep_role), role.name, dep_role.install_info['version']) else: self.display( '- dependency %s is already installed, skipping.' % dep_role.name) if not installed: log.warning("- %s was NOT installed successfully.", role.name) self.exit_without_ignore() return 0
def execute_init(self): """ creates the skeleton framework of a role that complies with the galaxy metadata format. """ init_path = self.options.init_path force = self.options.force role_skeleton_path = self.options.role_skeleton role_name = self.args.pop(0).strip() if self.args else None if not role_name: raise cli_exceptions.CliOptionsError( "- no role name specified for init") role_path = os.path.join(init_path, role_name) if os.path.exists(role_path): if os.path.isfile(role_path): raise cli_exceptions.GalaxyCliError( "- the path %s already exists, but is a file - aborting" % role_path) elif not force: raise cli_exceptions.GalaxyCliError( "- the directory %s already exists." "you can use --force to re-initialize this directory,\n" "however it will reset any main.yml files that may have\n" "been modified there already." % role_path) # FIXME(akl): role_skeleton stuff should probably be a module or two and a few classes instead of inline here # role_skeleton ends mostly being a list of file paths to copy inject_data = dict( role_name=role_name, author='your name', description='your description', company='your company (optional)', license='license (GPLv2, CC-BY, etc)', issue_tracker_url='http://example.com/issue/tracker', min_ansible_version='1.2', role_type=self.options.role_type) import pprint self.log.debug('inject_data: %s', pprint.pformat(inject_data)) # create role directory if not os.path.exists(role_path): os.makedirs(role_path) if role_skeleton_path is not None: skeleton_ignore_expressions = runtime.GALAXY_ROLE_SKELETON_IGNORE else: this_dir, this_filename = os.path.split(__file__) type_path = getattr(self.options, 'role_type', "default") role_skeleton_path = os.path.join(this_dir, '../', 'data/role_skelton', type_path) self.log.debug('role_skeleton_path: %s', role_skeleton_path) skeleton_ignore_expressions = ['^.*/.git_keep$'] role_skeleton = os.path.expanduser(role_skeleton_path) self.log.debug('role_skeleton: %s', role_skeleton) skeleton_ignore_re = [ re.compile(x) for x in skeleton_ignore_expressions ] template_env = Environment(loader=FileSystemLoader(role_skeleton)) # TODO: mv elsewhere, this is main role install logic for root, dirs, files in os.walk(role_skeleton, topdown=True): rel_root = os.path.relpath(root, role_skeleton) in_templates_dir = rel_root.split(os.sep, 1)[0] == 'templates' dirs[:] = [ d for d in dirs if not any(r.match(d) for r in skeleton_ignore_re) ] for f in files: filename, ext = os.path.splitext(f) if any( r.match(os.path.join(rel_root, f)) for r in skeleton_ignore_re): continue elif ext == ".j2" and not in_templates_dir: src_template = os.path.join(rel_root, f) dest_file = os.path.join(role_path, rel_root, filename) template_env.get_template(src_template).stream( inject_data).dump(dest_file) else: f_rel_path = os.path.relpath(os.path.join(root, f), role_skeleton) shutil.copyfile(os.path.join(root, f), os.path.join(role_path, f_rel_path)) for d in dirs: dir_path = os.path.join(role_path, rel_root, d) if not os.path.exists(dir_path): os.makedirs(dir_path) self.display("- %s was created successfully" % role_name)