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

DSL

Soul Stack описывается тремя видами артефактов на YAML. Все они типизированы; выражения внутри — на CEL, рендер файлов — Go text/template.

АртефактЧто описываетГде живёт
DestinyЖелаемое состояние одного хоста — набор идемпотентных шагов.git (репозиторий destiny)
ScenarioDestiny плюс оркестрация: на каких хостах, в каком порядке, с какими условиями.git (репозиторий сервиса)
EssenceПараметры и секреты, привязанные к хосту или группе.git (дефолты) + база (переопределения в spec)
  • Destiny — атомарный кирпичик: «как привести один хост в нужное состояние». Изолирован — видит только свой объявленный вход.
  • Scenario — операция над целым кластером: «как провести create / add_user / restart над набором хостов». Сценарий получает весь набор блоков задач Destiny плюс оркестрационную дельту (на каких хостах, в каком порядке) и может вызывать Destiny через apply:.
  • Essence — значения, которые подкладываются при рендере: настройки приложения, пароли, ключи. Сценарий видит Essence напрямую; Destiny получает значения только через явный проброс на входе.

Граница между Destiny и Scenario — рекомендация, не жёсткая стена: переиспользуемое, критичное или изолируемое выносится в отдельный Destiny; одноразовая логика конкретной операции допустима инлайн в сценарии.

В Soul Stack два движка выражений, и граница между ними проходит строго по файлу:

КонтекстДвижокМаркер
Выражения в YAML: условия (when:, where:, …)CELвся строка = выражение, без обёртки
Интерполяция значений внутри строк YAMLCEL${ … }
Файлы шаблонов с расширением .tmplGo text/template{{ … }}

В одном файле работает только один движок: CEL никогда не выполняется внутри .tmpl, text/template никогда не выполняется внутри .yml. Это не пересечение, а последовательная передача данных — CEL вычисляет значения в YAML, а затем явно поднятые значения попадают в рендер файла.

CEL обслуживает все выражения в YAML-артефактах. Две позиции:

  • Условные ключи (when:, where:, changed_when:, failed_when:, until:) — вся строка трактуется как CEL-выражение, без обёртки:

    when: input.do_restart
    where: register.role.stdout == "master"
  • Интерполяция в строковых значениях (params:, vars:, проброс на вход) — выражение оборачивается в ${ … }:

    params:
    command: "redis-cli replicaof ${ register.master.stdout } 6379"
    replicas: "${ input.replicas * 2 }"

CEL — это движок выражений без побочных эффектов: у него нет доступа к файловой системе, командам и произвольной сети. В выражениях доступны факты хоста (soulprint.self.<путь>), параметры (input.*, essence.*), результаты предыдущих шагов (register.*) и набор встроенных функций (проверки, размер, регулярки, работа со временем).

Файлы шаблонов с расширением .tmpl рендерятся движком Go text/template. Это единственное место, где он работает, и используется он только специальным шагом, который рендерит файл на хосте.

Рендер устроен безопасно по умолчанию: строгий режим (обращение к несуществующему полю — ошибка, а не пустая строка), закрытый список разрешённых функций (без выполнения команд, чтения окружения и генерации случайностей) и изолированный контекст (шаблон видит только явно поднятые значения, а не весь контекст сценария).

Секреты резолвятся через Vault на стороне сервера при рендере. Движок выражений обрабатывает их как обычные значения — иначе нельзя было бы легитимно передать секрет в параметр. Маскирование применяется на выходе: в логах, трейсах, ответах API и отчётах о прогоне секреты в открытом виде не появляются.

Артефакты можно проверить до применения офлайн-линтером soul-lint: он разбирает YAML, сверяет имена модулей и их состояний, проверяет схемы параметров и выражения — на рабочей станции или в CI, ничего не исполняя на хостах.