controller: ${VAR} env interpolation в YAML config (для secrets)
Config.from_yaml теперь recursively expands ${VAR} и ${VAR:-default}
в string values через os.environ. Позволяет хранить tokens / passwords
в gitignored .env, passed контейнеру через compose env section:
controller.yaml:
extra_http_headers:
Authorization: "Bearer ${GRAFANA_TOKEN}"
.env (gitignored):
GRAFANA_TOKEN=glsa_xxx
docker-compose.override.yml controller:
environment:
GRAFANA_TOKEN: "${GRAFANA_TOKEN:-}" # compose interpolates от .env
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -28,13 +28,33 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Self
|
||||
from typing import Any, Self
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
_ENV_PATTERN = re.compile(r"\$\{(\w+)(?::-([^}]*))?\}")
|
||||
|
||||
|
||||
def _expand_env(obj: Any) -> Any:
|
||||
"""Recursively replace ${VAR} and ${VAR:-default} в string values
|
||||
через os.environ. Используется при load YAML config — secrets вроде
|
||||
API tokens хранятся в .env (gitignored) и interpolate'ятся при start."""
|
||||
if isinstance(obj, str):
|
||||
return _ENV_PATTERN.sub(
|
||||
lambda m: os.environ.get(m.group(1), m.group(2) or ""),
|
||||
obj,
|
||||
)
|
||||
if isinstance(obj, dict):
|
||||
return {k: _expand_env(v) for k, v in obj.items()}
|
||||
if isinstance(obj, list):
|
||||
return [_expand_env(v) for v in obj]
|
||||
return obj
|
||||
|
||||
|
||||
class BrokerCfg(BaseModel):
|
||||
host: str = "localhost"
|
||||
port: int = 1883
|
||||
@@ -202,4 +222,5 @@ class Config(BaseModel):
|
||||
def from_yaml(cls, path: Path | str) -> Self:
|
||||
with open(path) as f:
|
||||
data = yaml.safe_load(f) or {}
|
||||
data = _expand_env(data)
|
||||
return cls.model_validate(data)
|
||||
|
||||
Reference in New Issue
Block a user