Перейти к основному содержимому

Источники конфигурации

ConfigSource — абстракция над местом, откуда читается конфигурация. В Phase 1 основной источник — YamlFileSource (файлы на диске), но API спроектирован под расширение. В Phase 2+ появятся адаптеры к централизованным хранилищам конфигурации:

ИсточникПримерСтатус
YamlFileSourceapp-config.yaml на дискеPhase 1 (по умолчанию)
JsonFileSourceapp-config.json на дискеPhase 1
InMemorySource (Python/TS) / DictSource (Go)словарь, собранный в коде — для тестовPhase 1
Интерполяция env ${VAR}читает окружение во время обработки сырого текстаPhase 1 (встроена, не отдельный источник)
EtcdSourceключи в etcd под префиксомPhase 2 (в плане)
ConsulSourceKV-хранилище в ConsulPhase 2 (в плане)
VaultSourceсекреты в HashiCorp VaultPhase 2 (в плане)
HttpSourceREST-эндпоинт, отдающий JSONPhase 2 (в плане)
SqlSourceтаблица в БД с конфигурацией, меняющейся в рантаймеPhase 2 (в плане)
KubernetesSourceConfigMap + Secret в KubernetesPhase 2 (в плане)

Интерфейс

Каждый источник реализует минимальный контракт:

id: string # уникальный идентификатор источника (например, "yaml:/app/config.yaml")
load(): ConfigTree # одноразовая загрузка (обязательно)
watch?(callback): Subscription # подписка на изменения (опционально)
close?(): void # освобождение ресурсов (опционально)

load() возвращает ConfigTree — дерево значений, которое загрузчик дальше сливает с другими источниками и прогоняет через интерполяцию env.

watch(callback) опционален. Если источник умеет следить за изменениями (файл через fsnotify; etcd через gRPC-стрим; Kubernetes через informer), загрузчик передаёт callback, который вызывается при обнаружении изменения. Если источник этого не умеет, метод опускается; подписки на изменения всё равно регистрируются, но активируются с active = false.

Сценарий по умолчанию — YamlFileSource

Для большинства dagstack-приложений Config.load(path) — это высокоуровневая обёртка над Config.loadFrom([YamlFileSource(path), ...]). Она автоматически находит и подключает три слоя:

  1. app-config.yaml — база (коммитится).
  2. app-config.local.yaml — переопределения разработчика (gitignored).
  3. app-config.${DAGSTACK_ENV}.yaml — переопределения окружения (коммитится).

Механика слоёв описана на странице Слои.

Явное перечисление источников

Когда нужен нестандартный порядок (например, в тестах) или микс типов источников, используй Config.loadFrom:

from dagstack.config import Config, YamlFileSource, InMemorySource

config = Config.load_from([
YamlFileSource("app-config.yaml"),
InMemorySource({"database": {"pool_size": 5}}), # тестовое переопределение
])

:::note Имя in-memory-источника В Python и TypeScript источник называется InMemorySource; в Go — DictSource. Это историческое расхождение между биндингами; семантика идентична (дерево в памяти, без интерполяции по умолчанию). :::

Порядок аргументов = порядок приоритета. У первого источника наименьший приоритет; у последнего — наибольший (он переопределяет более ранние).

Подстановка переменных окружения

Env-переменные не отдельный ConfigSource, а этап обработки значений в загруженном ConfigTree. Строковые значения вида ${VAR} или ${VAR:-default} интерполируются сразу после source.load(), до слияния с другими источниками.

Подробности — на странице Подстановка переменных окружения.

Семантика watch и горячая перезагрузка

Когда приложение подписывается на изменения своей секции через config.onSectionChange(path, callback), загрузчик регистрирует наблюдателя на каждом источнике, который умеет следить за изменениями:

  1. На каждом источнике регистрируется source.watch(on_source_change).
  2. Любое изменение в источнике → загрузчик пересобирает дерево.
  3. Новая версия валидируется (если у подписанных секций есть схема).
  4. Если валидация прошла, подписчики получают новые значения.
  5. Если валидация провалена, вся перезагрузка откатывается; подписчики не уведомляются (атомарный откат).

Если ни один источник не умеет следить за изменениями, каждая подписка регистрируется с active = false. Это не ошибка: приложение всё равно успешно стартует без горячей перезагрузки.

Подробности — Горячая перезагрузка (watch).

Реализация собственного ConfigSource

Контракт v1.0 фиксирует сигнатуры, но не формат хранения. Если нужен кастомный источник (например, ZooKeeper), реализуй интерфейс и передай экземпляр в Config.loadFrom:

from dagstack.config import ConfigSource, ConfigTree, Subscription

class ZookeeperSource(ConfigSource):
id = "zookeeper://zk.example.com/my-app"

def __init__(self, host: str, prefix: str):
self._client = zk_connect(host)
self._prefix = prefix

def load(self) -> ConfigTree:
raw = self._client.get_recursive(self._prefix)
return ConfigTree.from_dict(raw)

def watch(self, callback) -> Subscription:
# callback(event) при изменении дерева
...

def close(self) -> None:
self._client.close()

См. также