Условия и циклы

Тема дорожной карты · SaltStack

Условия {% if %} и циклы {% for %} — управляющие конструкции Jinja, которые превращают SLS-файл из статичного списка состояний в генератор: один шаблон разворачивается в разный YAML в зависимости от миньона и данных. Условия обычно ветвятся по grains (дистрибутив, версия, роль машины) и pillar (включена ли фича, какое окружение), циклы — разворачивают списки и словари из pillar в наборы однотипных состояний: пользователи, виртуальные хосты, точки монтирования. Важно держать в голове масштаб: это не логика времени исполнения, а логика времени рендеринга. К моменту, когда Salt начинает применять состояния, всех if и for уже нет — остался только сгенерированный ими YAML.

Как это работает

Условие ветвит текст шаблона:

{% if grains['os_family'] == 'RedHat' %}
{% set svc = 'httpd' %}
{% elif grains['os_family'] == 'Debian' %}
{% set svc = 'apache2' %}
{% else %}
{% set svc = 'apache' %}
{% endif %}

Цикл по словарю из pillar генерирует состояния — ID каждого должен быть уникален, поэтому в него включают переменную цикла:

{% for site, cfg in salt['pillar.get']('vhosts', {}).items() %}
vhost_{{ site }}:
  file.managed:
    - name: /etc/nginx/conf.d/{{ site }}.conf
    - source: salt://nginx/files/vhost.conf.jinja
    - template: jinja
{% endfor %}

Дефисы в тегах — {%- if ... -%} — съедают пробелы и переводы строк вокруг тега; без них циклы оставляют в результате пустые строки. Обычно это безвредно для YAML, но затрудняет чтение вывода slsutil.renderer. Глобально поведение регулируется опциями jinja_trim_blocks и jinja_lstrip_blocks в конфигурации, но по умолчанию они выключены, и менять их для общих формул рискованно — шаблоны, написанные под другие настройки, начнут рендериться иначе.

Когда применять

Ветвление по grains['os_family'] — правильный уровень общности: семейство покрывает целую линейку дистрибутивов, тогда как проверка точного grains['os'] быстро превращается в лестницу из elif. Циклы по pillar — стандартный способ отдать управление данными: добавление нового виртуального хоста становится правкой pillar, а не SLS-файла. Проверка «есть ли данные» перед циклом не нужна, если использовать pillar.get с пустым дефолтом ({} или []) — цикл по пустой коллекции просто ничего не сгенерирует. А вот когда условия начинают определять десятки значений (пакет, сервис, путь, владелец — и всё по ОС), это признак, что пора переходить на паттерн map.jinja и держать данные словарём, а не веткой кода.

Типичные ошибки

Главная — дублирующиеся ID состояний в цикле: если ID не включает переменную итерации, каждая следующая итерация затирает предыдущую, и применяется только последняя — без ошибок, молча. Вторая — сравнение с неверным регистром или значением grain: grains['os'] возвращает, например, Ubuntu, а не ubuntu; при сомнении проверьте реальное значение через salt '*' grains.item os os_family. Третья — вложенные циклы и условия на три-четыре уровня: такой шаблон невозможно ревьюить, а ошибка отступа в глубине даёт YAML-ошибку со смещённым номером строки. Четвёртая — логика, зависящая от порядка итерации по словарю, когда конфиг собирается из фрагментов: делайте результат нечувствительным к порядку или сортируйте явно.

Связанные понятия

Полезные ресурсы

Проверить знания (2)

Загрузка вопросов…