From f8ec1275ad82f4b88f047a677104bccd27590974 Mon Sep 17 00:00:00 2001 From: Conner Crosby Date: Fri, 6 Sep 2024 08:16:01 -0400 Subject: [PATCH] Handle bare exception case from nested jinja2 vars (#4298) --- .github/workflows/tox.yml | 2 +- examples/playbooks/jinja-nested-vars.yml | 11 +++++++ src/ansiblelint/rules/jinja.py | 42 +++++++++++++++++++++--- 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 examples/playbooks/jinja-nested-vars.yml diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 8366aa2c40..566317d52c 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -72,7 +72,7 @@ jobs: env: # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 891 + PYTEST_REQPASS: 892 steps: - uses: actions/checkout@v4 with: diff --git a/examples/playbooks/jinja-nested-vars.yml b/examples/playbooks/jinja-nested-vars.yml new file mode 100644 index 0000000000..c6a0640cf6 --- /dev/null +++ b/examples/playbooks/jinja-nested-vars.yml @@ -0,0 +1,11 @@ +--- +- name: Test + gather_facts: false + hosts: + - localhost + tasks: + - name: Test + ansible.builtin.debug: + msg: "{{ cron_hour_raw }}" + vars: + cron_hour_raw: "{{ 12 | random(seed=inventory_hostname) }}" diff --git a/src/ansiblelint/rules/jinja.py b/src/ansiblelint/rules/jinja.py index ff124a8af6..b83cd89f06 100644 --- a/src/ansiblelint/rules/jinja.py +++ b/src/ansiblelint/rules/jinja.py @@ -23,7 +23,11 @@ from ansiblelint.runner import get_matches from ansiblelint.skip_utils import get_rule_skips_from_line from ansiblelint.text import has_jinja -from ansiblelint.utils import parse_yaml_from_file, template +from ansiblelint.utils import ( # type: ignore[attr-defined] + Templar, + parse_yaml_from_file, + template, +) from ansiblelint.yaml_utils import deannotate, nested_items_path if TYPE_CHECKING: @@ -95,7 +99,10 @@ class JinjaRule(AnsibleLintRule, TransformMixin): tags = ["formatting"] version_added = "v6.5.0" _ansible_error_re = re.compile( - r"^(?P.*): (?P.*)\. String: (?P.*)$", + ( + r"^(?P.*): (?P.*)\. String: (?P.*)$" + r"|An unhandled exception occurred while templating '.*'\. Error was a .*, original message: (?P.*)" + ), flags=re.MULTILINE, ) @@ -160,13 +167,20 @@ def matchtask( ): error = match.group("error") detail = match.group("detail") - if error.startswith( + nested_error = match.group("nested_error") + if error and error.startswith( "template error while templating string", ): bypass = False - elif detail.startswith("unable to locate collection"): + elif detail and detail.startswith( + "unable to locate collection", + ): _logger.debug("Ignored AnsibleError: %s", exc) bypass = True + elif nested_error and nested_error.startswith( + "Unexpected templating type error occurred on", + ): + bypass = True else: bypass = False elif re.match(r"^lookup plugin (.*) not found$", exc.message): @@ -863,6 +877,26 @@ def test_jinja_transform( assert expected_content == transformed_content playbook.with_suffix(f".tmp{playbook.suffix}").unlink() + def test_jinja_nested_var_errors() -> None: + """Tests our ability to handle nested var errors from jinja2 templates.""" + + def _do_template(*args, **kwargs): # type: ignore[no-untyped-def] # Templar.do_template has no type hint + data = args[1] + + if data != "{{ 12 | random(seed=inventory_hostname) }}": + return do_template(*args, **kwargs) + + msg = "Unexpected templating type error occurred on (foo): bar" + raise AnsibleError(msg) + + do_template = Templar.do_template + collection = RulesCollection() + collection.register(JinjaRule()) + lintable = Lintable("examples/playbooks/jinja-nested-vars.yml") + with mock.patch.object(Templar, "do_template", _do_template): + results = Runner(lintable, rules=collection).run() + assert len(results) == 0 + def _get_error_line(task: dict[str, Any], path: list[str | int]) -> int: """Return error line number."""