Files
computer-playbook/utils/domains/list.py
T

138 lines
3.8 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import Any
import yaml
PROJECT_ROOT = Path(__file__).resolve().parents[2]
ROLES_DIR = PROJECT_ROOT / "roles"
def _render_ansible_strict(
*, raw: str, var_name: str, variables: dict[str, Any]
) -> str:
from utils.templating import render_ansible_strict
return render_ansible_strict(
templar=None,
raw=raw,
var_name=var_name,
err_prefix="domains.list",
variables=variables,
)
def _build_domain_index(
applications: dict[str, Any], include_aliases: bool = True
) -> dict[str, str]:
from utils.domains.application_domain_index import build_domain_index
return build_domain_index(applications, include_aliases=include_aliases)
def load_yaml_mapping(path: Path) -> dict[str, Any]:
if not path.exists():
return {}
data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
if not isinstance(data, dict):
raise ValueError(f"{path} must contain a YAML mapping")
return data
def render_domain_value(value: Any, variables: dict[str, Any], field_name: str) -> Any:
if isinstance(value, str):
return _render_ansible_strict(
raw=value,
var_name=field_name,
variables=variables,
)
if isinstance(value, list):
return [
render_domain_value(item, variables, f"{field_name}[{idx}]")
for idx, item in enumerate(value)
]
if isinstance(value, dict):
return {
key: render_domain_value(item, variables, f"{field_name}.{key}")
for key, item in value.items()
}
return value
def build_applications_from_roles(
roles_dir: Path, domain_primary: str
) -> dict[str, dict[str, Any]]:
variables = {"DOMAIN_PRIMARY": domain_primary}
applications: dict[str, dict[str, Any]] = {}
for role_dir in sorted(roles_dir.iterdir()):
vars_main = role_dir / "vars" / "main.yml"
config_main = role_dir / "config" / "main.yml"
if not vars_main.exists() or not config_main.exists():
continue
vars_data = load_yaml_mapping(vars_main)
application_id = vars_data.get("application_id")
if not isinstance(application_id, str) or not application_id.strip():
continue
config_data = load_yaml_mapping(config_main)
server = config_data.get("server")
if not isinstance(server, dict):
continue
domains = server.get("domains")
if not isinstance(domains, dict):
continue
applications[application_id] = {
"server": {
"domains": render_domain_value(
domains,
variables,
f"{application_id}.server.domains",
)
}
}
return applications
def list_derived_domains(domain_primary: str) -> list[str]:
primary = str(domain_primary).strip().lower()
if not primary:
return []
# Keep non-role domains that are part of the stack contract in one place.
return [f"test.{primary}"]
def add_www_variants(domains: list[str]) -> list[str]:
expanded = set(domains)
for domain in list(expanded):
if not domain.startswith("www."):
expanded.add(f"www.{domain}")
return sorted(expanded)
def list_application_domains(
domain_primary: str,
*,
include_aliases: bool = False,
include_www: bool = False,
) -> list[str]:
applications = build_applications_from_roles(ROLES_DIR, domain_primary)
domains = set(
_build_domain_index(applications, include_aliases=include_aliases).keys()
)
domains.update(list_derived_domains(domain_primary))
sorted_domains = sorted(domains)
if include_www:
return add_www_variants(sorted_domains)
return sorted_domains