Files
PacketStation/server/packetstation/guard.py
T

63 lines
2.2 KiB
Python
Raw Normal View History

"""Startup guards: the VISUAL target must match STACK, and insecure LDAP is never silent."""
from __future__ import annotations
import logging
from dataclasses import dataclass
from typing import Any
from .settings import Settings
log = logging.getLogger(__name__)
_TARGET_FIELDS = {"server", "database", "profile"}
class StackGuardError(Exception):
"""The configuration must not start."""
@dataclass
class GuardResult:
target: str # e.g. "VSQL\DEV / PESCOINCDEV (DEV)"
production: bool
explicit: bool
warnings: list[str]
def load_visual_settings() -> Any:
"""visualkit's own Settings (VISUAL_* variables). Tests monkeypatch this."""
from visualkit.config import Settings as VisualSettings
return VisualSettings()
def check_stack(settings: Settings, visual: Any | None = None) -> GuardResult:
"""Raise StackGuardError when STACK and the VISUAL target disagree."""
if visual is None:
visual = load_visual_settings()
production = bool(visual.is_production)
explicit = bool(_TARGET_FIELDS & set(getattr(visual, "model_fields_set", set())))
target = f"{visual.server} / {visual.database} ({'PRODUCTION' if production else 'DEV'})"
warnings: list[str] = []
if settings.stack != "prod" and production:
raise StackGuardError(f"STACK={settings.stack} but the VISUAL target is production: {target}")
if settings.stack == "prod" and not production:
if not explicit:
raise StackGuardError(
"STACK=prod but no VISUAL target is set (VisualKit would default to DEV); "
"set VISUAL_PROFILE=prod or VISUAL_SERVER and VISUAL_DATABASE"
)
warnings.append(f"STACK=prod reads a non-production VISUAL target: {target}")
if settings.ldap_allow_insecure:
warnings.append(f"LDAP_ALLOW_INSECURE=true on STACK={settings.stack}: LDAP may run without TLS")
return GuardResult(target, production, explicit, warnings)
def startup_checks(settings: Settings) -> GuardResult:
"""Run at app and worker start: raise on a bad stack, log every warning."""
result = check_stack(settings)
for w in result.warnings:
log.warning(w)
return result