Race condition with the combine filter, is that a bug?

I am running ansible-core 2.18 on Debian 12

ansible [core 2.18.6]
  config file = None
  configured module search path = ['/home/signed/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/signed/dev/docker/.venv/lib/python3.12/site-packages/ansible
  ansible collection location = /home/signed/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/signed/dev/docker/.venv/bin/ansible
  python version = 3.12.6 (main, Sep  9 2024, 22:11:19) [Clang 18.1.8 ] (/home/signed/dev/docker/.venv/bin/python3)
  jinja version = 3.1.6
  libyaml = True

I am running the following role :

- name: Enable TLS
  block:
    - name: Get mongodb config
      ansible.builtin.slurp:
        src: /etc/mongod.conf
      register: mongodb_config

    - name: Update mongodb config to enable TLS
      ansible.builtin.set_fact:
        mongodb_config_updated: >-
          {{
            mongodb_config.content | b64decode | from_yaml
            | combine({
                'net': {
                  'tls': {
                    'mode': mongodb_tls_mode | default('disabled'),
                    'certificateKeyFile': mongodb_tls_certkey_file,
                    'CAFile': mongodb_tls_ca_file,
                    'disabledProtocols': 'TLS1_0,TLS1_1' if mongodb_tls_minversion == 'TLS1_2' else 'TLS1_0,TLS1_1,TLS1_2',
                  }
                }
              }, recursive=true)
          }}

    - name: Write updated mongodb config
      ansible.builtin.copy:
        content: "{{ mongodb_config_updated | to_nice_yaml }}"
        dest: /etc/mongod.conf
        mode: "0644"
        owner: mongodb
        group: mongodb
        backup: true
      become: true

    - name: Attempt to perform a clean shutdown
      community.mongodb.mongodb_shutdown:
        auth_mechanism: "{{ mongodb_auth_method }}"
        login_user: "{{ mongodb_admin_user }}"
        login_password: "{{ mongodb_admin_password }}"

    - name: Restart mongodb service
      ansible.builtin.systemd:
        name: mongod
        state: restarted
        enabled: true
      become: true

With the input of :

net:
    bindIpAll: true
processManagement:
    timeZoneInfo: /usr/share/zoneinfo
security:
    authorization: enabled
storage:
    dbPath: /var/lib/mongodb
systemLog:
    destination: file
    logAppend: true
    path: /var/log/mongodb/mongod.log

Running the above role as no verbosity, leads to the combine filter not applying.

Running with -vvv however works just fine.

I suspect a race condition somewhere, but I don’t know whether that’s just my environment being finnicky.

I’m not sure that a race is possible as that would be a serialized operation, -vvv should have little to no impact on jinja template resolution, most of that code does not use verbosity at all.

I’m unable to reproduce the reported behavior with core 2.18.3 regardless of verbosity.

What specific clues lead you to the conclusion that the difference is due to the combine filter rather than, say, the “Write updated mongodb config” task?

Could you show us the console log, perhaps with the -D parameter added?

Also, would you mind throwing the following task in after the “Update mongodb config to enable TLS” task?

- name: Dump mongodb_config_updated
  ansible.builtin.debug:
    msg: '{{ mongodb_config_updated }}'

Running it with a debugger: always statement on the block and printing the resulting variable at each step.

Without verbosity, mongodb_config_updated stays identical as mongodb_config.content

With verbosity, mongodb_config_updated is applied correctly

Okay, I’ve set up a venv with the same Ansible version as yours (2.18.6), and I’ve boiled down the code to a stand-alone playbook which I think should demonstrate the issue you’re describing. Except it doesn’t, at least not for me.

Can you run the signed-log_01.yml playbook below in your environment to see whether it also exhibits the anomalous behavior? The commands I’m running are:

ansible-playbook signed-log_01.yml -D ; cat /tmp/mongod.conf

and

ansible-playbook signed-log_01.yml -D -vv ; cat /tmp/mongod.conf
---
# signed-log_01.yml
- name: Mongo config mangler
  hosts: localhost
  gather_facts: false
  debugger: always
  vars:
    mongod_conf: /tmp/mongod.conf
    mongodb_tls_certkey_file: 'mongodb_tls_certkey_file'
    mongodb_tls_ca_file: 'mongodb_tls_ca_file'
    mongodb_tls_minversion: not_gonna_match
  tasks:
    - name: Initialize a scratch mongod.conf
      ansible.builtin.copy:
        content: |
          net:
              bindIpAll: true
          processManagement:
              timeZoneInfo: /usr/share/zoneinfo
          security:
              authorization: enabled
          storage:
              dbPath: /var/lib/mongodb
          systemLog:
              destination: file
              logAppend: true
              path: /var/log/mongodb/mongod.log
        dest: '{{ mongod_conf }}'

    - name: Enable TLS in the scratch mongod.conf
      block:
        - name: Get mongodb config
          ansible.builtin.slurp:
            src: '{{ mongod_conf }}'
          register: mongodb_config

        - name: Update mongodb config to enable TLS
          ansible.builtin.set_fact:
            mongodb_config_updated: >-
              {{
                mongodb_config.content | b64decode | from_yaml
                | combine({
                    'net': {
                      'tls': {
                        'mode': mongodb_tls_mode | default('disabled'),
                        'certificateKeyFile': mongodb_tls_certkey_file,
                        'CAFile': mongodb_tls_ca_file,
                        'disabledProtocols': 'TLS1_0,TLS1_1' if mongodb_tls_minversion == 'TLS1_2' else 'TLS1_0,TLS1_1,TLS1_2',
                      }
                    }
                  }, recursive=true)
              }}

        - name: Dump mongodb_config_updated
          ansible.builtin.debug:
            msg: '{{ mongodb_config_updated }}'

        - name: Write updated mongodb config
          ansible.builtin.copy:
            content: "{{ mongodb_config_updated | to_nice_yaml }}"
            dest: '{{ mongod_conf }}'