Блог

pexels-ruandom-zhmall-ong-7779878

Нещодавно ми зіткнулися з викликом: зробити дуже складне розгортання, визначене в SaltStack, більш зручним. Метою було знайти спосіб передати додатковий параметр у команду state.apply, щоб керувати тим, чи виконується повне розгортання, чи швидке. Скорочена версія історії — ми зрештою прочитали вихідний код Salt і таки знайшли рішення.

Що ускладнило задачу ще більше: нам потрібно було отримати значення додаткового параметра всередині Jinja-коду в Pillar, причому цей Pillar не був напряму прикріплений до міньйона, а зчитувався через команду import з іншого Pillar.

Ось перелік всіх підходів, які не спрацювали (і чому), а також працююче рішення наприкінці.

Що ми пробували (і чому це не працює)

  1. Передача додаткового Pillar через командний рядок
    state.apply app.deploy pillar='{fastdeploy: True}'

    Це перше, що спадає на думку. Чудово працює, якщо ми можемо обробити умови всередині Jinja-коду стану.
    Але не працює, якщо потрібно обробити умови в Jinja-коді Pillar, бо передані через pillar аргументи ще недоступні на момент обробки Pillar у sls.

  2. Використання параметра localconfig
    state.apply app.deploy localconfig=config.yaml

    Звучить перспективно згідно з документацією. Але насправді нічого не змінює — читання вихідного коду Salt підтвердило це.

  3. Використання параметра pillarenv
    Просто для повноти картини. Але цей підхід не підходить для нашого випадку.
  4. Використання Pillar Stack
    Теоретично можливо, адже ця функція дозволяє читати додатковий Pillar у Pillar.
    Але такий підхід не дає можливості напряму передати додатковий параметр у state.apply.
  5. Отримання argv
    Salt state.apply ігнорує додаткові parameter=value, якщо вони йому незнайомі.
    Тому можна викликати:

    state.apply app.deploy fastdeploy=True

    Помилки не буде. А значення можна отримати всередині Jinja-коду в Pillar:

    {% set fastdeploy = "fastdeploy=True" in opts.get("argv", []) or "fastdeploy=true" in opts.get("argv", []) %}

    або

    {% set fastdeploy = "fastdeploy=True" in salt['config.get']('argv') or "fastdeploy=true" in salt['config.get']('argv') %}

    Цей підхід виглядав перспективно, але теж провалився.
    Значення argv доступні під час обчислення значень у Pillar, що дозволяє, наприклад, передавати версію PHP.
    Але argv ще не доступний, коли формується сторінка Pillar за допомогою import.

  6. Використання першого Pillar, прикріпленого через top.sls
    • Встановити прапорець десь (наприклад, у тимчасовий файл).
    • Прочитати його у наступному Pillar, прикріпленому через top.sls.
      Це працює, але виглядає “брудно”.

Рішення: використання grains у roster файлі

Оскільки ми використовуємо salt-ssh для виклику розгортання, у нас є roster-файл (/etc/salt/roster).
До того ж, ми використовуємо Docker, і виклик salt-ssh зазвичай додається до команди-обгортки, наприклад:

./.docker_run.sh salt-ssh --wipe minion state.apply app.deploy

Ми модифікували entrypoint.sh усередині Docker і код, що генерує roster-файл.
Тепер ми можемо викликати:

./.docker_run.sh salt-ssh --wipe minion state.apply app.deploy grains="{fastdeploy: True}"

І отримати значення всередині Jinja-коду в Pillar:

{% if grains.get("fastdeploy", False) %}

І це працює навіть під час побудови Pillar через import!

Додаткові можливості

Ми використовуємо yq для обробки параметра grains=, що дозволяє передавати складні структури:

grains='{a: b, b: {c: d, e: f}, fastdeploy: True}'

Це працює так само, як передача додаткових Pillar (стандартна функція Salt).