Files
computer-playbook/plugins/lookup/container_depends_on.py
kevinveenbirkenbachandClaude Fable 5 40e553069d refactor(structure): nest prefix-grouped sibling files into subfolders
Move files that share a delimiter-bounded name prefix within a folder (threshold 2) into a subfolder named after the prefix, dropping the prefix from the filename. Covers plugins/filter (docker, generate, has, merge, get/all), env handler subpackages, utils/roles/entity, utils/tests/swarm/write, role SQL/scripts/templates, keycloak/mailu task-includes, shared tasks/utils/load, and CI scripts. Update every import, query_file/script/include_tasks/src/lookup target, compose bind mount, and python -m path to the new locations.

Extend the prefix-grouped-files lint to threshold 2 with dir-level .nocheck and file-head nocheck opt-outs, and skip groups whose shortest member's stem equals the shared prefix. Keep flat only where a loader forbids nesting: WordPress mu-plugins and the docs/requirements namespace. Ansible filter discovery is recursive, so nested filters stay Jinja-resolvable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 20:00:03 +02:00

111 lines
3.8 KiB
Python

from __future__ import annotations
import contextlib
import textwrap
from collections.abc import Mapping
from typing import Any
from ansible.errors import AnsibleError
from ansible.plugins.loader import lookup_loader
from ansible.plugins.lookup import LookupBase
from plugins.filter.docker.service_enabled import (
FilterModule as _DockerServiceEnabledFilter,
)
from utils.cache.yaml import dump_yaml_str
from utils.roles.applications.services.database import (
get_database_service_config,
resolve_database_service_key,
)
from utils.roles.applications.services.sso import get_sso_config
def _resolve_local_database_host(
applications: dict[str, Any], application_id: str
) -> str | None:
try:
dbtype = resolve_database_service_key(applications, application_id)
except ValueError as exc:
raise AnsibleError(f"container_depends_on: {exc}") from exc
if not dbtype:
return None
cfg = get_database_service_config(applications, application_id)
if not bool(cfg.get("enabled", False)):
return None
if bool(cfg.get("shared", False)):
return None
return "database"
class LookupModule(LookupBase):
def run(
self,
terms: list[Any],
variables: dict[str, Any] | None = None,
**kwargs: Any,
) -> list[str]:
if not terms or len(terms) != 1:
raise AnsibleError(
"container_depends_on: expected exactly 1 positional term "
"(application_id)"
)
application_id = str(terms[0]).strip()
if not application_id:
raise AnsibleError(
"container_depends_on: 'application_id' must be a non-empty string"
)
vars_ = variables or getattr(self._templar, "available_variables", {}) or {}
templar = getattr(self, "_templar", None)
applications = lookup_loader.get(
"applications", loader=self._loader, templar=templar
).run([], variables=vars_)[0]
if application_id not in applications:
raise AnsibleError(
f"container_depends_on: unknown application_id '{application_id}'"
)
entries: dict[str, dict[str, str]] = {}
db_host = _resolve_local_database_host(applications, application_id)
if db_host:
entries[db_host] = {"condition": "service_healthy"}
redis_cfg = applications[application_id].get("services", {}).get("redis", {})
redis_local = _DockerServiceEnabledFilter.is_docker_service_enabled(
applications, application_id, "redis"
) and not bool(redis_cfg.get("shared", False))
redis_enabled = (
redis_local
or get_sso_config(applications, application_id)["is_proxy_gated"]
)
if redis_enabled:
entries["redis"] = {"condition": "service_healthy"}
extra = kwargs.get("extra")
if extra:
if not isinstance(extra, Mapping):
raise AnsibleError("container_depends_on: 'extra' must be a mapping")
for name, body in extra.items():
if isinstance(body, Mapping):
entries[str(name)] = {str(k): str(v) for k, v in body.items()}
if not entries:
return [""]
indent = int(kwargs.get("indent", 0))
raw_mode = vars_.get("DEPLOYMENT_MODE", "compose")
if templar is not None:
with contextlib.suppress(Exception):
raw_mode = templar.template(raw_mode)
if str(raw_mode).strip() == "swarm":
payload: dict[str, object] = {"depends_on": list(entries.keys())}
else:
payload = {"depends_on": entries}
body = dump_yaml_str(payload).rstrip()
if indent <= 0:
return [body]
return [textwrap.indent(body, " " * indent)]