mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
A force_bridge app (matrix's MDAD flavor) deploys as a node-local compose stack and publishes its local port on its host, while openresty runs as an overlay task whose 127.0.0.1 is its own loopback; the readiness probe died on 502. The proxy_pass lookup now resolves networks.local.force_bridge (trust-tagged for the 2.19 templar, hard-failing on an unrendered template instead of silently falling back to loopback) and keys the decision on the real DEPLOYMENT_MODE, since such apps rewrite the effective mode to compose via compose_mode_force. The upstream becomes host.docker.internal:<local_port> rendered as a literal directive, because the request-time resolver cannot see /etc/hosts entries, and the swarm openresty service maps host.docker.internal to the host gateway like prometheus already does. Non-force_bridge renders stay byte-identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
111 lines
4.1 KiB
Python
111 lines
4.1 KiB
Python
"""Lookup ``proxy_pass``: thin wrapper emitting the mode-aware directive via
|
|
:func:`utils.networks.proxy.resolve_upstream` + :func:`render_proxy_pass`."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
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.proxy import render_proxy_pass, resolve_upstream
|
|
from utils.roles.applications.config import get as get_app_conf
|
|
from utils.templating.ansible import _trust_as_template
|
|
|
|
|
|
def _as_str(value: Any) -> str:
|
|
return "" if value is None else str(value).strip()
|
|
|
|
|
|
def _as_bool(value: Any) -> bool:
|
|
return _as_str(value).lower() in ("true", "1", "yes")
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
def run(
|
|
self,
|
|
terms: list[Any] | None,
|
|
variables: dict[str, Any] | None = None,
|
|
**kwargs: Any,
|
|
) -> list[str]:
|
|
if not terms:
|
|
raise AnsibleError("proxy_pass lookup requires the application_id term")
|
|
|
|
application_id = _as_str(terms[0])
|
|
if not application_id:
|
|
raise AnsibleError("proxy_pass: application_id must be non-empty")
|
|
|
|
service_key = _as_str(terms[1]) if len(terms) > 1 else ""
|
|
port_kind = _as_str(terms[2]) if len(terms) > 2 else "http"
|
|
if not port_kind:
|
|
port_kind = "http"
|
|
tail = _as_str(kwargs.get("tail")) or "request"
|
|
location = _as_str(kwargs.get("location")) or "/"
|
|
|
|
vars_ = variables or getattr(self._templar, "available_variables", {}) or {}
|
|
templar = getattr(self, "_templar", None)
|
|
|
|
raw_mode = vars_.get("DEPLOYMENT_MODE", "compose")
|
|
if templar is not None:
|
|
with contextlib.suppress(Exception):
|
|
raw_mode = templar.template(raw_mode)
|
|
real_deployment_mode = str(raw_mode).strip()
|
|
|
|
mode_force = vars_.get("compose_mode_force", "")
|
|
if templar is not None:
|
|
with contextlib.suppress(Exception):
|
|
mode_force = templar.template(mode_force)
|
|
deployment_mode = _as_str(mode_force) or real_deployment_mode
|
|
|
|
applications = lookup_loader.get(
|
|
"applications", loader=self._loader, templar=getattr(self, "_templar", None)
|
|
).run([], variables=vars_)[0]
|
|
|
|
force_bridge_raw = get_app_conf(
|
|
applications=applications,
|
|
application_id=application_id,
|
|
config_path="networks.local.force_bridge",
|
|
strict=False,
|
|
default=False,
|
|
skip_missing_app=True,
|
|
)
|
|
if templar is not None and isinstance(force_bridge_raw, str):
|
|
with contextlib.suppress(Exception):
|
|
force_bridge_raw = templar.template(
|
|
_trust_as_template(force_bridge_raw)
|
|
)
|
|
if isinstance(force_bridge_raw, str) and "{{" in force_bridge_raw:
|
|
raise AnsibleError(
|
|
f"proxy_pass: networks.local.force_bridge for "
|
|
f"{application_id!r} did not template: {force_bridge_raw!r}"
|
|
)
|
|
# Exception: keyed on real_deployment_mode, not deployment_mode - a
|
|
# force_bridge app rewrites the latter to 'compose' via
|
|
# compose_mode_force, which would silently no-op the host-gateway path.
|
|
host_gateway = _as_bool(force_bridge_raw) and real_deployment_mode == "swarm"
|
|
|
|
try:
|
|
authority = resolve_upstream(
|
|
applications,
|
|
application_id,
|
|
service_key,
|
|
port_kind,
|
|
deployment_mode,
|
|
local_port=_as_str(kwargs.get("local_port")),
|
|
internal_port=_as_str(kwargs.get("internal_port")),
|
|
host_gateway=host_gateway,
|
|
)
|
|
return [
|
|
render_proxy_pass(
|
|
authority,
|
|
deployment_mode,
|
|
tail=tail,
|
|
location=location,
|
|
host_gateway=host_gateway,
|
|
)
|
|
]
|
|
except ValueError as exc:
|
|
raise AnsibleError(f"proxy_pass: {exc}") from exc
|