Search code examples
ansibleblock

ansible - block conditionnal is ignored


i have a problem xith a block in an ansible role

Extract of roles/apps_nifi/tasks/main.yml :

- debug:
    msg: "nifi_extra_instances_name is defined"
  when: nifi_extra_instances_name  is defined

- debug:
    msg: "nifi_extra_instances_name is not defined"
  when: nifi_extra_instances_name  is not defined

- name: Import variables for extra instances of Nifi
  block:
    - set_fact:
        nifi_extra_instances_vars: "{{ item.value }}"
      when: nifi_extra_instances_name in item.key
      loop: "{{ lookup('dict', nifi_extra_instances, wantlist=True) }}"

    - set_fact:
        "{{ item.key }}": "{{ item.value }}"
      loop: "{{ lookup('dict', nifi_extra_instances_vars , wantlist=True) }}"
      when: nifi_extra_instances_vars is defined and nifi_extra_instances_vars|length > 0
  when: nifi_extra_instances_name is defined

nifi_extra_instances_name is a variable setup when i call the role if it is needed.

plabook main.yml :

- hosts: nifi_hosts
  become: yes

  roles:
    - { role: apps_nifi, tags: nifi }
    - { role: apps_nifi-registry, tags: apps_nifi-registry }
    - { role: apps_nifi-api, tags: apps_nifi-api, become: no }
    #- { role: apps_nifi, tags: "nifi-burn", vars: { nifi_extra_instances_name: burn} }

So the variable nifi_extra_instances_name is not defined in this example, i expect that the code into "block" is not played :

TASK [apps_nifi : debug] **********************************************************************************************************************************************
Wednesday 14 October 2020  10:39:45 +0200 (0:00:12.496)       0:00:20.193 *****
skipping: [XXXXX]

TASK [apps_nifi : debug] **********************************************************************************************************************************************
Wednesday 14 October 2020  10:39:45 +0200 (0:00:00.052)       0:00:20.245 *****
ok: [XXXXX] => {
    "msg": "nifi_extra_instances_name is not defined"
}

TASK [apps_nifi : set_fact] *******************************************************************************************************************************************
Wednesday 14 October 2020  10:39:45 +0200 (0:00:00.055)       0:00:20.300 *****
skipping: [XXXX] => (item={u'key': u'burn', u'value': {u'nifi_dir': u'/local/nifi-burn', u'nifi_web_https_port': 9445, u'nifi_ports': [u'9445:9445', u'9094:9094'], u'container_name': u'bdmpusher-burn'}})

TASK [apps_nifi : set_fact] *******************************************************************************************************************************************
Wednesday 14 October 2020  10:39:45 +0200 (0:00:00.058)       0:00:20.359 *****
fatal: [XXXXX]: FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'dict'. Error was a <class 'ansible.errors.AnsibleError'>, original message: with_dict expects a dict"}

any ideas please ?


Solution

  • A when condition for a block (as well as for e.g. a role stanza at play level, an import_task...) is "pushed" up to every affected task. In other words, the condition for the full block is evaluated at every task level.

    So the following piece of code:

    - block:
        - name: do a
          debug:
            msg: a
          when: task_a_condition
        - name: do b
          debug:
            msg: b
          when: task_b_condition
      when: block_condition
    

    Is the exact equivalent of:

    - name: do a
      debug:
        msg: a
      when: task_a_condition and block_condition
    - name: do b
      debug:
        msg: b
      when: task_b_condition and block condition
    

    Hence, all when conditions are analyzed in whatever case.

    Moreover, when looping, the loop expression is analyzed first to determine the needed iterations, then the when condition is analyzed for every item. In your case, it leads to an error because nifi_extra_instances_vars is undefined (and hence not a dict).

    You can fix that on that specific task with the following loop expression leading to an empty loop when needed:

    loop: "{{ query('dict', nifi_extra_instances | default({})) }}"
    

    (note: using query instead of lookup automatically returns a list)

    Now, if you really do not want those tasks to play at all even when skipped, there is IMO a better solution:

    • Move the block tasks to a separate file and remove the block stanza.
    • Use include_tasks with the same when condition as your current block. In this case it will only apply to the include statement itself, not to the individual tasks. Do not confuse with import_task which would lead to the exact same result as the block for the current problem.