feat(validation): treat any <placeholder> sub-option as a keyed collection

This commit is contained in:
2026-10-05 18:21:02 +02:00
parent fec3fbfa8e
commit 7129ceb515
2 changed files with 6 additions and 6 deletions
+5 -5
View File
@@ -2,8 +2,8 @@
Each spec file's ``argument_specs.inputs.options`` declares top-level variables. Every one the host
defines is checked without Ansible's lenient coercion: a string is never a list, bool or dict. A dict
option whose only sub-option is named ``<name>`` is a collection keyed by name; each entry is checked
against that sub-option, and a null entry means the entry is not managed. Messages name the key path
option whose only sub-option is a placeholder such as ``<name>`` is a collection keyed by name; each
entry is checked against that sub-option, and a null entry means the entry is not managed. Messages name the key path
and the types involved but never a value, so secrets cannot leak into job output.
"""
@@ -14,7 +14,6 @@ from ansible.errors import AnsibleError
from ansible.parsing.vault import EncryptedString
from ansible.plugins.action import ActionBase
KEYED = "<name>"
SPEC_KEYS = {"type", "required", "default", "choices", "elements", "options", "description", "no_log"}
UNRESOLVED = object()
@@ -91,10 +90,11 @@ class _Validator:
self.check(element, item, f"{path}[{index}]")
def check_options(self, options, value, path):
if set(options) == {KEYED}:
if len(options) == 1 and next(iter(options)).startswith("<"):
entry_spec = next(iter(options.values()))
for name, entry in value.items():
if entry is not None:
self.check(options[KEYED], entry, f"{path}.{name}")
self.check(entry_spec, entry, f"{path}.{name}")
return
for key in value:
if key not in options:
+1 -1
View File
@@ -36,7 +36,7 @@ argument_specs:
users:
type: dict
options:
"<name>":
"<user>":
type: dict
options:
password: