diff options
Diffstat (limited to 'src/ansiblelint/rules/schema.py')
-rw-r--r-- | src/ansiblelint/rules/schema.py | 107 |
1 files changed, 79 insertions, 28 deletions
diff --git a/src/ansiblelint/rules/schema.py b/src/ansiblelint/rules/schema.py index 32ff2eb..6997acd 100644 --- a/src/ansiblelint/rules/schema.py +++ b/src/ansiblelint/rules/schema.py @@ -1,7 +1,9 @@ """Rule definition for JSON Schema Validations.""" + from __future__ import annotations import logging +import re import sys from typing import TYPE_CHECKING, Any @@ -13,6 +15,7 @@ from ansiblelint.schemas.main import validate_file_schema from ansiblelint.text import has_jinja if TYPE_CHECKING: + from ansiblelint.config import Options from ansiblelint.utils import Task @@ -94,7 +97,11 @@ class ValidateSchemaRule(AnsibleLintRule): def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]: """Return matches found for a specific playbook.""" results: list[MatchError] = [] - if not data or file.kind not in ("tasks", "handlers", "playbook"): + if ( + not data + or file.kind not in ("tasks", "handlers", "playbook") + or file.failed() + ): return results # check at play level results.extend(self._get_field_matches(file=file, data=data)) @@ -117,7 +124,7 @@ class ValidateSchemaRule(AnsibleLintRule): message=msg, lineno=data.get("__line__", 1), lintable=file, - rule=ValidateSchemaRule(), + rule=self, details=ValidateSchemaRule.description, tag=f"schema[{file.kind}]", ), @@ -129,9 +136,13 @@ class ValidateSchemaRule(AnsibleLintRule): task: Task, file: Lintable | None = None, ) -> bool | str | MatchError | list[MatchError]: - results = [] + results: list[MatchError] = [] if not file: file = Lintable("", kind="tasks") + + if file.failed(): + return results + results.extend(self._get_field_matches(file=file, data=task.raw_task)) for key in pre_checks["task"]: if key in task.raw_task: @@ -141,7 +152,7 @@ class ValidateSchemaRule(AnsibleLintRule): MatchError( message=msg, lintable=file, - rule=ValidateSchemaRule(), + rule=self, details=ValidateSchemaRule.description, tag=f"schema[{tag}]", ), @@ -151,12 +162,15 @@ class ValidateSchemaRule(AnsibleLintRule): def matchyaml(self, file: Lintable) -> list[MatchError]: """Return JSON validation errors found as a list of MatchError(s).""" result: list[MatchError] = [] + + if file.failed(): + return result + if file.kind not in JSON_SCHEMAS: return result - errors = validate_file_schema(file) - if errors: - if errors[0].startswith("Failed to load YAML file"): + for error in validate_file_schema(file): + if error.startswith("Failed to load YAML file"): _logger.debug( "Ignored failure to load %s for schema validation, as !vault may cause it.", file, @@ -165,13 +179,14 @@ class ValidateSchemaRule(AnsibleLintRule): result.append( MatchError( - message=errors[0], + message=error, lintable=file, - rule=ValidateSchemaRule(), + rule=self, details=ValidateSchemaRule.description, tag=f"schema[{file.kind}]", ), ) + break if not result: result = super().matchyaml(file) @@ -183,7 +198,6 @@ if "pytest" in sys.modules: import pytest # pylint: disable=ungrouped-imports - from ansiblelint.config import options from ansiblelint.rules import RulesCollection from ansiblelint.runner import Runner @@ -191,27 +205,30 @@ if "pytest" in sys.modules: ("file", "expected_kind", "expected"), ( pytest.param( - "examples/collection/galaxy.yml", + "examples/.collection/galaxy.yml", "galaxy", - ["'GPL' is not one of"], + [r".*'GPL' is not one of.*https://"], id="galaxy", ), pytest.param( "examples/roles/invalid_requirements_schema/meta/requirements.yml", "requirements", - ["{'foo': 'bar'} is not valid under any of the given schemas"], + [ + # r".*{'foo': 'bar'} is not valid under any of the given schemas.*https://", + r".*{'foo': 'bar'} is not of type 'array'.*https://", + ], id="requirements", ), pytest.param( "examples/roles/invalid_meta_schema/meta/main.yml", "meta", - ["False is not of type 'string'"], + [r".*False is not of type 'string'.*https://"], id="meta", ), pytest.param( "examples/playbooks/vars/invalid_vars_schema.yml", "vars", - ["'123' does not match any of the regexes"], + [r".* '123' does not match any of the regexes.*https://"], id="vars", ), pytest.param( @@ -223,14 +240,23 @@ if "pytest" in sys.modules: pytest.param( "examples/ee_broken/execution-environment.yml", "execution-environment", - ["{'foo': 'bar'} is not valid under any of the given schemas"], + [ + r".*Additional properties are not allowed \('foo' was unexpected\).*https://", + ], id="execution-environment-broken", ), - ("examples/meta/runtime.yml", "meta-runtime", []), + pytest.param( + "examples/meta/runtime.yml", + "meta-runtime", + [], + id="meta-runtime", + ), pytest.param( "examples/broken_collection_meta_runtime/meta/runtime.yml", "meta-runtime", - ["Additional properties are not allowed ('foo' was unexpected)"], + [ + r".*Additional properties are not allowed \('foo' was unexpected\).*https://", + ], id="meta-runtime-broken", ), pytest.param( @@ -242,7 +268,9 @@ if "pytest" in sys.modules: pytest.param( "examples/inventory/broken_dev_inventory.yml", "inventory", - ["Additional properties are not allowed ('foo' was unexpected)"], + [ + r".*Additional properties are not allowed \('foo' was unexpected\).*https://", + ], id="inventory-broken", ), pytest.param( @@ -260,7 +288,17 @@ if "pytest" in sys.modules: pytest.param( "examples/broken/.ansible-lint", "ansible-lint-config", - ["Additional properties are not allowed ('foo' was unexpected)"], + [ + r".*Additional properties are not allowed \('foo' was unexpected\).*https://", + ], + id="ansible-lint-config-broken", + ), + pytest.param( + "examples/broken_supported_ansible_also/.ansible-lint", + "ansible-lint-config", + [ + r".*supported_ansible_also True is not of type 'array'.*https://", + ], id="ansible-lint-config-broken", ), pytest.param( @@ -272,7 +310,9 @@ if "pytest" in sys.modules: pytest.param( "examples/broken/ansible-navigator.yml", "ansible-navigator-config", - ["Additional properties are not allowed ('ansible' was unexpected)"], + [ + r".*Additional properties are not allowed \('ansible' was unexpected\).*https://", + ], id="ansible-navigator-config-broken", ), pytest.param( @@ -284,20 +324,25 @@ if "pytest" in sys.modules: pytest.param( "examples/roles/broken_argument_specs/meta/argument_specs.yml", "role-arg-spec", - ["Additional properties are not allowed ('foo' was unexpected)"], + [ + r".*Additional properties are not allowed \('foo' was unexpected\).*https://", + ], id="role-arg-spec-broken", ), pytest.param( "examples/changelogs/changelog.yaml", "changelog", - ["Additional properties are not allowed ('foo' was unexpected)"], + [ + r".*Additional properties are not allowed \('foo' was unexpected\).*https://", + ], id="changelog", ), pytest.param( "examples/rulebooks/rulebook-fail.yml", "rulebook", [ - "Additional properties are not allowed ('that_should_not_be_here' was unexpected)", + # r".*Additional properties are not allowed \('that_should_not_be_here' was unexpected\).*https://", + r".*'sss' is not of type 'object'.*https://", ], id="rulebook", ), @@ -324,19 +369,24 @@ if "pytest" in sys.modules: ), ), ) - def test_schema(file: str, expected_kind: str, expected: list[str]) -> None: + def test_schema( + file: str, + expected_kind: str, + expected: list[str], + config_options: Options, + ) -> None: """Validate parsing of ansible output.""" lintable = Lintable(file) assert lintable.kind == expected_kind - rules = RulesCollection(options=options) + rules = RulesCollection(options=config_options) rules.register(ValidateSchemaRule()) results = Runner(lintable, rules=rules).run() assert len(results) == len(expected), results for idx, result in enumerate(results): assert result.filename.endswith(file) - assert expected[idx] in result.message + assert re.match(expected[idx], result.message) assert result.tag == f"schema[{expected_kind}]" @pytest.mark.parametrize( @@ -356,12 +406,13 @@ if "pytest" in sys.modules: expected_kind: str, expected_tag: str, count: int, + config_options: Options, ) -> None: """Validate ability to detect schema[moves].""" lintable = Lintable(file) assert lintable.kind == expected_kind - rules = RulesCollection(options=options) + rules = RulesCollection(options=config_options) rules.register(ValidateSchemaRule()) results = Runner(lintable, rules=rules).run() |