Skip to content

Commit 008b41c

Browse files
authored
Add action and args properties to task class (#3576)
1 parent c2ca14e commit 008b41c

File tree

7 files changed

+60
-19
lines changed

7 files changed

+60
-19
lines changed

examples/playbooks/rule-args-module-fail.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
masked: false
1818

1919
- name: Enable service httpd and ensure it is not masked
20-
# module should produce: 'Unsupported parameters for ansible.builtin.systemd module: foo. Supported parameters include: no_block, state, daemon_reload (daemon-reload), name (service, unit), force, masked, daemon_reexec (daemon-reexec), scope, enabled.'
20+
# module should produce: 'Unsupported parameters for ansible.builtin.systemd module"
2121
ansible.builtin.systemd:
2222
foo: true
2323

@@ -34,3 +34,4 @@
3434
ansible.builtin.file:
3535
path: /opt/software/deployment
3636
state: away
37+
mode: "0600"

examples/playbooks/rule-risky-file-permissions-fail.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
path: foo
99
create: true
1010
mode: preserve
11+
section: bar
1112

1213
- name: FAIL_INI_PERMISSION
1314
hosts: all
@@ -16,6 +17,7 @@
1617
community.general.ini_file:
1718
path: foo
1819
create: true
20+
section: bar
1921

2022
- name: FAIL_PRESERVE_MODE
2123
hosts: all
@@ -28,7 +30,7 @@
2830
- name: FAIL_MISSING_PERMISSIONS_TOUCH
2931
hosts: all
3032
tasks:
31-
- name: Permissions missing and might create file
33+
- name: Permissions missing and might create file # noqa: fqcn[action-core]
3234
file:
3335
path: foo
3436
state: touch
@@ -40,7 +42,7 @@
4042
- name: FAIL_MISSING_PERMISSIONS_DIRECTORY
4143
hosts: all
4244
tasks:
43-
- name: Permissions missing and might create directory
45+
- name: Permissions missing and might create directory # noqa: fqcn[action-core]
4446
file:
4547
path: foo
4648
state: directory
@@ -71,15 +73,16 @@
7173
- name: FAIL_REPLACE_PRESERVE
7274
hosts: all
7375
tasks:
74-
- name: Replace does not allow preserve mode
76+
- name: Replace does not allow preserve mode # noqa: fqcn[action-core]
7577
replace:
7678
path: foo
7779
mode: preserve
80+
regexp: foo
7881

7982
- name: FAIL_PERMISSION_COMMENT
8083
hosts: all
8184
tasks:
82-
- name: Permissions is only a comment
85+
- name: Permissions is only a comment # noqa: fqcn[action-core]
8386
file:
8487
path: foo
8588
owner: root

examples/playbooks/rule-risky-file-permissions-pass.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
- name: Replace should not require mode
5757
ansible.builtin.replace:
5858
path: foo
59+
regexp: foo
5960

6061
- name: SUCCESS_RECURSE
6162
hosts: all
@@ -64,6 +65,7 @@
6465
ansible.builtin.file:
6566
state: directory
6667
recurse: true
68+
path: foo
6769
- name: Permissions not missing and numeric (fqcn)
6870
ansible.builtin.file:
6971
path: bar

src/ansiblelint/constants.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,13 @@ def main():
136136
SKIPPED_RULES_KEY = "__skipped_rules__"
137137
LINE_NUMBER_KEY = "__line__"
138138
FILENAME_KEY = "__file__"
139-
ANNOTATION_KEYS = [FILENAME_KEY, LINE_NUMBER_KEY, SKIPPED_RULES_KEY]
140-
139+
ANNOTATION_KEYS = [
140+
FILENAME_KEY,
141+
LINE_NUMBER_KEY,
142+
SKIPPED_RULES_KEY,
143+
"__ansible_module__",
144+
"__ansible_module_original__",
145+
]
141146
INCLUSION_ACTION_NAMES = {
142147
"include",
143148
"include_tasks",

src/ansiblelint/rules/args.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,10 @@ def _parse_failed_msg(
282282

283283
from ansiblelint.runner import Runner # pylint: disable=ungrouped-imports
284284

285-
def test_args_module_fail() -> None:
285+
def test_args_module_fail(default_rules_collection: RulesCollection) -> None:
286286
"""Test rule invalid module options."""
287-
collection = RulesCollection()
288-
collection.register(ArgsRule())
289287
success = "examples/playbooks/rule-args-module-fail.yml"
290-
results = Runner(success, rules=collection).run()
288+
results = Runner(success, rules=default_rules_collection).run()
291289
assert len(results) == 5
292290
assert results[0].tag == "args[module]"
293291
assert "missing required arguments" in results[0].message
@@ -300,12 +298,13 @@ def test_args_module_fail() -> None:
300298
assert results[4].tag == "args[module]"
301299
assert "value of state must be one of" in results[4].message
302300

303-
def test_args_module_pass(caplog: pytest.LogCaptureFixture) -> None:
301+
def test_args_module_pass(
302+
default_rules_collection: RulesCollection,
303+
caplog: pytest.LogCaptureFixture,
304+
) -> None:
304305
"""Test rule valid module options."""
305-
collection = RulesCollection()
306-
collection.register(ArgsRule())
307306
success = "examples/playbooks/rule-args-module-pass.yml"
308307
with caplog.at_level(logging.WARNING):
309-
results = Runner(success, rules=collection).run()
308+
results = Runner(success, rules=default_rules_collection).run()
310309
assert len(results) == 0, results
311310
assert len(caplog.records) == 0, caplog.records

src/ansiblelint/rules/risky_file_permissions.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ def matchtask(
9595
module = task["action"]["__ansible_module__"]
9696
mode = task["action"].get("mode", None)
9797

98+
if not isinstance(task.args, dict):
99+
# We are unable to check args when using jinja templating
100+
return False
101+
98102
if module not in self._modules and module not in self._modules_with_create:
99103
return False
100104

@@ -151,11 +155,13 @@ def matchtask(
151155
),
152156
),
153157
)
154-
def test_risky_file_permissions(file: str, expected: int) -> None:
158+
def test_risky_file_permissions(
159+
file: str,
160+
expected: int,
161+
default_rules_collection: RulesCollection,
162+
) -> None:
155163
"""The ini_file module does not accept preserve mode."""
156-
collection = RulesCollection()
157-
collection.register(MissingFilePermissionsRule())
158-
runner = RunFromText(collection)
164+
runner = RunFromText(default_rules_collection)
159165
results = runner.run(Path(file))
160166
assert len(results) == expected
161167
for result in results:

src/ansiblelint/utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
from ansiblelint.app import get_app
5757
from ansiblelint.config import Options, options
5858
from ansiblelint.constants import (
59+
ANNOTATION_KEYS,
5960
FILENAME_KEY,
6061
INCLUSION_ACTION_NAMES,
6162
LINE_NUMBER_KEY,
@@ -788,6 +789,30 @@ def name(self) -> str | None:
788789
"""Return the name of the task."""
789790
return self.raw_task.get("name", None)
790791

792+
@property
793+
def action(self) -> str:
794+
"""Return the resolved action name."""
795+
action_name = self.normalized_task["action"]["__ansible_module_original__"]
796+
if not isinstance(action_name, str):
797+
msg = "Task actions can only be strings."
798+
raise RuntimeError(msg)
799+
return action_name
800+
801+
@property
802+
def args(self) -> Any:
803+
"""Return the arguments passed to the task action.
804+
805+
While we usually expect to return a dictionary, it can also
806+
return a templated string when jinja is used.
807+
"""
808+
if "args" in self.raw_task:
809+
return self.raw_task["args"]
810+
result = {}
811+
for k, v in self.normalized_task["action"].items():
812+
if k not in ANNOTATION_KEYS:
813+
result[k] = v
814+
return result
815+
791816
@property
792817
def normalized_task(self) -> dict[str, Any]:
793818
"""Return the name of the task."""

0 commit comments

Comments
 (0)