bchards
bchards

Reputation: 401

`lineinfile` and `blockinfile` always duplicating lines

I am trying to use ansible to add two lines to a configuration file for an LXC container on a Proxmox server.

I have used both blockinfile and lineinfile to achieve this, and it works, but subsequent runs result in the duplication of lines despite that not being the (to my understanding) expected behavior.

When using blockinfile, the markers and block also appear in different locations in the file.

blockinfile

With this I am using a unique marker, with no newlines as per the documentation.

---
- name: Configure LXC Container
  ansible.builtin.blockinfile:
    path: "/etc/pve/lxc/{{ tailscale_lxc_vmid }}.conf"
    marker: "# {mark} ANSIBLE MANAGED BLOCK FOR CONTAINER {{ tailscale_lxc_vmid }}"
    block: |
      lxc.cgroup2.devices.allow = c 10:200 rwm
      lxc.mount.entry = /dev/net/tun dev/net/tun none bind,create=file
    owner: root
    group: www-data
    mode: "0640"
    insertafter: EOF
  register: lxc_config_result

Running the above results in the following configuration file.

# BEGIN ANSIBLE MANAGED BLOCK FOR CONTAINER 105
# END ANSIBLE MANAGED BLOCK FOR CONTAINER 105
arch: amd64
cmode: tty
console: 1
cores: 1
cpulimit: 0
cpuunits: 1024
hostname: test
memory: 512
net0: name=eth0,bridge=vmbr0,gw=192.168.0.1,hwaddr=XX:XX:XX:XX:XX:XX,ip=192.168.0.10/24,type=veth
onboot: 1
ostype: ubuntu
protection: 0
rootfs: local-lvm:vm-105-disk-0,size=8G
swap: 512
tty: 2
unprivileged: 1
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file

This seems to be bugged in some way because the marker lines are at the top of the file, and the block contents are at the end. I have tried removing insertafter, and also setting insertafter to unprivileged: 1 to force it to be placed in a specific location, but all three of these result in the above output.

In addition, running ansible again duplicated the block at the end of the file.

lineinfile

Because of the above issues, I tried switching to lineinfile.

- name: Ensure TUN device permissions
  ansible.builtin.lineinfile:
    path: "/etc/pve/lxc/{{ tailscale_lxc_vmid }}.conf"
    line: "lxc.cgroup2.devices.allow = c 10:200 rwm"
    state: present
  register: group_allow_result

- name: Ensure TUN device is mounted
  ansible.builtin.lineinfile:
    path: "/etc/pve/lxc/{{ tailscale_lxc_vmid }}.conf"
    line: "lxc.mount.entry = /dev/net/tun dev/net/tun none bind,create=file"
    state: present
  register: mount_result

This worked as expected, placing the two lines at the end of the file. But again, on subsequent runs, the lines are duplicated instead of being registered as already existing. I tried using the regexp option to specify how to search for the line, in-case of whitespace issues, but got the same result.

What am I doing wrong here?

I am thinking I am going to have to result to shell commands to grep for the line and then echo it in if it's not present. Which will work, but I would like to know what the issue is with blockinfile and lineinfile because I am using them elsewhere with no issue.

Upvotes: 1

Views: 68

Answers (2)

U880D
U880D

Reputation: 12142

Based on your comment

Proxmox LXC configuration files accept both <key>: <value> and <key> = <value>. However, it automatically converts the latter into the former. This is why lineinfile was not working.

and the documentation Anatomy of LXC Container Config File, Manual: pct.conf ...

For a configuration file 105.conf

arch: amd64
cmode: tty
console: 1
cores: 1
cpulimit: 0
cpuunits: 1024
hostname: test
memory: 512
net0: name=eth0,bridge=vmbr0,gw=192.168.0.1,hwaddr=XX:XX:XX:XX:XX:XX,ip=192.168.0.10/24,type=veth
onboot: 1
ostype: ubuntu
protection: 0
rootfs: local-lvm:vm-105-disk-0,size=8G
swap: 512
tty: 2
unprivileged: 1

a minimal example playbook

---
- hosts: localhost
  become: false
  gather_facts: false

  vars:

    tailscale_lxc_vmid: 105

  tasks:

  - name: Configure LXC Container
    ansible.builtin.blockinfile:
      path: "{{ tailscale_lxc_vmid }}.conf"
      marker: "# {mark} ANSIBLE MANAGED BLOCK FOR CONTAINER {{ tailscale_lxc_vmid }}"
      block: |
        lxc.cgroup2.devices.allow: c 10:200 rwm
        lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
      insertafter: EOF
    register: lxc_config_result

will result into an output of

TASK [Configure LXC Container] ******
changed: [localhost]

and a config file content cat 105.conf at the first run

arch: amd64
cmode: tty
console: 1
cores: 1
cpulimit: 0
cpuunits: 1024
hostname: test
memory: 512
net0: name=eth0,bridge=vmbr0,gw=192.168.0.1,hwaddr=XX:XX:XX:XX:XX:XX,ip=192.168.0.10/24,type=veth
onboot: 1
ostype: ubuntu
protection: 0
rootfs: local-lvm:vm-105-disk-0,size=8G
swap: 512
tty: 2
unprivileged: 1
# BEGIN ANSIBLE MANAGED BLOCK FOR CONTAINER 105
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
# END ANSIBLE MANAGED BLOCK FOR CONTAINER 105

and for second run into

TASK [Configure LXC Container] ******
ok: [localhost]

Q: "Why blockinfile was producing marker lines at the top of the file and placing the block at the bottom"

A: This isn't reproducible for me.

Q: "But lineinfile is working as expected using the <key>: <value> format."

A: This also not reproducible for me, since a minimal example

---
- hosts: localhost
  become: false
  gather_facts: false

  vars:

    tailscale_lxc_vmid: 105

  tasks:

  - name: Configure LXC Container
    lineinfile:
      path: "{{ tailscale_lxc_vmid }}.conf"
      regexp: ".*{{ item.key }}.*"
      line: "{{ item.key }} = {{ item.value }}"
    loop:
      - { key: 'lxc.cgroup2.devices.allow', value: 'c 10:200 rwm' }
      - { key: 'lxc.mount.entry', value: '/dev/net/tun dev/net/tun none bind,create=file' }

will result into an output of

TASK [Configure LXC Container] *************************************************************************************
changed: [localhost] => (item={'key': 'lxc.cgroup2.devices.allow', 'value': 'c 10:200 rwm'})
changed: [localhost] => (item={'key': 'lxc.mount.entry', 'value': '/dev/net/tun dev/net/tun none bind,create=file'})
TASK [Configure LXC Container] *************************************************************************************
ok: [localhost] => (item={'key': 'lxc.cgroup2.devices.allow', 'value': 'c 10:200 rwm'})
ok: [localhost] => (item={'key': 'lxc.mount.entry', 'value': '/dev/net/tun dev/net/tun none bind,create=file'})

Overall, this will be the case for both modules and syntax options, means also for INI-style

lxc.cgroup2.devices.allow = c 10:200 rwm
lxc.mount.entry = /dev/net/tun dev/net/tun none bind,create=file

Upvotes: 0

bchards
bchards

Reputation: 401

This turned out to be a Proxmox issue, not an Ansible one.

Proxmox LXC configuration files accept both <key>: <value> and <key> = <value>. However, it automatically converts the latter into the former. This is why lineinfile was not working.

I am still slightly unsure as to why blockinfile was producing marker lines at the top of the file and placing the block at the bottom, but lineinfile is working as expected using the <key>: <value> format.

Upvotes: -1

Related Questions