Skip to content

Commit

Permalink
Avoid exception with YAML files which are just strings
Browse files Browse the repository at this point in the history
Fixes: #1082
  • Loading branch information
ssbarnea committed Dec 26, 2020
1 parent c7c8a51 commit cfd5a95
Show file tree
Hide file tree
Showing 9 changed files with 41 additions and 19 deletions.
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ per-file-ignores =
lib/ansiblelint/__main__.py: C901
lib/ansiblelint/cli.py: D101 D102 D103
lib/ansiblelint/formatters/__init__.py: D101 D102
lib/ansiblelint/utils.py: D103
lib/ansiblelint/utils.py: D103 C901
lib/ansiblelint/rules/*.py: D100 D101 D102
lib/ansiblelint/rules/__init__.py: D100 D101 D102 C901

# FIXME: drop these once they're fixed
# Ref: https://github.com/ansible-community/ansible-lint/issues/725
Expand Down
1 change: 1 addition & 0 deletions a.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo
3 changes: 3 additions & 0 deletions examples/plain_string.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
foo
# This file is valid YAML but from our point of view is an error, as is
# neither a Sequence or a Mapping.
11 changes: 11 additions & 0 deletions lib/ansiblelint/_internal/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ class AnsibleParserErrorRule(BaseRule):
severity = 'VERY_HIGH'
tags = ['core']
version_added = 'v5.0.0'


class LoadingFailureRule(BaseRule):
"""File loading failure."""

id = '901'
shortdesc = 'Failed to load or parse file'
description = 'Linter failed to process a YAML file, possible not an Ansible file.'
severity = 'VERY_HIGH'
tags = ['core']
version_added = 'v4.3.0'
14 changes: 0 additions & 14 deletions lib/ansiblelint/rules/LoadingFailureRule.py

This file was deleted.

12 changes: 10 additions & 2 deletions lib/ansiblelint/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from typing import List

import ansiblelint.utils
from ansiblelint._internal.rules import AnsibleParserErrorRule
from ansiblelint._internal.rules import AnsibleParserErrorRule, LoadingFailureRule
from ansiblelint.errors import BaseRule, MatchError, RuntimeErrorRule
from ansiblelint.skip_utils import append_skipped_rules, get_rule_skips_from_line

Expand Down Expand Up @@ -129,6 +129,14 @@ def matchyaml(self, file: dict, text: str) -> List[MatchError]:
return matches

yaml = ansiblelint.utils.parse_yaml_linenumbers(text, 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 'get' not in dir(yaml):
return [MatchError(
filename=file['path'],
rule=LoadingFailureRule()
)]
if not yaml:
return matches

Expand Down Expand Up @@ -190,7 +198,7 @@ def __init__(self, rulesdirs=None) -> None:
# internal rules included in order to expose them for docs as they are
# not directly loaded by our rule loader.
self.rules.extend(
[RuntimeErrorRule(), AnsibleParserErrorRule()])
[RuntimeErrorRule(), AnsibleParserErrorRule(), LoadingFailureRule()])
for rulesdir in self.rulesdirs:
_logger.debug("Loading rules from %s", rulesdir)
self.extend(load_plugins(rulesdir))
Expand Down
2 changes: 1 addition & 1 deletion lib/ansiblelint/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import ansiblelint.file_utils
import ansiblelint.skip_utils
import ansiblelint.utils
from ansiblelint._internal.rules import LoadingFailureRule
from ansiblelint.errors import MatchError
from ansiblelint.rules.LoadingFailureRule import LoadingFailureRule

if TYPE_CHECKING:
from ansiblelint.rules import RulesCollection
Expand Down
7 changes: 6 additions & 1 deletion lib/ansiblelint/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
from yaml.composer import Composer
from yaml.representer import RepresenterError

from ansiblelint._internal.rules import AnsibleParserErrorRule
from ansiblelint._internal.rules import AnsibleParserErrorRule, LoadingFailureRule
from ansiblelint.constants import CUSTOM_RULESDIR_ENVVAR, DEFAULT_RULESDIR, FileType
from ansiblelint.errors import MatchError
from ansiblelint.file_utils import normpath
Expand Down Expand Up @@ -187,6 +187,11 @@ def find_children(playbook: Tuple[str, str], playbook_dir: str) -> List:
raise SystemExit(str(e))
results = []
basedir = os.path.dirname(playbook[0])
# playbook_ds can be an AnsibleUnicode string, which we consider invalid
if "get" not in playbook_ds:
raise MatchError(
filename=playbook[0],
rule=LoadingFailureRule)
items = _playbook_items(playbook_ds)
for item in items:
for child in _rebind_match_filename(playbook[0], play_children)(
Expand Down
7 changes: 7 additions & 0 deletions test/TestExamples.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@ def test_example(default_rules_collection):
"""example.yml is expected to have 15 match errors inside."""
result = Runner(default_rules_collection, 'examples/example.yml', [], [], []).run()
assert len(result) == 15


def test_example_plain_string(default_rules_collection):
"""Validates that loading valid YAML string produce error."""
result = Runner(default_rules_collection, 'examples/plain_string.yml', [], [], []).run()
assert len(result) == 1
assert "Failed to load or parse file" in result[0]

0 comments on commit cfd5a95

Please sign in to comment.