Esempio n. 1
0
def _validate_task_handler_action_for_role(th_action: dict) -> None:
    """Verify that the task handler action is valid for role include."""
    module = th_action['__ansible_module__']

    if 'name' not in th_action:
        raise MatchError(message=f"Failed to find required 'name' key in {module!s}")

    if not isinstance(th_action['name'], str):
        raise MatchError(
            message=f"Value assigned to 'name' key on '{module!s}' is not a string.",
        )
Esempio n. 2
0
 def create_matcherror(self,
                       message: Optional[str] = None,
                       linenumber: int = 0,
                       details: str = "",
                       filename: Optional[Union[str, Lintable]] = None,
                       tag: str = "") -> MatchError:
     match = MatchError(message=message,
                        linenumber=linenumber,
                        details=details,
                        filename=filename,
                        rule=copy.copy(self))
     if tag:
         match.tag = tag
     return match
def test_matcherror_compare_with_dummy_sentinel(operation, expected_value):
    """Check that MatchError comparison runs other types fallbacks."""
    dummy_obj = DummySentinelTestObject()
    # NOTE: This assertion abuses the CPython property to cache short string
    # NOTE: objects because the identity check is more presice and we don't
    # NOTE: want extra operator protocol methods to influence the test.
    assert operation(MatchError("foo"), dummy_obj) is expected_value
Esempio n. 4
0
def _taskshandlers_children(basedir, k, v, parent_type):
    results = []
    for th in v:
        if 'include' in th:
            append_children(th['include'], basedir, k, parent_type, results)
        elif 'include_tasks' in th:
            append_children(th['include_tasks'], basedir, k, parent_type, results)
        elif 'import_playbook' in th:
            append_children(th['import_playbook'], basedir, k, parent_type, results)
        elif 'import_tasks' in th:
            append_children(th['import_tasks'], basedir, k, parent_type, results)
        elif 'include_role' in th or 'import_role' in th:
            th = normalize_task_v2(th)
            module = th['action']['__ansible_module__']
            if "name" not in th['action']:
                raise MatchError(
                    "Failed to find required 'name' key in %s" % module)
            if not isinstance(th['action']["name"], str):
                raise RuntimeError(
                    "Value assigned to 'name' key on '%s' is not a string." %
                    module)
            results.extend(_roles_children(basedir, k, [th['action'].get("name")],
                                           parent_type,
                                           main=th['action'].get('tasks_from', 'main')))
        elif 'block' in th:
            results.extend(_taskshandlers_children(basedir, k, th['block'], parent_type))
            if 'rescue' in th:
                results.extend(_taskshandlers_children(basedir, k, th['rescue'], parent_type))
            if 'always' in th:
                results.extend(_taskshandlers_children(basedir, k, th['always'], parent_type))
    return results
Esempio n. 5
0
    def matchlines(self, file, text) -> List[MatchError]:
        matches: List[MatchError] = []
        if not self.match:
            return matches
        # arrays are 0-based, line numbers are 1-based
        # so use prev_line_no as the counter
        for (prev_line_no, line) in enumerate(text.split("\n")):
            if line.lstrip().startswith('#'):
                continue

            rule_id_list = get_rule_skips_from_line(line)
            if self.id in rule_id_list:
                continue

            result = self.match(file, line)
            if not result:
                continue
            message = None
            if isinstance(result, str):
                message = result
            m = MatchError(message=message,
                           linenumber=prev_line_no + 1,
                           details=line,
                           filename=file['path'],
                           rule=self)
            matches.append(m)
        return matches
Esempio n. 6
0
def test_matcherror_invalid():
    """Ensure that MatchError requires message or rule."""
    expected_err = (
        r"^MatchError\(\) missing a required argument: one of 'message' or 'rule'$"
    )
    with pytest.raises(TypeError, match=expected_err):
        MatchError()
def test_matcherror_compare_with_other_fallback(
    other,
    operation,
    expected_value,
):
    """Check that MatchError comparison runs other types fallbacks."""
    assert operation(MatchError("foo"), other) is expected_value
Esempio n. 8
0
 def test_format_coloured_string(self):
     match = MatchError(message="message",
                        linenumber=1,
                        details="hello",
                        filename="filename.yml",
                        rule=self.rule)
     self.formatter.format(match)
Esempio n. 9
0
    def run(
        self, file: Lintable, tags: Set[str] = set(), skip_list: List[str] = []
    ) -> List[MatchError]:
        matches: List[MatchError] = list()

        if not file.path.is_dir():
            try:
                if file.content is not None:  # loads the file content
                    pass
            except IOError as e:
                return [
                    MatchError(
                        message=str(e),
                        filename=file,
                        rule=LoadingFailureRule(),
                        tag=e.__class__.__name__.lower(),
                    )
                ]

        for rule in self.rules:
            if (
                not tags
                or rule.has_dynamic_tags
                or not set(rule.tags).union([rule.id]).isdisjoint(tags)
            ):
                rule_definition = set(rule.tags)
                rule_definition.add(rule.id)
                if set(rule_definition).isdisjoint(skip_list):
                    matches.extend(rule.getmatches(file))

        # some rules can produce matches with tags that are inside our
        # skip_list, so we need to cleanse the matches
        matches = [m for m in matches if m.tag not in skip_list]

        return matches
Esempio n. 10
0
def test_matcherror_compare_with_other_fallback(
    other: object,
    operation: Callable[..., bool],
    expected_value: bool,
) -> None:
    """Check that MatchError comparison runs other types fallbacks."""
    assert operation(MatchError("foo"), other) is expected_value
Esempio n. 11
0
 def test_unicode_format_string(self):
     match = MatchError(message=u'\U0001f427',
                        linenumber=1,
                        details="hello",
                        filename="filename.yml",
                        rule=self.rule)
     self.formatter.format(match)
Esempio n. 12
0
    def matchyaml(self, file: Lintable) -> List[MatchError]:
        matches: List[MatchError] = []
        if not self.matchplay or str(file.base_kind) != 'text/yaml':
            return matches

        yaml = ansiblelint.utils.parse_yaml_linenumbers(file)
        # yaml returned can be an AnsibleUnicode (a string) when the yaml
        # file contains a single string. YAML spec allows this but we consider
        # this an fatal error.
        if isinstance(yaml, str):
            if yaml.startswith('$ANSIBLE_VAULT'):
                return []
            return [MatchError(filename=str(file.path), rule=LoadingFailureRule())]
        if not yaml:
            return matches

        if isinstance(yaml, dict):
            yaml = [yaml]

        yaml = ansiblelint.skip_utils.append_skipped_rules(yaml, file)

        for play in yaml:

            # Bug #849
            if play is None:
                continue

            if self.id in play.get('skipped_rules', ()):
                continue

            matches.extend(self.matchplay(file, play))

        return matches
Esempio n. 13
0
def normalize_task_v2(task: dict) -> dict:  # noqa: C901
    """Ensure tasks have an action key and strings are converted to python objects."""
    result = dict()
    if 'always_run' in task:
        # FIXME(ssbarnea): Delayed import to avoid circular import
        # See https://github.com/ansible/ansible-lint/issues/880
        # noqa: # pylint:disable=cyclic-import,import-outside-toplevel
        from ansiblelint.rules.AlwaysRunRule import AlwaysRunRule

        raise MatchError(rule=AlwaysRunRule,
                         filename=task[FILENAME_KEY],
                         linenumber=task[LINE_NUMBER_KEY])

    sanitized_task = _sanitize_task(task)
    mod_arg_parser = ModuleArgsParser(sanitized_task)
    try:
        action, arguments, result['delegate_to'] = mod_arg_parser.parse()
    except AnsibleParserError as e:
        try:
            task_info = "%s:%s" % (task[FILENAME_KEY], task[LINE_NUMBER_KEY])
        except KeyError:
            task_info = "Unknown"
        pp = pprint.PrettyPrinter(indent=2)
        task_pprint = pp.pformat(sanitized_task)

        _logger.critical("Couldn't parse task at %s (%s)\n%s", task_info,
                         e.message, task_pprint)
        raise SystemExit(ANSIBLE_FAILURE_RC)

    # denormalize shell -> command conversion
    if '_uses_shell' in arguments:
        action = 'shell'
        del arguments['_uses_shell']

    for (k, v) in list(task.items()):
        if k in ('action', 'local_action', 'args',
                 'delegate_to') or k == action:
            # we don't want to re-assign these values, which were
            # determined by the ModuleArgsParser() above
            continue
        else:
            result[k] = v

    result['action'] = dict(__ansible_module__=action)

    if '_raw_params' in arguments:
        result['action']['__ansible_arguments__'] = arguments[
            '_raw_params'].split(' ')
        del arguments['_raw_params']
    else:
        result['action']['__ansible_arguments__'] = list()

    if 'argv' in arguments and not result['action']['__ansible_arguments__']:
        result['action']['__ansible_arguments__'] = arguments['argv']
        del arguments['argv']

    result['action'].update(arguments)
    return result
Esempio n. 14
0
    def _get_ansible_syntax_check_matches(lintable: Lintable) -> List[MatchError]:
        """Run ansible syntax check and return a list of MatchError(s)."""
        if lintable.kind != 'playbook':
            return []

        with timed_info("Executing syntax check on %s", lintable.path):
            cmd = ['ansible-playbook', '--syntax-check', str(lintable.path)]
            run = subprocess.run(
                cmd,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                shell=False,  # needed when command is a list
                universal_newlines=True,
                check=False
            )
            result = []
        if run.returncode != 0:
            message = None
            filename = str(lintable.path)
            linenumber = 0
            column = None

            stderr = strip_ansi_escape(run.stderr)
            stdout = strip_ansi_escape(run.stdout)
            if stderr:
                details = stderr
                if stdout:
                    details += "\n" + stdout
            else:
                details = stdout

            m = _ansible_syntax_check_re.search(stderr)
            if m:
                message = m.groupdict()['title']
                # Ansible returns absolute paths
                filename = m.groupdict()['filename']
                linenumber = int(m.groupdict()['line'])
                column = int(m.groupdict()['column'])

            if run.returncode == 4:
                rule: BaseRule = AnsibleSyntaxCheckRule()
            else:
                rule = RuntimeErrorRule()
                if not message:
                    message = (
                        f"Unexpected error code {run.returncode} from "
                        f"execution of: {' '.join(cmd)}")

            result.append(MatchError(
                message=message,
                filename=filename,
                linenumber=linenumber,
                column=column,
                rule=rule,
                details=details
                ))
        return result
Esempio n. 15
0
 def test_dict_format_line(self) -> None:
     match = MatchError(
         message="xyz",
         linenumber=1,
         details={'hello': 'world'},  # type: ignore
         filename="filename.yml",
         rule=self.rule,
     )
     self.formatter.format(match)
Esempio n. 16
0
 def matchlines(self, file, text):
     matches = []
     # arrays are 0-based, line numbers are 1-based
     # so use prev_line_no as the counter
     for (prev_line_no, line) in enumerate(text.split("\n")):
         if issuspicious(line):
             matche = MatchError(line, prev_line_no + 1, self.shortdesc, file['path'], self)
             matches.append(matche)
     return matches
Esempio n. 17
0
 def test_dict_format_line(self):
     match = MatchError(
         "xyz",
         1,
         {'hello': 'world'},
         "filename.yml",
         self.rule,
     )
     self.formatter.format(match, True)
def test_matcherror_compare_no_other_fallback(other, operation, operator_char):
    """Check that MatchError comparison with other types causes TypeError."""
    expected_error = (r'^('
                      r'unsupported operand type\(s\) for {operator!s}:|'
                      r"'{operator!s}' not supported between instances of"
                      r") 'MatchError' and '{other_type!s}'$".format(
                          other_type=type(other).__name__,
                          operator=operator_char))
    with pytest.raises(TypeError, match=expected_error):
        operation(MatchError("foo"), other)
Esempio n. 19
0
def _taskshandlers_children(
    basedir: str, k: str, v: Union[None, Any], parent_type: FileType
) -> List[Lintable]:
    results: List[Lintable] = []
    if v is None:
        raise MatchError(
            message="A malformed block was encountered while loading a block.",
            rule=RuntimeErrorRule(),
        )
    for th in v:

        # ignore empty tasks, `-`
        if not th:
            continue

        with contextlib.suppress(LookupError):
            children = _get_task_handler_children_for_tasks_or_playbooks(
                th,
                basedir,
                k,
                parent_type,
            )
            results.append(children)
            continue

        if (
            'include_role' in th or 'import_role' in th
        ):  # lgtm [py/unreachable-statement]
            th = normalize_task_v2(th)
            _validate_task_handler_action_for_role(th['action'])
            results.extend(
                _roles_children(
                    basedir,
                    k,
                    [th['action'].get("name")],
                    parent_type,
                    main=th['action'].get('tasks_from', 'main'),
                )
            )
            continue

        if 'block' not in th:
            continue

        results.extend(_taskshandlers_children(basedir, k, th['block'], parent_type))
        if 'rescue' in th:
            results.extend(
                _taskshandlers_children(basedir, k, th['rescue'], parent_type)
            )
        if 'always' in th:
            results.extend(
                _taskshandlers_children(basedir, k, th['always'], parent_type)
            )

    return results
Esempio n. 20
0
def normalize_task_v2(task: Dict[str, Any]) -> Dict[str, Any]:
    """Ensure tasks have a normalized action key and strings are converted to python objects."""
    result = dict()

    sanitized_task = _sanitize_task(task)
    mod_arg_parser = ModuleArgsParser(sanitized_task)
    try:
        action, arguments, result['delegate_to'] = mod_arg_parser.parse(
            skip_action_validation=options.skip_action_validation)
    except AnsibleParserError as e:
        raise MatchError(
            rule=AnsibleParserErrorRule(),
            message=e.message,
            filename=task.get(FILENAME_KEY, "Unknown"),
            linenumber=task.get(LINE_NUMBER_KEY, 0),
        )

    # denormalize shell -> command conversion
    if '_uses_shell' in arguments:
        action = 'shell'
        del arguments['_uses_shell']

    for (k, v) in list(task.items()):
        if k in ('action', 'local_action', 'args',
                 'delegate_to') or k == action:
            # we don't want to re-assign these values, which were
            # determined by the ModuleArgsParser() above
            continue
        result[k] = v

    if not isinstance(action, str):
        raise RuntimeError("Task actions can only be strings, got %s" % action)
    action_unnormalized = action
    # convert builtin fqn calls to short forms because most rules know only
    # about short calls but in the future we may switch the normalization to do
    # the opposite. Mainly we currently consider normalized the module listing
    # used by `ansible-doc -t module -l 2>/dev/null`
    action = removeprefix(action, "ansible.builtin.")
    result['action'] = dict(__ansible_module__=action,
                            __ansible_module_original__=action_unnormalized)

    if '_raw_params' in arguments:
        result['action']['__ansible_arguments__'] = arguments[
            '_raw_params'].split(' ')
        del arguments['_raw_params']
    else:
        result['action']['__ansible_arguments__'] = list()

    if 'argv' in arguments and not result['action']['__ansible_arguments__']:
        result['action']['__ansible_arguments__'] = arguments['argv']
        del arguments['argv']

    result['action'].update(arguments)
    return result
Esempio n. 21
0
 def create_matcherror(
         self,
         message: str = None,
         linenumber: int = 0,
         details: str = "",
         filename: str = None) -> MatchError:
     return MatchError(
         message=message,
         linenumber=linenumber,
         details=details,
         filename=filename,
         rule=self
         )
Esempio n. 22
0
    def matchyaml(self, file: Lintable) -> List[MatchError]:
        matches: List[MatchError] = []
        if not self.matchplay:
            return matches

        yaml = ansiblelint.utils.parse_yaml_linenumbers(
            file.content, file.path)
        # yaml returned can be an AnsibleUnicode (a string) when the yaml
        # file contains a single string. YAML spec allows this but we consider
        # this an fatal error.
        if isinstance(yaml, str):
            return [MatchError(filename=file.path, rule=LoadingFailureRule())]
        if not yaml:
            return matches

        if isinstance(yaml, dict):
            yaml = [yaml]

        yaml = ansiblelint.skip_utils.append_skipped_rules(
            yaml, file.content, file.kind)

        for play in yaml:

            # Bug #849
            if play is None:
                continue

            if self.id in play.get('skipped_rules', ()):
                continue

            result = self.matchplay(file, play)
            if not result:
                continue

            if isinstance(result, tuple):
                result = [result]

            if not isinstance(result, list):
                raise TypeError("{} is not a list".format(result))

            for section, message, *optional_linenumber in result:
                linenumber = self._matchplay_linenumber(
                    play, optional_linenumber)
                matches.append(
                    self.create_matcherror(message=message,
                                           linenumber=linenumber,
                                           details=str(section),
                                           filename=file.path))
        return matches
 def setup_class(self) -> None:
     """Set up few MatchError objects."""
     self.rule = AnsibleLintRule()
     self.rule.id = "TCF0001"
     self.rule.severity = "VERY_HIGH"
     self.matches = []
     self.matches.append(
         MatchError(
             message="message",
             linenumber=1,
             details="hello",
             filename="filename.yml",
             rule=self.rule,
         ))
     self.matches.append(
         MatchError(
             message="message",
             linenumber=2,
             details="hello",
             filename="filename.yml",
             rule=self.rule,
         ))
     self.formatter = CodeclimateJSONFormatter(pathlib.Path.cwd(),
                                               display_relative_path=True)
Esempio n. 24
0
def normalize_task_v2(task: dict) -> dict:  # noqa: C901
    """Ensure tasks have an action key and strings are converted to python objects."""
    result = dict()

    sanitized_task = _sanitize_task(task)
    mod_arg_parser = ModuleArgsParser(sanitized_task)
    try:
        action, arguments, result['delegate_to'] = mod_arg_parser.parse()
    except AnsibleParserError as e:
        raise MatchError(
            rule=AnsibleParserErrorRule(),
            message=e.message,
            filename=task.get(FILENAME_KEY, "Unknown"),
            linenumber=task.get(LINE_NUMBER_KEY, 0),
        )

    # denormalize shell -> command conversion
    if '_uses_shell' in arguments:
        action = 'shell'
        del arguments['_uses_shell']

    for (k, v) in list(task.items()):
        if k in ('action', 'local_action', 'args',
                 'delegate_to') or k == action:
            # we don't want to re-assign these values, which were
            # determined by the ModuleArgsParser() above
            continue
        else:
            result[k] = v

    result['action'] = dict(__ansible_module__=action)

    if '_raw_params' in arguments:
        result['action']['__ansible_arguments__'] = arguments[
            '_raw_params'].split(' ')
        del arguments['_raw_params']
    else:
        result['action']['__ansible_arguments__'] = list()

    if 'argv' in arguments and not result['action']['__ansible_arguments__']:
        result['action']['__ansible_arguments__'] = arguments['argv']
        del arguments['argv']

    result['action'].update(arguments)
    return result
Esempio n. 25
0
 def _emit_matches(self, files: List[Lintable]) -> Generator[MatchError, None, None]:
     visited: Set[Lintable] = set()
     while visited != self.lintables:
         for lintable in self.lintables - visited:
             try:
                 for child in ansiblelint.utils.find_children(lintable):
                     if self.is_excluded(str(child.path)):
                         continue
                     self.lintables.add(child)
                     files.append(child)
             except MatchError as e:
                 e.rule = LoadingFailureRule()
                 yield e
             except AttributeError:
                 yield MatchError(
                     filename=str(lintable.path), rule=LoadingFailureRule()
                 )
             visited.add(lintable)
Esempio n. 26
0
def find_children(lintable: Lintable) -> List[Lintable]:  # noqa: C901
    if not lintable.path.exists():
        return []
    playbook_dir = str(lintable.path.parent)
    _set_collections_basedir(playbook_dir or os.path.abspath('.'))
    add_all_plugin_dirs(playbook_dir or '.')
    if lintable.kind == 'role':
        playbook_ds = AnsibleMapping({'roles': [{'role': str(lintable.path)}]})
    elif lintable.kind not in ("playbook", "tasks"):
        return []
    else:
        try:
            playbook_ds = parse_yaml_from_file(str(lintable.path))
        except AnsibleError as e:
            raise SystemExit(str(e))
    results = []
    basedir = os.path.dirname(str(lintable.path))
    # playbook_ds can be an AnsibleUnicode string, which we consider invalid
    if isinstance(playbook_ds, str):
        raise MatchError(filename=str(lintable.path),
                         rule=LoadingFailureRule())
    for item in _playbook_items(playbook_ds):
        # if lintable.kind not in ["playbook"]:
        #     continue
        for child in play_children(basedir, item, lintable.kind, playbook_dir):
            # We avoid processing parametrized children
            path_str = str(child.path)
            if "$" in path_str or "{{" in path_str:
                continue

            # Repair incorrect paths obtained when old syntax was used, like:
            # - include: simpletask.yml tags=nginx
            valid_tokens = list()
            for token in split_args(path_str):
                if '=' in token:
                    break
                valid_tokens.append(token)
            path = ' '.join(valid_tokens)
            if path != path_str:
                child.path = Path(path)
                child.name = child.path.name

            results.append(child)
    return results
Esempio n. 27
0
    def run(self, playbookfile, tags=set(), skip_list=frozenset()) -> List:
        text = ""
        matches: List = list()
        error: Optional[IOError] = None

        for i in range(3):
            try:
                with open(playbookfile['path'], mode='r',
                          encoding='utf-8') as f:
                    text = f.read()
                break
            except IOError as e:
                _logger.warning("Couldn't open %s - %s [try:%s]",
                                playbookfile['path'], e.strerror, i)
                error = e
                sleep(1)
                continue
        else:
            return [
                MatchError(message=str(error),
                           filename=playbookfile['path'],
                           rule=LoadingFailureRule())
            ]

        for rule in self.rules:
            if not tags or not set(rule.tags).union([rule.id
                                                     ]).isdisjoint(tags):
                rule_definition = set(rule.tags)
                rule_definition.add(rule.id)
                if set(rule_definition).isdisjoint(skip_list):
                    matches.extend(rule.matchlines(playbookfile, text))
                    matches.extend(rule.matchtasks(playbookfile, text))
                    matches.extend(
                        rule.matchyaml(
                            Lintable(playbookfile['path'],
                                     content=text,
                                     kind=playbookfile['type'])))

        # some rules can produce matches with tags that are inside our
        # skip_list, so we need to cleanse the matches
        matches = [m for m in matches if m.tag not in skip_list]

        return matches
Esempio n. 28
0
    def matchtasks(self, file: str,
                   text: str) -> List[MatchError]:  # noqa: C901
        matches: List[MatchError] = []
        if not self.matchtask:
            return matches

        if file['type'] == 'meta':
            return matches

        yaml = ansiblelint.utils.parse_yaml_linenumbers(text, file['path'])
        if not yaml:
            return matches

        yaml = append_skipped_rules(yaml, text, file['type'])

        try:
            tasks = ansiblelint.utils.get_normalized_tasks(yaml, file)
        except MatchError as e:
            return [e]

        for task in tasks:
            if self.id in task.get('skipped_rules', ()):
                continue

            if 'action' not in task:
                continue
            result = self.matchtask(file, task)
            if not result:
                continue

            message = None
            if isinstance(result, str):
                message = result
            task_msg = "Task/Handler: " + ansiblelint.utils.task_to_str(task)
            m = MatchError(message=message,
                           linenumber=task[ansiblelint.utils.LINE_NUMBER_KEY],
                           details=task_msg,
                           filename=file['path'],
                           rule=self)
            matches.append(m)
        return matches
Esempio n. 29
0
def find_children(playbook: Tuple[str, str],
                  playbook_dir: str) -> List[Lintable]:
    if not os.path.exists(playbook[0]):
        return []
    _set_collections_basedir(playbook_dir or os.path.abspath('.'))
    add_all_plugin_dirs(playbook_dir or '.')
    if playbook[1] == 'role':
        playbook_ds = {'roles': [{'role': playbook[0]}]}
    else:
        try:
            playbook_ds = parse_yaml_from_file(playbook[0])
        except AnsibleError as e:
            raise SystemExit(str(e))
    results = []
    basedir = os.path.dirname(playbook[0])
    # playbook_ds can be an AnsibleUnicode string, which we consider invalid
    if isinstance(playbook_ds, str):
        raise MatchError(filename=playbook[0], rule=LoadingFailureRule)
    items = _playbook_items(playbook_ds)
    for item in items:
        for child in play_children(basedir, item, playbook[1], playbook_dir):
            # We avoid processing parametrized children
            path_str = str(child.path)
            if "$" in path_str or "{{" in path_str:
                continue

            # Repair incorrect paths obtained when old syntax was used, like:
            # - include: simpletask.yml tags=nginx
            valid_tokens = list()
            for token in split_args(path_str):
                if '=' in token:
                    break
                valid_tokens.append(token)
            path = ' '.join(valid_tokens)
            if path != path_str:
                child.path = Path(path)
                child.name = child.path.name

            results.append(child)
    return results
Esempio n. 30
0
    def matchyaml(self, file: str, text: str) -> List[MatchError]:
        matches: List[MatchError] = []
        if not self.matchplay:
            return matches

        yaml = ansiblelint.utils.parse_yaml_linenumbers(text, file['path'])
        if not yaml:
            return matches

        if isinstance(yaml, dict):
            yaml = [yaml]

        yaml = ansiblelint.skip_utils.append_skipped_rules(
            yaml, text, file['type'])

        for play in yaml:
            if self.id in play.get('skipped_rules', ()):
                continue

            result = self.matchplay(file, play)
            if not result:
                continue

            if isinstance(result, tuple):
                result = [result]

            if not isinstance(result, list):
                raise TypeError("{} is not a list".format(result))

            for section, message, *optional_linenumber in result:
                linenumber = self._matchplay_linenumber(
                    play, optional_linenumber)
                m = MatchError(message=message,
                               linenumber=linenumber,
                               details=str(section),
                               filename=file['path'],
                               rule=self)
                matches.append(m)
        return matches