diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 3667ea1f9f7..3b9052c81b7 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -59,7 +59,7 @@ jobs: WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. - PYTEST_REQPASS: 801 + PYTEST_REQPASS: 802 steps: - name: Activate WSL1 if: "contains(matrix.shell, 'wsl')" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eeacc261079..4ab8baf26c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -146,7 +146,7 @@ repos: # empty args needed in order to match mypy cli behavior args: [--strict] additional_dependencies: - - ansible-compat>=4.0.1 + - ansible-compat>=4.1.0 - black>=22.10.0 - cryptography>=39.0.1 - filelock diff --git a/examples/playbooks/rule-schema-become-method.yml b/examples/playbooks/rule-schema-become-method.yml new file mode 100644 index 00000000000..23eca31bbb3 --- /dev/null +++ b/examples/playbooks/rule-schema-become-method.yml @@ -0,0 +1,5 @@ +--- +- name: Test 'become_method' plugin validity + hosts: localhost + become: true + become_method: ansible.builtin.sudo diff --git a/src/ansiblelint/rules/schema.py b/src/ansiblelint/rules/schema.py index 31d8c1b6841..5c47b245f19 100644 --- a/src/ansiblelint/rules/schema.py +++ b/src/ansiblelint/rules/schema.py @@ -49,6 +49,10 @@ }, } +FIELD_CHECKS = { + "become_method": get_app().runtime.plugins.become.keys(), +} + class ValidateSchemaRule(AnsibleLintRule): """Perform JSON Schema Validation for known lintable kinds.""" @@ -77,24 +81,23 @@ class ValidateSchemaRule(AnsibleLintRule): "schema[vars]": "", } + become_method_msg = f"'become_method' must be one of the currently installed plugins: {', '.join(FIELD_CHECKS['become_method'])}" + def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]: """Return matches found for a specific playbook.""" - results = [] - + results: list[MatchError] = [] if not data or file.kind not in ("tasks", "handlers", "playbook"): return results # check at play level become_method = data.get("become_method", None) - available_become_plugins = get_app().runtime.plugins.become - available_become_plugins_keys = available_become_plugins.keys() if ( become_method and not has_jinja(become_method) - and not available_become_plugins.get(become_method) + and become_method not in FIELD_CHECKS["become_method"] ): results.append( MatchError( - message=f"'become_method' must be one of the currently installed plugins: {', '.join(available_become_plugins_keys)}", + message=self.become_method_msg, lintable=file or Lintable(""), rule=ValidateSchemaRule(), details=ValidateSchemaRule.description, @@ -102,22 +105,6 @@ def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]: ), ) - # check at task level - tasks = data.get("tasks", []) - if tasks: - for task in tasks: - become_method = task.get("become_method", None) - if become_method and not has_jinja(become_method): - results.append( - MatchError( - message=f"'become_method' must be one of the currently installed plugins: {', '.join(available_become_plugins_keys)}", - lintable=file or Lintable(""), - rule=ValidateSchemaRule(), - details=ValidateSchemaRule.description, - tag=f"schema[{file.kind}]", - ), - ) - return results def matchtask( @@ -126,6 +113,21 @@ def matchtask( file: Lintable | None = None, ) -> bool | str | MatchError | list[MatchError]: result = [] + become_method = task.raw_task.get("become_method", None) + if ( + become_method + and not has_jinja(become_method) + and become_method not in FIELD_CHECKS["become_method"] + ): + result.append( + MatchError( + message=self.become_method_msg, + lintable=file or Lintable(""), + rule=ValidateSchemaRule(), + details=ValidateSchemaRule.description, + tag=f"schema[{file.kind}]", # type: ignore[union-attr] + ), + ) for key in pre_checks["task"]: if key in task.raw_task: msg = pre_checks["task"][key]["msg"] @@ -300,6 +302,12 @@ def matchyaml(self, file: Lintable) -> list[MatchError]: [], id="rulebook2", ), + pytest.param( + "examples/playbooks/rule-schema-become-method.yml", + "playbook", + [], + id="playbook", + ), ), ) def test_schema(file: str, expected_kind: str, expected: list[str]) -> None: