Ansible - only last with_items being updated in file

谁说胖子不能爱 提交于 2021-01-04 09:51:28

问题


Using blockinfile: which is using with_items from an exernal file. When I run the playbook I can see all itmes being processed but the resulting end file only has the last item updated.

Apologies, bit of a noob to this, so might be missing something obvious.

Tried various permutations

I have an external yaml config file with following contents - which is included as include_vars:

yaml props file:

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

Ansible tasks:

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

Expected:

##### 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

Actual:

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

回答1:


blockinfile is behaving exactly as designed: it adds a block of text to a target file, and will remove the matching block before adding a modified version. So for every iteration of your loop, blockinfile is removing the block added by the previous iteration and adding a new one.

Given that you're adding single lines to the file, rather than a block, you're probably better off using the lineinfile module, as in:

---
- 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 }}"

While this works, it's still problematic: if you change the value of one of your properties, you'll end up with multiple entries in the file. E.g, if we were to change dataSource.maxWait from 1000 to 2000, we would end up with:

dataSource.maxWait=1000
dataSource.maxWait=2000

We can protect against that using the regexp option to the lineinfile module, like this:

- 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 }}"

This will cause the module to remove any existing lines for the particular property before adding the new one.

Incidentally, you might want to consider slightly restructuring your data, using a dictionary rather than a list of "key=value" strings, like this:

---
- 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 }}"

And lastly, rather than using lineinfile or blockinfile, you might want to consider using ansible's template module to create your /app.properties file rather than trying to edit it.




回答2:


blockinfile uses a marker to keep track of the blocks it manages in a file. By default, this marker is ANSIBLE MANAGED BLOCK.

What happens in your case since you use the default marker is that a block is created after the line "##### Data Source Properties" for the first item then edited for the next items.

One solution would be to change the marker for each item. An other is to use lineinfile as reported by @Larsk

I would prefer in this case creating the full block at once:

- 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') }}"

If your intent is to do more complicated stuff with your config file, follow @Larsk advice and use a template.



来源:https://stackoverflow.com/questions/55834178/ansible-only-last-with-items-being-updated-in-file

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!