mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
fix(svc-net-tor): render the onion forward flags before reading them
2f533eb279read TOR_ONION_SSH_ENABLED straight out of the lookup's variables, but group vars reach a lookup unrendered, so the flag arrived as its own template text and boolean() refused it. The torrc render aborted, and with it every tor row: 14 jobs across both runs on095ea5e882failed with the same line, Baserow#0 in compose and Nextcloud#1 in swarm among them. Quoted from job 104843161609: The lookup plugin 'tor_extra_ports' failed: The value '{{ lookup('config', application_id, 'services.tor.forwards.ssh') | bool }}' is not a valid boolean. Reproduced locally: boolean() on that literal raises with the identical message. tor_egress_clients.py:43 in this same role has resolved its own references through templar.template(trust_as_template(...)) sincebf32f57b85. _flag now does the same and names the variable when the value is still not a boolean afterwards. The unit tests passed real booleans, which is why they stayed green through seven gate runs; the new case sends template text through a templar, the shape production delivers. No lint or unit test could have caught this, because it only appears once ansible hands the variable over unrendered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
095ea5e882
commit
08b4d0f2ff
2 files changed
+48
-1
No files matched your search
@@ -28,6 +28,7 @@ from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible.plugins.loader import lookup_loader
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from ansible.template import trust_as_template
|
||||
|
||||
FLAGGED_PORTS: tuple[tuple[str, int], ...] = (
|
||||
("TOR_ONION_SSH_ENABLED", 22),
|
||||
@@ -35,6 +36,25 @@ FLAGGED_PORTS: tuple[tuple[str, int], ...] = (
|
||||
)
|
||||
|
||||
|
||||
def _flag(templar: Any, name: str, value: Any) -> bool:
|
||||
"""A declared flag as a boolean, with its Jinja resolved first.
|
||||
|
||||
Args:
|
||||
templar: the lookup's templar, or None when it has none.
|
||||
name: the variable the flag came from, for the error message.
|
||||
value: whatever the variable holds; group vars reach a lookup
|
||||
unrendered, so a reference arrives as its own template text.
|
||||
"""
|
||||
if isinstance(value, str) and "{{" in value and templar is not None:
|
||||
value = templar.template(trust_as_template(value))
|
||||
try:
|
||||
return boolean(value)
|
||||
except TypeError as exc:
|
||||
raise AnsibleError(
|
||||
f"lookup('tor_extra_ports'): {name} is not a boolean: {value!r}"
|
||||
) from exc
|
||||
|
||||
|
||||
def _entry(port: int) -> dict[str, Any]:
|
||||
"""One forward, in the shape ``torrc.j2`` consumes.
|
||||
|
||||
@@ -61,7 +81,7 @@ class LookupModule(LookupBase):
|
||||
for name, port in FLAGGED_PORTS:
|
||||
if name not in variables:
|
||||
raise AnsibleError(f"lookup('tor_extra_ports'): {name} is not defined")
|
||||
if boolean(variables[name]):
|
||||
if _flag(getattr(self, "_templar", None), name, variables[name]):
|
||||
entries.append(_entry(port))
|
||||
|
||||
derived = lookup_loader.get(
|
||||
|
||||
@@ -49,6 +49,16 @@ class _DerivedPorts:
|
||||
return [self.entries]
|
||||
|
||||
|
||||
class _Templar:
|
||||
"""Resolves a group_var reference the way the play's templar does."""
|
||||
|
||||
def __init__(self, rendered: Any):
|
||||
self.rendered = rendered
|
||||
|
||||
def template(self, _value):
|
||||
return self.rendered
|
||||
|
||||
|
||||
class TestExtraPorts(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
@@ -63,6 +73,23 @@ class TestExtraPorts(unittest.TestCase):
|
||||
):
|
||||
return lookup.run([], variables={**ENABLED, **variables})[0]
|
||||
|
||||
def test_a_flag_that_arrives_as_template_text_is_still_read(self) -> None:
|
||||
lookup = self.module.LookupModule()
|
||||
lookup._templar = _Templar(True)
|
||||
lookup._loader = None
|
||||
variables = dict.fromkeys(ENABLED, "{{ lookup('config', application_id, 'services.tor.forwards.x') | bool }}")
|
||||
with mock.patch.object(
|
||||
self.module.lookup_loader, "get", return_value=_DerivedPorts([])
|
||||
):
|
||||
ports = lookup.run([], variables=variables)[0]
|
||||
self.assertIn(
|
||||
HTTP_ENTRY,
|
||||
ports,
|
||||
"group vars reach a lookup unrendered, so the flag arrives as its own "
|
||||
"template text; reading it without templating aborts the torrc render "
|
||||
"on every tor row",
|
||||
)
|
||||
|
||||
def test_http_is_forwarded_without_the_proxy_in_the_inventory(self) -> None:
|
||||
self.assertIn(
|
||||
HTTP_ENTRY,
|
||||
|
||||
Reference in new issue
Block a user