- 13 конвенций по осям arch / lang / stack / common; репозитории берут оттуда копии в свой docs/conventions/ и коммитят их у себя - conv — синхронизация копий: add / status / diff / pull / push, локальные регионы исключены из сравнения, поэтому расхождение не даёт шума
4.4 KiB
status, extends
| status | extends |
|---|---|
| рекомендуемая | arch/config.md |
Конфигурация: реализация на Go
Как arch/config.md выглядит в Go-приложении.
Формат и загрузчик
- TOML. Разбор и валидация — целиком в
internal/config; наружу отдаётся готовая структураConfig. - Одна корневая структура
Configс под-структурами по секциям — имена структур совпадают с именами секций, чтобы конфиг и код читались рядом. - Умолчания — в
Default(), поверх накладывается разобранный файл. - Флаг
--config=pathпереопределяет путь; по умолчаниюconfig.tomlв рабочей директории, образец —config.example.toml.
Длительности
time.Duration не разбирается из строки TOML сама по себе — нужен свой тип
с UnmarshalText, отдающий time.Duration:
type Duration time.Duration
func (d *Duration) UnmarshalText(b []byte) error { … }
func (d Duration) Std() time.Duration { … }
Так в конфиге видна единица измерения (poll_interval = "5s"), а не голое
число. Цена: ошибка в длительности всплывает на разборе TOML, до общей
валидации, поэтому в общий сбор проблем она не попадает — про неё узнаёшь
отдельно и первой.
Чтение окружения
Приложение не читает окружение для конфигурации. Механизируется
forbidigo, и паттерн должен покрывать все входы, а не только
os.Getenv:
^os\.(Getenv|LookupEnv|Environ|ExpandEnv)$
Правило про приложение, поэтому за его границей запрет не действует:
- тесты — не приложение: интеграционному тесту нормально брать креды внешнего сервиса из окружения;
- переменные рантайма — те, что читает не наш код, а Go или ОС
(
GOMEMLIMIT,GOMAXPROCS,GODEBUG,TZ).
Отдельный случай — переменные, которые читает стандартная библиотека от
имени приложения: дефолтный http.Transport уважает
HTTP_PROXY/HTTPS_PROXY. Формально их читает не наш код, но это
конфигурация поведения приложения, поэтому прокси задаётся полем конфига и
явным Transport, а не окружением.
Валидация
- Проверки собираются
errors.Join, чтобы за один запуск показать все проблемы конфига, а не первую. - IANA-зона валидируется
time.LoadLocation. База зон встраивается импортом_ "time/tzdata"вmain, а не в библиотечном пакете: иначе ~450 КБ zoneinfo навязываются каждому импортёру. Со встроенной базой ошибкаLoadLocationозначает битое имя зоны, а не отсутствие zoneinfo в контейнере. - Невалидный конфиг —
slogуровняERRORиos.Exit(1)изmain, до старта серверов и воркеров.
Секреты
Go-специфики нет: секреты приходят из деплоя уже в файле, проверка их непустоты идёт вместе с остальной валидацией — см. базу.
Связано
lang/go/time.md— зона отображения и формат времени.lang/go/logging.md—slog, которым падает невалидный конфиг.