Files
computer-playbook/plugins/lookup/subnet_address_regex.py
T
kevinveenbirkenbachandClaude Opus 5 549d272884 refactor(lookup): keep one resolver for a variable that may still be a template
Seven lookups read application_id out of the play's variables and every one of
them has to template it, because sys-stk-full defines application_id from
sys_stk_full_application_id and a dict read returns that unrendered. They did
it in five spellings: a public resolve_var, two byte-identical private copies,
and inline variants. Nothing named the rule, so it survived as folklore, and a
lookup written without knowing it took down three jobs of one run.

resolve_var now lives in utils/templating, which is where the six lookups that
import it can reach it without pulling in a networking module for a concern
that is not networking. utils/networks/lookup_context re-exports it under the
alias form, so its own importers are untouched and autodoc still resolves one
target. The two private copies go, and so does the fourth copy this branch
added to webserver_resource a few commits ago.

The private copies guarded value is not None and the public one did not. The
canonical keeps the guard: it is narrower than every live caller, each of
which passes a defaulted value, so no existing call can reach the branch.

The helper had no test at all. Its six behaviours are characterised first,
including that a templar raising on an undefined variable leaves the value
alone rather than failing the play, which is what the suppression is for.

Four inline copies stay. compose_volumes lets a template error propagate where
this suppresses it, and mcp_servers only templates a str containing a template
marker. Folding those in would change behaviour, which is not a refactor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 11:30:54 +02:00

61 lines
2.0 KiB
Python

"""Lookup `subnet_address_regex`: match any address in the role's local subnet.
Single SPOT for turning `networks.local.subnet` into an address matcher. A swarm
task carries an address on every network it joins, all of them site-local, so a
service that must bind to one of them needs this rather than a generic
"site-local" selector.
Example:
{{ lookup('subnet_address_regex') }} # 192\\.168\\.24\\.\\d+
Raises when the subnet is missing or its prefix is not octet-aligned, so a
topology this cannot express fails at render time instead of binding silently
to the wrong network.
"""
from __future__ import annotations
from typing import Any
from ansible.errors import AnsibleError
from ansible.plugins.loader import lookup_loader
from ansible.plugins.lookup import LookupBase
from utils.networks.address import subnet_address_regex
from utils.templating.vars import resolve_var
class LookupModule(LookupBase):
def run(
self,
terms: list[Any] | None,
variables: dict[str, Any] | None = None,
**kwargs: Any,
) -> list[str]:
if terms:
raise AnsibleError("subnet_address_regex lookup takes no positional terms")
vars_ = variables or getattr(self._templar, "available_variables", {}) or {}
templar = getattr(self, "_templar", None)
application_id = resolve_var(templar, vars_.get("application_id"))
if not application_id:
raise AnsibleError(
"subnet_address_regex lookup: application_id is required in variables"
)
config_lookup = lookup_loader.get(
"config", loader=self._loader, templar=templar
)
subnet = config_lookup.run(
[str(application_id), "networks.local.subnet"], variables=vars_
)[0]
try:
return [subnet_address_regex(str(subnet))]
except ValueError as exc:
raise AnsibleError(
f"subnet_address_regex lookup for '{application_id}': {exc}"
) from exc