Esempio n. 1
0
def indent_checker(filename):
    with codecs.open(filename, mode='rb', encoding='utf-8') as f:
        indent_regex = re.compile(r"^(?P<indent>\s*(?:- )?)(?P<rest>.*)$")
        verb_regex = re.compile(".*: [|>]\d?$")
        lineno = 0
        prev_indent = ''
        verbatim = False
        errors = []
        for line in f:
            lineno += 1
            match = indent_regex.match(line)
            if verb_regex.match(line):
                verbatim = True
            if len(match.group('rest')) == 0:
                if verbatim:
                    verbatim = False
                continue
            if verbatim:
                continue
            curr_indent = match.group('indent')
            offset = len(curr_indent) - len(prev_indent)
            if offset > 0 and offset != 2:
                if match.group('indent').endswith('- '):
                    errors.append(
                        Error(
                            lineno,
                            "lines starting with '- ' should have same "
                            "or less indentation than previous line"))
                else:
                    errors.append(
                        Error(lineno,
                              "indentation should increase by 2 chars"))
            prev_indent = curr_indent
        return errors
Esempio n. 2
0
def same_variable_defined_in_competing_groups(candidate, options):
    result = Result(candidate.path)
    # assume that group_vars file is under an inventory *directory*
    invfile = os.path.dirname(os.path.dirname(candidate.path))
    global _inv

    try:
        if ANSIBLE > 1:
            loader = ansible.parsing.dataloader.DataLoader()
            try:
                from ansible.inventory.manager import InventoryManager
                inv = _inv or InventoryManager(loader=loader, sources=invfile)
            except ImportError:
                var_manager = VariableManager()
                inv = _inv or ansible.inventory.Inventory(loader=loader,
                                                          variable_manager=var_manager,
                                                          host_list=invfile)
            _inv = inv
        else:
            inv = _inv or ansible.inventory.Inventory(invfile)
            _inv = inv
    except AnsibleError as e:
        result.errors = [Error(None, "Inventory is broken: %s" % e.message)]
        return result

    if hasattr(inv, 'groups'):
        group = inv.groups.get(os.path.basename(candidate.path))
    else:
        group = inv.get_group(os.path.basename(candidate.path))
    if not group:
        # group file exists in group_vars but no related group
        # in inventory directory
        return result
    remove_inherited_and_overridden_group_vars(group, inv)
    group_vars = set(_vars[group].keys())
    child_hosts = group.hosts
    child_groups = group.child_groups
    siblings = set()

    for child_host in child_hosts:
        siblings.update(child_host.groups)
    for child_group in child_groups:
        siblings.update(child_group.parent_groups)
    for sibling in siblings:
        if sibling != group:
            remove_inherited_and_overridden_group_vars(sibling, inv)
            sibling_vars = set(_vars[sibling].keys())
            common_vars = sibling_vars & group_vars
            common_hosts = [host.name for host in set(child_hosts) & set(sibling.hosts)]
            if common_vars and common_hosts:
                for var in common_vars:
                    error_msg_template = "Sibling groups {0} and {1} with common hosts {2} " + \
                                         "both define variable {3}"
                    error_msg = error_msg_template.format(group.name, sibling.name,
                                                          ", ".join(common_hosts), var)
                    result.errors.append(Error(None, error_msg))

    return result
Esempio n. 3
0
def same_variable_defined_in_competing_groups(candidate, options):
    result = Result(candidate.path)

    vaultpass = get_vault_password(options)
    # assume that group_vars file is under an inventory *directory*
    sdirs = candidate.path.split(os.sep)
    if sdirs.index('group_vars') == 0:
        invfile = os.getcwd()
    else:
        invfile = os.path.join(*sdirs[:sdirs.index('group_vars')])
    grpname = os.path.splitext(sdirs[sdirs.index('group_vars') + 1])[0]
    global _inv

    try:
        inv = _inv or parse_inventory(invfile)
    except AnsibleError as e:
        result.errors = [Error(None, "Inventory is broken: %s" % e.message)]
        return result

    if hasattr(inv, 'groups'):
        group = inv.groups.get(grpname)
    else:
        group = inv.get_group(grpname)
    if not group:
        # group file exists in group_vars but no related group
        # in inventory directory
        return result
    remove_inherited_and_overridden_group_vars(group, inv, invfile, vaultpass)
    group_vars = set(_vars[group].keys())
    child_hosts = group.hosts
    child_groups = group.child_groups
    siblings = set()

    for child_host in child_hosts:
        siblings.update(child_host.groups)
    for child_group in child_groups:
        siblings.update(child_group.parent_groups)
    for sibling in siblings:
        if sibling != group:
            remove_inherited_and_overridden_group_vars(sibling, inv, invfile,
                                                       vaultpass)
            sibling_vars = set(_vars[sibling].keys())
            common_vars = sibling_vars & group_vars
            common_hosts = [
                host.name for host in set(child_hosts) & set(sibling.hosts)
            ]
            if common_vars and common_hosts:
                for var in common_vars:
                    error_msg_template = "Sibling groups {0} and {1} with common hosts {2} " + \
                                         "both define variable {3}"
                    error_msg = error_msg_template.format(
                        group.name, sibling.name, ", ".join(common_hosts), var)
                    result.errors.append(Error(None, error_msg))

    return result
Esempio n. 4
0
def rolesfile_contains_scm_in_src(candidate, settings):
    result = Result(candidate.path)
    if candidate.path.endswith(".yml") and os.path.exists(candidate.path):
        try:
            with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
                roles = parse_yaml_linenumbers(f.read(), candidate.path)
            for role in roles:
                if '+' in role.get('src'):
                    error = Error(role['__line__'], "Use scm key rather "
                                  "than src: scm+url")
                    result.errors.append(error)
        except Exception as e:
            result.errors = [Error(None, "Cannot parse YAML from %s: %s" %
                                   (candidate.path, str(e)))]
    return result
Esempio n. 5
0
def files_should_have_actual_content(candidate, settings):
    errors = []
    with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
        content = yaml.safe_load(f.read())
    if not content:
        errors = [Error(None, "%s appears to have no useful content" % candidate)]
    return Result(candidate.path, errors)
Esempio n. 6
0
def parse(candidate, options):
    result = Result(candidate.path)
    try:
        parse_inventory(candidate.path)
    except Exception as e:
        result.errors = [Error(None, "Inventory is broken: %s" % e.message)]
    return result
Esempio n. 7
0
def yaml_form_rather_than_key_value(candidate, settings):
    with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
        content = parse_yaml_linenumbers(f.read(), candidate.path)
    errors = []
    if content:
        fileinfo = dict(type=candidate.filetype, path=candidate.path)
        for task in get_action_tasks(content, fileinfo):
            normal_form = normalize_task(task, candidate.path)
            action = normal_form['action']['__ansible_module__']
            arguments = normal_form['action']['__ansible_arguments__']
            # Cope with `set_fact` where task['set_fact'] is None
            if not task.get(action):
                continue
            if isinstance(task[action], dict):
                continue
            # allow skipping based on tag e.g. if using splatting
            # https://docs.ansible.com/ansible/devel/reference_appendices\
            # /faq.html#argsplat-unsafe
            if 'skip_ansible_lint' in (task.get('tags') or []):
                continue
            # strip additional newlines off task[action]
            if task[action].strip().split() != arguments:
                errors.append(Error(task['__line__'], "Task arguments appear "
                                    "to be in key value rather "
                                    "than YAML format"))
    return Result(candidate.path, errors)
Esempio n. 8
0
def yamlrolesfile(candidate, settings):
    rolesfile = os.path.join(os.path.dirname(candidate.path), "rolesfile")
    result = Result(candidate)
    if os.path.exists(rolesfile) and not os.path.exists(rolesfile + ".yml"):
        result.errors = [Error(None, "Rolesfile %s does not "
                                     "have a .yml extension" % rolesfile)]
        return result
    rolesfile = os.path.join(os.path.dirname(candidate.path), "rolesfile.yml")
    if os.path.exists(rolesfile):
        with codecs.open(rolesfile, mode='rb', encoding='utf-8') as f:
            try:
                yaml.safe_load(f)
            except Exception as e:
                result.errors = [Error(None, "Cannot parse YAML from %s: %s" %
                                       (rolesfile, str(e)))]
    return result
Esempio n. 9
0
def metamain(candidate, settings):
    try:
        fh = codecs.open(candidate.path, mode='rb', encoding='utf-8')
    except IOError, e:
        result = Result(candidate)
        result.errors = [
            Error(None, "Could not open %s: %s" % (candidate.path, e))
        ]
Esempio n. 10
0
def code_passes_pycodestyle(candidate, options):
    result = utils.execute(["pycodestyle", candidate.path])
    errors = []
    if result.rc:
        for line in result.output.strip().split('\n'):
            lineno = int(line.split(':')[1])
            errors.append(Error(lineno, line))
    return Result(candidate.path, errors)
Esempio n. 11
0
def no_vars_in_host_file(candidate, options):
    errors = []
    with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
        try:
            yaml.safe_load(f)
        except Exception:
            for (lineno, line) in enumerate(f):
                if ':vars]' in line:
                    errors.append(
                        Error(lineno + 1, "contains a vars definition"))
    return Result(candidate.path, errors)
Esempio n. 12
0
def parse(candidate, options):
    result = Result(candidate.path)
    try:
        if ANSIBLE > 1:
            loader = ansible.parsing.dataloader.DataLoader()
            var_manager = ansible.vars.VariableManager()
            ansible.inventory.Inventory(loader=loader,
                                        variable_manager=var_manager,
                                        host_list=candidate.path)
        else:
            ansible.inventory.Inventory(candidate.path)
    except Exception, e:
        result.errors = [Error(None, "Inventory is broken: %s" % e.message)]
Esempio n. 13
0
def repeated_names(playbook, settings):
    with codecs.open(playbook['path'], mode='rb', encoding='utf-8') as f:
        yaml = parse_yaml_linenumbers(f, playbook['path'])
    namelines = defaultdict(list)
    errors = []
    if yaml:
        for task in get_action_tasks(yaml, playbook):
            if 'name' in task:
                namelines[task['name']].append(task['__line__'])
        for (name, lines) in namelines.items():
            if len(lines) > 1:
                errors.append(Error(lines[-1],
                                    "Task/handler name %s appears multiple times" % name))
    return Result(playbook, errors)
Esempio n. 14
0
def same_variable_defined_in_competing_groups(candidate, options):
    result = Result(candidate.path)
    # assume that group_vars file is under an inventory *directory*
    invfile = os.path.dirname(os.path.dirname(candidate.path))
    global _inv

    try:
        if ANSIBLE > 1:
            loader = ansible.parsing.dataloader.DataLoader()
            var_manager = ansible.vars.VariableManager()
            inv = _inv or ansible.inventory.Inventory(
                loader=loader, variable_manager=var_manager, host_list=invfile)
            _inv = inv
        else:
            inv = _inv or ansible.inventory.Inventory(invfile)
            _inv = inv
    except AnsibleError, e:
        result.errors = [Error(None, "Inventory is broken: %s" % e.message)]
        return result
Esempio n. 15
0
def playbook_contains_logic(candidate, settings):
    errors = []
    with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
        plays = parse_yaml_linenumbers(f.read(), candidate.path)
    for logic in ['tasks', 'pre_tasks', 'post_tasks', 'vars', 'handlers']:
        for play in plays:
            if logic in play:
                if isinstance(play[logic], list):
                    firstitemline = play[logic][0]['__line__'] - 1
                elif isinstance(play[logic], dict):
                    firstitemline = play[logic]['__line__']
                else:
                    continue
                # we can only access line number of first thing in the section
                # so we guess the section starts on the line above.
                errors.append(
                    Error(firstitemline,
                          "%s should not be required in a play" % logic))

    return Result(candidate.path, errors)
Esempio n. 16
0
def yaml_form_rather_than_key_value(candidate, settings):
    with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
        content = parse_yaml_linenumbers(f.read(), candidate.path)
    errors = []
    if content:
        fileinfo = dict(type=candidate.filetype, path=candidate.path)
        for task in get_action_tasks(content, fileinfo):
            normal_form = normalize_task(task, candidate.path)
            action = normal_form['action']['__ansible_module__']
            arguments = normal_form['action']['__ansible_arguments__']
            # Cope with `set_fact` where task['set_fact'] is None
            if not task.get(action):
                continue
            if isinstance(task[action], dict):
                continue
            # strip additional newlines off task[action]
            if task[action].strip().split() != arguments:
                errors.append(
                    Error(
                        task['__line__'], "Task arguments appear "
                        "to be in key value rather "
                        "than YAML format"))
    return Result(candidate.path, errors)
Esempio n. 17
0
def yaml_form_rather_than_key_value(candidate, settings):
    with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
        content = parse_yaml_linenumbers(f.read(), candidate.path)
    errors = []
    if content:
        fileinfo = dict(type=candidate.filetype, path=candidate.path)
        for task in get_action_tasks(content, fileinfo):
            normal_form = normalize_task(task, candidate.path)
            action = normal_form['action']['__ansible_module__']
            arguments = normal_form['action']['__ansible_arguments__']
            # FIXME: This is a bug - perhaps when connection is local
            # or similar
            if action not in task:
                continue
            if isinstance(task[action], dict):
                continue
            if task[action] != ' '.join(arguments):
                errors.append(
                    Error(
                        task['__line__'], "Task arguments appear "
                        "to be in key value rather "
                        "than YAML format"))
    return Result(candidate.path, errors)
Esempio n. 18
0
def host_vars_exist(candidate, settings):
    return Result(candidate.path,
                  [Error(None, "Host vars are generally "
                         "not required")])
Esempio n. 19
0
def repeated_vars(candidate, settings):
    with codecs.open(candidate.realpath, 'r') as f:
        errors = hunt_repeated_yaml_keys(f) or dict()
    return Result(candidate, [Error(err_line, "Variable %s occurs more than once" % err_key)
                              for err_key in errors for err_line in errors[err_key]])
Esempio n. 20
0
def host_vars_exist(candidate, settings):
    """Group variables are preferred over host variables."""
    return Result(candidate.path,
                  [Error(None, "Host vars are generally "
                         "not required")])
Esempio n. 21
0
    try:
        fh = codecs.open(candidate.path, mode='rb', encoding='utf-8')
    except IOError, e:
        result = Result(candidate)
        result.errors = [
            Error(None, "Could not open %s: %s" % (candidate.path, e))
        ]
    try:
        result = Result(candidate)
        data = yaml.safe_load(fh)
        if 'dependencies' in data:
            if data["dependencies"] == []:
                return result
            else:
                result.errors = [
                    Error(None, "Role dependencies are "
                          "not empty")
                ]
        else:
            result.errors = [
                Error(
                    None, "Role meta/main.yml does "
                    "not contain a dependencies section")
            ]
    except Exception, e:
        result.errors = [
            Error(None, "Could not parse in %s: %s" % (candidate.path, e))
        ]
    finally:
        fh.close()
    return result
Esempio n. 22
0
def repeated_vars(candidate, settings):
    with codecs.open(candidate.path, 'r') as text:
        errors = hunt_repeated_yaml_keys(text) or dict()
    return Result([Error(errors[err], "Variable %s occurs more than once" % err) for
                   err in errors])
Esempio n. 23
0
def check_fail(candidate, settings):
    return Result(candidate, [Error(1, "test failed")])
Esempio n. 24
0
        # group file exists in group_vars but no related group
        # in inventory directory
        return result
    remove_inherited_and_overridden_group_vars(group, inv)
    group_vars = set(_vars[group].keys())
    child_hosts = group.hosts
    child_groups = group.child_groups
    siblings = set()

    for child_host in child_hosts:
        siblings.update(child_host.groups)
    for child_group in child_groups:
        siblings.update(child_group.parent_groups)
    for sibling in siblings:
        if sibling != group:
            remove_inherited_and_overridden_group_vars(sibling, inv)
            sibling_vars = set(_vars[sibling].keys())
            common_vars = sibling_vars & group_vars
            common_hosts = [
                host.name for host in set(child_hosts) & set(sibling.hosts)
            ]
            if common_vars and common_hosts:
                for var in common_vars:
                    error_msg_template = "Sibling groups {0} and {1} with common hosts {2} " + \
                                         "both define variable {3}"
                    error_msg = error_msg_template.format(
                        group.name, sibling.name, ", ".join(common_hosts), var)
                    result.errors.append(Error(None, error_msg))

    return result