Шаблон Ansible Jinja2 dict в dict

Я хочу создать шаблоны конфигураций php.ini для apache2 и cli. Я отфильтровал параметры по умолчанию и добавил их в словарь следующим образом:

php_config_options:
  cli:
    engine: "On"
    short_open_tag: "Off"
    precision: 14
    [...]
  apache2:
    engine: "On"
    short_open_tag: "Off"
    precision: 14
    [...]

Я поместил это под vars/Ubuntu-22.yaml, чтобы иметь возможность постоянно настраивать его для выпусков. Мой шаблон выглядит так:

{% if php_config_options[environment] is defined %}
{% for option, value in php_config_options[environment].items() %}
{{ option }} = {{ value }}
{% endfor %}
{% endif %}

и моя задача такая:

- name: setup | Template php.ini
  ansible.builtin.template:
    src: "{{ php_version }}/php.ini.j2"
    dest: /etc/php/{{ php_version }}/apache2/php.ini
    owner: root
    group: root
    mode: 0644
  vars:
    environment: "apache2"

Когда я пытаюсь запустить его, я получаю сообщение об ошибке dict object has no element [], но не знаю почему. Может ли кто-нибудь помочь мне здесь?

Моя идея состоит в том, чтобы использовать эти переменные по умолчанию, но когда я захочу изменить одну из них, я установлю ее либо в group_vars, либо в host_vars, и он выберет эту + другие переменные по умолчанию.

Возможно, вы захотите использовать {% if environment in php_config_options %}

rhbvkleef 29.08.2024 15:02

Кстати, должны быть библиотеки, которые могут генерировать действительный INI-файл из dict. Выполнение этого с помощью шаблонов Jinja чрезвычайно подвержено ошибкам. Случайная новая строка или что-то в этом роде, и вы испортите синтаксис INI.

deceze 29.08.2024 15:07

Потому что вы пытаетесь создать INI-файл PHP…?!

deceze 29.08.2024 15:53

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

Zeitounator 29.08.2024 17:29
Введение в Ansible Roles
Введение в Ansible Roles
Ansible - это отличный инструмент управления конфигурацией, который можно использовать для автоматизации настройки или развертывания на большом...
1
4
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам следует использовать модуль Community.general.ini_file, который был разработан именно для вашего случая использования — чтобы настроить существующие настройки, не сохраняя значения по умолчанию самостоятельно.

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

Допустим, у нас есть файл config.ini в каталоге playbook со следующим содержимым:

[section0]
key0 = value0
[section1]
key1 = old_value

Затем мы можем выполнить следующий файл playbook.yaml

- name: Generate ini from yaml
  hosts: localhost
  vars:
    default_php_config_options:
      section1:
        key1: default_value
      section2:
        key2: default_value
      section3:
        key3: default_value
    php_config_options:
      section1:
        key1: value1
      section2:
        key2: value2
        key3: value4
  tasks:
    - name: Generate INI file from YAML variable
      community.general.ini_file:
        path: "{{ playbook_dir }}/config.ini"
        section: "{{ item.0 }}"
        option: "{{ item.1 }}"
        value: "{{ item.2 }}"
        mode: "0644"
      loop: >-
          {%- set results = [] -%}
          {%- for section in default_php_config_options | combine(php_config_options) | items -%}
          {%- for item in section.1.items() -%}
          {%- set _ = results.append([section.0, item.0, item.1]) %}
          {%- endfor -%}
          {%- endfor -%}
          {{ results }}

В этой книге мы определяем две переменные: default_php_config_options с настройками по умолчанию, которые необходимо переопределить, и php_config_options которые содержат переопределения для параметров по умолчанию.

Вывод команды ansible-playbook playbook.yaml будет содержать

TASK [Generate INI file from YAML variable] 
changed: [localhost] => (item=['section1', 'key1', 'value1'])
changed: [localhost] => (item=['section2', 'key2', 'value2'])
changed: [localhost] => (item=['section2', 'key3', 'value4'])
changed: [localhost] => (item=['section3', 'key3', 'default_value'])

и содержимое файла config.ini

[section0]
key0 = value0
[section1]
key1 = value1
[section2]
key2 = value2
key3 = value4
[section3]
key3 = default_value

Обратите внимание, что значение ключа1 в разделе 1 было заменено и добавлены отсутствующие параметры, в то время как исходные значения раздела 0 остались неизменными.

Теперь об этом цикле: что мы делаем — мы объединяем default_php_config_options с php_config_options, таким образом переопределяя первое значения из более позднего, если оно присутствует (см. документацию по объединению фильтров), а затем для каждой пары ключ/значение в каждом разделе, который мы создаем. список, содержащий

  • раздел
  • имя ключа
  • значение этого ключа

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

Предостережение заключается в том, что если вы удалите что-то из default_php_config_options или в php_config_options, но в ключе, которого не было в настройках по умолчанию, это не приведет к изменению уже измененных ключей или разделов обратно к значениям по умолчанию. В этом случае вам следует либо явно указать старое значение, либо создать отдельную задачу для удаления ненужных ключей.

Обновлено: добавлен пример объединения. default_php_config_options | combine(php_config_options) можно использовать и с примером @Vladimir Botka, но я оставлю свой ответ, который дает возможность работать с существующими ini-файлами.

спасибо за этот хороший ответ, но я думаю, что это подходит для моего случая. Я хочу иметь значения по умолчанию и иметь возможность их переопределять. cli — это дополнительный файл, и apache тоже. Это не разделы в одном файле. очень важно иметь значения по умолчанию, например. в vars/main.yaml и иметь возможность переопределять их, например. host_vars, но без необходимости указывать их все.

patrick_s 29.08.2024 18:20

Добавлен пример объединения некоторых значений по умолчанию с некоторыми значениями, отличными от значений по умолчанию.

Andrew 29.08.2024 18:53

Большое спасибо. Когда у меня есть одна и та же опция в обоих словарях, занимает ли место приоритет переменной?

patrick_s 29.08.2024 19:22
Ответ принят как подходящий

Учитывая упрощенный словарь

php_config_options:
  cli:
    engine: "On"
    short_open_tag: "Off"
    precision: 14

Используйте фильтр Community.general.to_ini. Например, шаблон

{{ php_config_options | community.general.to_ini }}

дает

[cli]
engine = On
short_open_tag = Off
precision = 14

Example of a complete playbook for testing

- hosts: localhost

  vars:

    php_config_options:
      cli:
        engine: "On"
        short_open_tag: "Off"
        precision: 14

  tasks:

    - copy:
        dest: /tmp/ansible/php.ini
        content: |
          {{ php_config_options | community.general.to_ini }}

Это была комбинация того и другого, но это более простой вариант. Я просто не использовал копию, вместо этого я использовал шаблон, но это сработало. Предварительно я объединил словари в другую переменную с суффиксом _merged.

patrick_s 29.08.2024 20:32

Вы можете использовать его в файле шаблона, блоке контента или где-либо еще. Шаблон Jinja2 такой же. Модуль копирования использовался для простоты.

Vladimir Botka 29.08.2024 20:53

Другие вопросы по теме