fix: fix handling of blocks (#767)

This commit is contained in:
Robert Kaussow 2024-01-31 22:49:19 +01:00 committed by GitHub
parent 37e701217e
commit a89d6d5336
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 90 additions and 63 deletions

View File

@ -149,11 +149,12 @@ class RuleBase(metaclass=RuleExtendedMeta):
# No need to normalize_task if we are skipping it. # No need to normalize_task if we are skipping it.
continue continue
normalized.append( normalized_task = normalize_task(
normalize_task(
task, candidate.path, settings["ansible"]["custom_modules"] task, candidate.path, settings["ansible"]["custom_modules"]
) )
) normalized_task["__raw_task__"] = task
normalized.append(normalized_task)
except LaterError as ex: except LaterError as ex:
e = ex.original e = ex.original

View File

@ -9,11 +9,7 @@ class CheckFQCNBuiltin(RuleBase):
helptext = "use FQCN `{module_alias}` for module action `{module}`" helptext = "use FQCN `{module_alias}` for module action `{module}`"
description = "Module actions should use full qualified collection names" description = "Module actions should use full qualified collection names"
types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars"] types = ["playbook", "task", "handler", "rolevars", "hostvars", "groupvars"]
module_aliases = { module_aliases = {"block/always/rescue": "block/always/rescue"}
"block": "block",
"always": "always",
"rescue": "rescue",
}
def check(self, candidate, settings): def check(self, candidate, settings):
tasks, errors = self.get_normalized_tasks(candidate, settings) tasks, errors = self.get_normalized_tasks(candidate, settings)

View File

@ -21,7 +21,7 @@ SORTER_TASKS = (
class CheckKeyOrder(RuleBase): class CheckKeyOrder(RuleBase):
rid = "ANS129" rid = "ANS129"
description = "Check optimized key order" description = "Check for recommended key order"
helptext = "{type} key order can be improved to `{sorted_keys}`" helptext = "{type} key order can be improved to `{sorted_keys}`"
types = ["playbook", "task", "handler"] types = ["playbook", "task", "handler"]

View File

@ -144,7 +144,7 @@ class Settings:
"exclude": [ "exclude": [
"meta", "meta",
"debug", "debug",
"block", "block/always/rescue",
"include_role", "include_role",
"import_role", "import_role",
"include_tasks", "include_tasks",

View File

@ -21,7 +21,6 @@
# THE SOFTWARE. # THE SOFTWARE.
import codecs import codecs
import copy
import os import os
from contextlib import suppress from contextlib import suppress
@ -370,24 +369,22 @@ def _kv_to_dict(v):
def normalize_task(task, filename, custom_modules=None): def normalize_task(task, filename, custom_modules=None):
"""Ensure tasks have an action key and strings are converted to python objects.""" """Ensure tasks have an action key and strings are converted to python objects."""
def _normalize(task, custom_modules):
if custom_modules is None: if custom_modules is None:
custom_modules = [] custom_modules = []
ansible_action_type = task.get("__ansible_action_type__", "task")
if "__ansible_action_type__" in task:
del task["__ansible_action_type__"]
# temp. extract metadata
ansible_meta = {}
for key in ["__line__", "__file__", "__ansible_action_meta__"]:
default = None
if key == "__ansible_action_meta__":
default = {}
ansible_meta[key] = task.pop(key, default)
normalized = {} normalized = {}
ansible_parsed_keys = ("action", "local_action", "args", "delegate_to")
if is_nested_task(task):
_extract_ansible_parsed_keys_from_task(normalized, task, ansible_parsed_keys)
# Add dummy action for block/always/rescue statements
normalized["action"] = {
"__ansible_module__": "block/always/rescue",
"__ansible_module_original__": "block/always/rescue",
"__ansible_arguments__": "block/always/rescue",
}
return normalized
builtin = list(ansible.parsing.mod_args.BUILTIN_TASKS) builtin = list(ansible.parsing.mod_args.BUILTIN_TASKS)
builtin = list(set(builtin + custom_modules)) builtin = list(set(builtin + custom_modules))
@ -405,7 +402,7 @@ def normalize_task(task, filename, custom_modules=None):
del arguments["_uses_shell"] del arguments["_uses_shell"]
for k, v in list(task.items()): for k, v in list(task.items()):
if k in ("action", "local_action", "args", "delegate_to") or k == action: if k in ansible_parsed_keys or k == action:
# we don"t want to re-assign these values, which were # we don"t want to re-assign these values, which were
# determined by the ModuleArgsParser() above # determined by the ModuleArgsParser() above
continue continue
@ -420,12 +417,32 @@ def normalize_task(task, filename, custom_modules=None):
} }
if "_raw_params" in arguments: if "_raw_params" in arguments:
normalized["action"]["__ansible_arguments__"] = arguments["_raw_params"].strip().split() normalized["action"]["__ansible_arguments__"] = (
arguments["_raw_params"].strip().split()
)
del arguments["_raw_params"] del arguments["_raw_params"]
else: else:
normalized["action"]["__ansible_arguments__"] = [] normalized["action"]["__ansible_arguments__"] = []
normalized["action"].update(arguments) normalized["action"].update(arguments)
return normalized
# temp. extract metadata
ansible_meta = {}
for key in ["__line__", "__file__", "__ansible_action_meta__"]:
default = None
if key == "__ansible_action_meta__":
default = {}
ansible_meta[key] = task.pop(key, default)
ansible_action_type = task.get("__ansible_action_type__", "task")
if "__ansible_action_type__" in task:
del task["__ansible_action_type__"]
normalized = _normalize(task, custom_modules)
normalized[FILENAME_KEY] = filename normalized[FILENAME_KEY] = filename
normalized["__ansible_action_type__"] = ansible_action_type normalized["__ansible_action_type__"] = ansible_action_type
@ -446,13 +463,8 @@ def action_tasks(yaml, candidate):
# Add sub-elements of block/rescue/always to tasks list # Add sub-elements of block/rescue/always to tasks list
tasks.extend(extract_from_list(tasks, ["block", "rescue", "always"])) tasks.extend(extract_from_list(tasks, ["block", "rescue", "always"]))
# Remove block/rescue/always elements from tasks list
block_rescue_always = ("block", "rescue", "always")
tasks[:] = [task for task in tasks if all(k not in task for k in block_rescue_always)]
allowed = ["include", "include_tasks", "import_playbook", "import_tasks"] return tasks
return [task for task in tasks if set(allowed).isdisjoint(task.keys())]
def task_to_str(task): def task_to_str(task):
@ -483,8 +495,6 @@ def extract_from_list(blocks, candidates):
meta_data.pop(key, None) meta_data.pop(key, None)
actions = add_action_type(block[candidate], candidate, meta_data) actions = add_action_type(block[candidate], candidate, meta_data)
for action in actions:
action["__raw_task__"] = copy.copy(block)
results.extend(actions) results.extend(actions)
elif block[candidate] is not None: elif block[candidate] is not None:
@ -575,6 +585,26 @@ def normalized_yaml(file, options):
return lines return lines
def is_nested_task(task):
"""Check if task includes block/always/rescue."""
# Cannot really trust the input
if isinstance(task, str):
return False
return any(task.get(key) for key in ["block", "rescue", "always"])
def _extract_ansible_parsed_keys_from_task(result, task, keys):
"""Return a dict with existing key in task."""
for k, v in list(task.items()):
if k in keys:
# we don't want to re-assign these values, which were
# determined by the ModuleArgsParser() above
continue
result[k] = v
return result
class UnsafeTag: class UnsafeTag:
"""Handle custom yaml unsafe tag.""" """Handle custom yaml unsafe tag."""

View File

@ -34,7 +34,7 @@ ansible:
exclude: exclude:
- "meta" - "meta"
- "debug" - "debug"
- "block" - "block/always/rescue"
- "include_role" - "include_role"
- "include_tasks" - "include_tasks"
- "include_vars" - "include_vars"