Расширение для стандартных модулей управления конфигурациями в Go

от автора

Всем доброго времени суток! Имея за плечами многолетний опыт разработки в Java, а точнее в Spring Framework и начав разрабатывать на языке Go в промышленных масштабах, я стал сталкиваться с такой проблемой, что мне действительно не хватает многих фишек из Spring’a. И одна из этих проблем: указание переменной среды в качестве параметра в конфигурационных yaml-файлах

Для автоматизации деплоя, использования различных правил сборки проблема вставала все острее и острее. Я поисследовал различные модули и библиотеки. Нашел несколько интересных решений в cleanenv и даже в viper, но пришел к выводу: «а почему бы не сделать что-то свое?!» Сказано — сделано.

Проблема

Задача стояла следующим образом: я не знаю какую библиотеку я буду использовать в будущем, может быть напишу свою, может на рынке появится что-то более интересное. Но библиотека должна быть отделимой и работать в любой момент времени при наличии только двух сущностей: структуры и названия переменной в структуре. Можно обойтись просто кодом.

Решение

У нас есть некий конфиг для удобства. Называется local.yaml. Представляет собой простой yaml-файл с набором переменных. Все переменные окружения прописываются через специальные символы ${MY_VAR}. Прямо как в Spring’e!

properties:   host: ${SERVER_HOST}   port: ${SERVER_PORT}    routes:     - name: Host1       target: ${HOST1_TARGET}     - name: Host2       target: ${HOST2_TARGET}

Теперь понятно как будут выглядеть структуры

type ( ApiServer struct { Host   string  `mapstructure:"host"` Port   string  `mapstructure:"port"` Routes []Route `mapstructure:"routes"` }  Route struct { Name   string `mapstructure:"name"` Target string `mapstructure:"target"` } )

Ну и осталось дело за малым. Проинициализировать структуры нашим файлом конфигураций и прикрепить разработанное расширение. Инициализировать будем viper’ом, но на самом деле это не имеет никакого значения. Приблизительно, не вдаваясь в подробности инициализации viper, это будет выглядеть как-то так

if err := viper.ReadInConfig(); err != nil { log.Fatalf("could not load configuration: %v", err) }  viper.AutomaticEnv()  config := &ApiServer{}  if err := viper.UnmarshalKey("properties", config); err != nil { panic(err) }

Самое главное тут получить проинциализированный config переменную, которая является нашей структурой к которой мы будем прикручивать уже переменные среды. А это, как показывает код очень и очень просто

import (   gobindenv "github.com/dissdoc/go-bindenv" )  // Инициализируем переменные gobindenv.BindEnv(config)  // И теперь делаем что хотим fmt.Println(config.Host, config.Port)

О расширении пару слов

Называется данное расширение go-bindenv. Устанавливается на ваш вкус, как хотите, как пример. За собой не тянет дополнительных модулей. Все работает максимально «экологично» 😉

go get github.com/dissdoc/go-bindenv@v0.1.0

Изначально то, что реализовано сейчас — мне хватает более чем. Но если, на ваш взгляд, чего-то недостает, я готов выслушать и реализовать.

Библиотека содержит в себе несколько бизнес-слоев, каждый отвечает за свой функционал, чтобы в дальнейшем было проще расширять.

readenv.go — содержит функционал чтения переменных среды. В случае, если переменная определена в конфиге, но не передано значение — вызывается panic

rule.go — одна из возможных интерпретаций определения переменных. В моем случае я пользуюсь только регулярными выражениями

wrapper.go — рефлексия определяющая в каком типе переменных определено правило и на основе этого инициализирует данную переменную значением

В заключении

Данный функционал мне помогает сократить количество шагов для деплоя, а так же немного поубирать лишний код. С другой стороны, хочется улучшать и расширять функционал данного модуля, но не хочется из него делать очередной швейцарский нож. Надеюсь, что данный модуль будет вам полезен!

P.S. никаких телеграм-каналов, блогов не веду. Делаю все в свое удовольствие. До новых встреч!

UPD: основная задача данного расширения — развязать зависимости кода от конфигурации в части чтения переменных. Теперь, в случае изменения названия переменной — сам код приложения переписывать не нужно


ссылка на оригинал статьи https://habr.com/ru/articles/776284/