Skip to content

Commit

Permalink
Check validity of 'become_method' values from code (#3499)
Browse files Browse the repository at this point in the history
Co-authored-by: Sorin Sbarnea <ssbarnea@redhat.com>
  • Loading branch information
audgirka and ssbarnea authored May 31, 2023
1 parent 0f40faa commit c4e37e4
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: 804
PYTEST_REQPASS: 806
steps:
- name: Activate WSL1
if: "contains(matrix.shell, 'wsl')"
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions examples/playbooks/rule-schema-become-method-fail.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
- name: Test 'become_method' plugin validity
hosts: localhost
become: true
become_method: this_is_not_an_installed_plugin
tasks:
- name: Another example
ansible.builtin.debug:
msg: "This should not be reached"
become_method: this_is_not_an_installed_plugin
5 changes: 5 additions & 0 deletions examples/playbooks/rule-schema-become-method-pass.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
- name: Test 'become_method' plugin validity
hosts: localhost
become: true
become_method: ansible.builtin.sudo
67 changes: 64 additions & 3 deletions src/ansiblelint/rules/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

import logging
import sys
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from ansiblelint.app import get_app
from ansiblelint.errors import MatchError
from ansiblelint.file_utils import Lintable
from ansiblelint.rules import AnsibleLintRule
from ansiblelint.schemas.__main__ import JSON_SCHEMAS
from ansiblelint.schemas.main import validate_file_schema
from ansiblelint.text import has_jinja

if TYPE_CHECKING:
from ansiblelint.utils import Task
Expand Down Expand Up @@ -47,6 +49,10 @@
},
}

FIELD_CHECKS = {
"become_method": get_app().runtime.plugins.become.keys(), # pylint: disable=no-member
}


class ValidateSchemaRule(AnsibleLintRule):
"""Perform JSON Schema Validation for known lintable kinds."""
Expand Down Expand Up @@ -75,12 +81,49 @@ 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: list[MatchError] = []
if not data or file.kind not in ("tasks", "handlers", "playbook"):
return results
# check at play level
for key, value in FIELD_CHECKS.items():
if key in data:
plugin_value = data.get(key, None)
if not has_jinja(plugin_value) and plugin_value not in value:
results.append(
MatchError(
message=self.become_method_msg,
lintable=file or Lintable(""),
rule=ValidateSchemaRule(),
details=ValidateSchemaRule.description,
tag=f"schema[{file.kind}]",
),
)

return results

def matchtask(
self,
task: Task,
file: Lintable | None = None,
) -> bool | str | MatchError | list[MatchError]:
result = []
for key, value in FIELD_CHECKS.items():
if key in task.raw_task:
plugin_value = task.raw_task.get(key, None)
if not has_jinja(plugin_value) and plugin_value not in value:
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"]
Expand All @@ -98,9 +141,9 @@ def matchtask(

def matchyaml(self, file: Lintable) -> list[MatchError]:
"""Return JSON validation errors found as a list of MatchError(s)."""
result = []
result: list[MatchError] = []
if file.kind not in JSON_SCHEMAS:
return []
return result

errors = validate_file_schema(file)
if errors:
Expand All @@ -120,6 +163,9 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
tag=f"schema[{file.kind}]",
),
)

if not result:
result = super().matchyaml(file)
return result


Expand Down Expand Up @@ -252,6 +298,21 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
[],
id="rulebook2",
),
pytest.param(
"examples/playbooks/rule-schema-become-method-pass.yml",
"playbook",
[],
id="playbook",
),
pytest.param(
"examples/playbooks/rule-schema-become-method-fail.yml",
"playbook",
[
"'become_method' must be one of the currently installed plugins",
"'become_method' must be one of the currently installed plugins",
],
id="playbook2",
),
),
)
def test_schema(file: str, expected_kind: str, expected: list[str]) -> None:
Expand Down

0 comments on commit c4e37e4

Please sign in to comment.