Ansible — только последние элементы, которые обновляются в файле

#ansible

#ansible

Вопрос:

Использование blockinfile: который использует with_items из внешнего файла. Когда я запускаю playbook, я вижу, что все обрабатываемые элементы, но в результирующем конечном файле обновляется только последний элемент.

Извиняюсь, немного новичок в этом, поэтому может отсутствовать что-то очевидное.

Пробовал различные перестановки

У меня есть внешний файл конфигурации yaml со следующим содержимым, который включен как include_vars:

файл реквизитов yaml:

 ds_props:
 - prop: dataSource.initialSize=8
 - prop: dataSource.maxActive=50
 - prop: dataSource.maxIdle=20
 - prop: dataSource.minIdle=5
 - prop: dataSource.maxWait=1000
  

Задачи Ansible:

 - name: update DS settings
  blockinfile:
   path: /app.properties
   insertafter: "##### Data Source Properties"
   block: |
           "{{ item.prop }}"
  with_items: "{{ ds_props }}"
  

Ожидаемый:

 ##### Data Source Properties #####
# BEGIN ANSIBLE MANAGED BLOCK
dataSource.initialSize=8
dataSource.maxActive=50
dataSource.maxIdle=20
dataSource.minIdle=5
dataSource.maxWait=1000
# END ANSIBLE MANAGED BLOCK
  

Актуально:

 ##### Data Source Properties #####
# BEGIN ANSIBLE MANAGED BLOCK
dataSource.maxWait=1000
# END ANSIBLE MANAGED BLOCK
  

Комментарии:

1. Потрясающе. Спасибо за подробный ответ.

Ответ №1:

blockinfile использует marker для отслеживания блоков, которыми он управляет в файле. По умолчанию этот маркер является ANSIBLE MANAGED BLOCK .

Что происходит в вашем случае, поскольку вы используете маркер по умолчанию, так это то, что блок создается после строки «##### Свойства источника данных» для первого элемента, а затем редактируется для следующих элементов.

Одним из решений было бы изменить маркер для каждого элемента. Другой вариант — использовать lineinfile, как сообщает @Larsk

Я бы предпочел в этом случае создать полный блок сразу:

 - name: update DS settings
  blockinfile:
   path: /app.properties
   insertafter: "##### Data Source Properties"
   marker: "Custom ds props - ansible managed"
   block: "{{ ds_props | json_query('[].prop') | join('n') }}"
  

Если вы намерены выполнять более сложные действия с вашим конфигурационным файлом, следуйте совету @ Larsk и используйте шаблон.

Ответ №2:

blockinfile работает точно так, как задумано: он добавляет блок текста в целевой файл и удаляет соответствующий блок перед добавлением измененной версии. Таким образом, для каждой итерации вашего цикла blockinfile удаляется блок, добавленный предыдущей итерацией, и добавляется новый.

Учитывая, что вы добавляете отдельные строки в файл, а не блок, вам, вероятно, лучше использовать lineinfile модуль, как в:

 ---
- hosts: localhost
  gather_facts: false
  vars:
    ds_props:
      - prop: dataSource.initialSize=8
      - prop: dataSource.maxActive=50
      - prop: dataSource.maxIdle=20
      - prop: dataSource.minIdle=5
      - prop: dataSource.maxWait=1000

  tasks:
    - name: update DS settings using lineinfile
      lineinfile:
        path: /app.properties-line
        line: "{{ item.prop }}"
        insertafter: "##### Data Source Properties"
      with_items: "{{ ds_props }}"
  

Хотя это работает, это все еще проблематично: если вы измените значение одного из своих свойств, в результате в файле будет несколько записей. Например, если бы мы изменили dataSource.maxWait с 1000 на 2000 , мы бы получили:

 dataSource.maxWait=1000
dataSource.maxWait=2000
  

Мы можем защитить от этого, используя regexp опцию для lineinfile модуля, например:

 - name: update DS settings using lineinfile
  lineinfile:
    path: /app.properties-line
    line: "{{ item.prop }}"
    insertafter: "##### Data Source Properties"
    regexp: "{{ item.prop.split('=')[0] }}"
  with_items: "{{ ds_props }}"
  

Это приведет к тому, что модуль удалит все существующие строки для конкретного свойства перед добавлением нового.

Кстати, возможно, вам захочется немного реструктурировать свои данные, используя словарь, а не список строк «ключ = значение», например:

 ---
- hosts: localhost
  gather_facts: false
  vars:
    ds_props:
      dataSource.initialSize: 8
      dataSource.maxActive: 50
      dataSource.maxIdle: 20
      dataSource.minIdle: 5
      dataSource.maxWait: 1000
  tasks:
    - name: update DS settings using lineinfile
      lineinfile:
        path: /app.properties-line
        line: "{{ item.key }}={{ item.value }}"
        insertafter: "##### Data Source Properties"
        regexp: "{{ item.key }}"
      with_items: "{{ ds_props|dict2items }}"
  

И, наконец, вместо использования lineinfile или blockinfile , вы можете рассмотреть возможность использования template модуля ansible для создания вашего /app.properties файла, а не пытаться редактировать его.