81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
"""CER-P5-010: env validation policy — fail-fast per mandatory, soft per optional.
|
|
|
|
Usage al boot di ogni mcp `__main__.py`:
|
|
|
|
from option_mcp_common.env_validation import require_env, optional_env, summarize
|
|
|
|
creds_file = require_env("CREDENTIALS_FILE", "deribit credentials JSON path")
|
|
host = optional_env("HOST", default="0.0.0.0")
|
|
summarize(["CREDENTIALS_FILE", "HOST", "PORT"])
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MissingEnvError(RuntimeError):
|
|
"""Mandatory env var absent or empty."""
|
|
|
|
|
|
def require_env(name: str, description: str = "") -> str:
|
|
"""Fail-fast: raise MissingEnvError se name non presente o vuoto.
|
|
|
|
Uscita dal processo con codice 2 se chiamato dal main(). Comporta
|
|
logging chiaro del missing var prima dell'exit.
|
|
"""
|
|
val = (os.environ.get(name) or "").strip()
|
|
if not val:
|
|
msg = f"missing mandatory env var: {name}"
|
|
if description:
|
|
msg += f" ({description})"
|
|
logger.error(msg)
|
|
raise MissingEnvError(msg)
|
|
return val
|
|
|
|
|
|
def optional_env(name: str, *, default: str = "") -> str:
|
|
"""Soft: ritorna env o default. Log INFO se default usato."""
|
|
val = (os.environ.get(name) or "").strip()
|
|
if not val:
|
|
if default:
|
|
logger.info("env %s not set, using default=%r", name, default)
|
|
return default
|
|
return val
|
|
|
|
|
|
def summarize(names: list[str]) -> None:
|
|
"""Log INFO di tutti gli env rilevanti con presenza (mask se SECRET/KEY/TOKEN)."""
|
|
sensitive_tokens = ("SECRET", "KEY", "TOKEN", "PASSWORD", "CREDENTIAL", "WALLET")
|
|
for n in names:
|
|
val = os.environ.get(n)
|
|
if val is None:
|
|
logger.info("env[%s]: <unset>", n)
|
|
continue
|
|
if any(t in n.upper() for t in sensitive_tokens):
|
|
logger.info("env[%s]: <set, %d chars>", n, len(val))
|
|
else:
|
|
logger.info("env[%s]: %s", n, val)
|
|
|
|
|
|
def fail_fast_if_missing(names: list[str]) -> None:
|
|
"""Verifica lista di nomi mandatory al boot. Exit 2 se uno solo manca.
|
|
|
|
Uso preferito: early call in main() per bloccare boot se config incompleta.
|
|
"""
|
|
missing: list[str] = []
|
|
for n in names:
|
|
if not (os.environ.get(n) or "").strip():
|
|
missing.append(n)
|
|
if missing:
|
|
logger.error("boot aborted: missing mandatory env vars: %s", missing)
|
|
print(
|
|
f"FATAL: missing mandatory env vars: {missing}",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(2)
|