summaryrefslogtreecommitdiffstats
path: root/src/ansiblelint/rules/schema.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/ansiblelint/rules/schema.py')
-rw-r--r--src/ansiblelint/rules/schema.py107
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()