mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
89 lines
3.4 KiB
Python
89 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ansible.errors import AnsibleError
|
|
from ansible.module_utils.parsing.convert_bool import boolean as _to_bool
|
|
from ansible.plugins.lookup import LookupBase
|
|
|
|
from utils.networks.lookup_context import resolve_var
|
|
|
|
LOOPBACK = "127.0.0.1"
|
|
CONTAINER_HOST_ALIAS = "host.docker.internal"
|
|
NODE_GROUP = "svc-swarm-node"
|
|
MANAGER_GROUP = "svc-swarm-manager"
|
|
|
|
|
|
def resolve_smtp_host(
|
|
variables: dict[str, Any], email: dict[str, Any], *, in_container: bool = False
|
|
) -> Any:
|
|
"""Return the SMTP endpoint msmtp relays through.
|
|
|
|
Args:
|
|
variables: the play's variable namespace.
|
|
email: the resolved ``lookup('email')`` mapping.
|
|
in_container: the rendered config is mounted INTO a container rather
|
|
than read by the host's own msmtp.
|
|
|
|
Returns:
|
|
The configured relay host, the loopback address, the container's alias
|
|
for its host, or a swarm manager.
|
|
|
|
Outside the containerised test rig the configured relay is used unchanged.
|
|
Inside it that relay domain has neither DNS nor a route: a host carrying the
|
|
stack itself reaches mail on loopback, while a bare infra node has nothing
|
|
listening there and has to enter through the manager's routing mesh.
|
|
|
|
``in_container`` exists because one template serves two consumers. The host's
|
|
/root/.msmtprc is read by the host and loopback is right for it; the copy
|
|
mounted at /etc/msmtprc is read inside a container, where 127.0.0.1 is that
|
|
container's own loopback and nothing listens on it. Such a service must also
|
|
emit ``lookup('container_extra_hosts')`` so the alias resolves.
|
|
"""
|
|
relay = email.get("host")
|
|
in_rig = _to_bool(email.get("external"), strict=False) and _to_bool(
|
|
variables.get("DOCKER_IN_CONTAINER"), strict=False
|
|
)
|
|
if not in_rig:
|
|
return relay
|
|
|
|
if in_container:
|
|
return CONTAINER_HOST_ALIAS
|
|
|
|
if variables.get("DEPLOYMENT_MODE") == "compose" or NODE_GROUP in (
|
|
variables.get("group_names") or []
|
|
):
|
|
return LOOPBACK
|
|
|
|
managers = (variables.get("groups") or {}).get(MANAGER_GROUP) or []
|
|
return managers[0] if managers else relay
|
|
|
|
|
|
class LookupModule(LookupBase):
|
|
"""
|
|
Usage:
|
|
{{ lookup('smtp_host', email) }}
|
|
{{ lookup('smtp_host', email, in_container=true) }}
|
|
|
|
SMTP endpoint for the msmtp relay config. Takes the resolved email mapping.
|
|
Pass in_container=true when the rendered file is mounted into a container
|
|
instead of being read by the host's own msmtp.
|
|
"""
|
|
|
|
def run(self, terms, variables: dict[str, Any] | None = None, **kwargs):
|
|
if len(terms or []) != 1:
|
|
raise AnsibleError(
|
|
"smtp_host: expected exactly 1 term: lookup('smtp_host', email)"
|
|
)
|
|
email = terms[0]
|
|
if not isinstance(email, dict):
|
|
raise AnsibleError("smtp_host: the term must be the email mapping.")
|
|
variables = variables or getattr(self._templar, "available_variables", {}) or {}
|
|
in_container = _to_bool(kwargs.get("in_container", False), strict=False)
|
|
templar = getattr(self, "_templar", None)
|
|
variables = dict(variables)
|
|
for key in ("DOCKER_IN_CONTAINER", "DEPLOYMENT_MODE"):
|
|
if key in variables:
|
|
variables[key] = resolve_var(templar, variables[key])
|
|
return [resolve_smtp_host(variables, email, in_container=in_container)]
|