Вложенные модели конфига¶
В реальных сервисах конфиг не плоский. Есть DB-секция, cache-секция,
секция API-ключей. vaultly поддерживает вложение SecretModel
напрямую.
from vaultly import Secret, SecretModel
class DbConfig(SecretModel):
password: str = Secret("/{stage}/db/password")
pool_size: int = Secret("/{stage}/db/pool_size")
class CacheConfig(SecretModel):
redis_url: str = Secret("/{stage}/cache/url")
class App(SecretModel):
stage: str
db: DbConfig
cache: CacheConfig
openai_key: str = Secret("/services/openai/key")
Конструирование¶
Вложенных детей можно передавать как dict (стандартный паттерн Pydantic):
Pydantic-валидатор примет dict и сконструирует DbConfig /
CacheConfig. После этого model_validator(mode='after') от vaultly
привяжет детей к корню.
Можно и предконструировать:
Дитя, созданное так без родительского контекста, откладывает валидацию
пути — vaultly видит, что DbConfig() отдельно не имеет поля
{stage}, и оставляет резолюцию тому, кто его обернёт.
Что вложенные дети получают от корня¶
При привязке к корню дитя начинает использовать три его вещи:
- Бэкенд — берётся только корневой
backend=. Полеbackendу ребёнка игнорируется после привязки. - Кэш —
config.db.refresh("password")чистит ту же запись, что иconfig.refresh(...)для того же пути. - Контекст пути —
{stage}вDbConfigрезолвится из поляstageкорня, а не из самогоDbConfig.
Refresh из любой точки дерева¶
refresh и refresh_all ходят по тому же общему кэшу, откуда бы их
ни вызвать:
# Эквивалентно: оба чистят /prod/db/password.
config.db.refresh("password")
config.db._effective_root().refresh_all() # очищает весь кэш
Большинство сервисов после ротации просто вызывают
config.refresh_all().
Path-валидация по всему дереву¶
validate="paths" обходит всё дерево при конструировании. Опечатка в
любом вложенном поле всплывает сразу, с указанием конкретного поля:
class BadDb(SecretModel):
password: str = Secret("/{stge}/password") # опечатка
class App(SecretModel):
stage: str
db: BadDb
App(stage="prod", db={}, backend=...)
# > MissingContextVariableError: secret field BadDb.password references {stge},
# but no such field exists on the root model
Ограничения¶
- Циклы между моделями не поддерживаются (Pydantic откажется такое конструировать в любом случае).
- Списки или dict из
SecretModelспециально не поддерживаются — для vaultly это просто Pydantic-типы. Не кладитеlist[SecretModel]в модель в надежде, что секреты каждого элемента будут делить кэш родителя. Используйте однуSecretModelна один логический инстанс. - Не делайте
model_copyна ребёнке, чтобы переиспользовать его в другом дереве. Копирование заблокировано как раз потому, что кэш и связи_rootстали бы неоднозначными. Создавайте свежий экземпляр.