简体   繁体   中英

Ansible Making json_query / loop conditional with a when clause

I have some ansible code involving a json_query in a loop that I'm trying to make conditional. But I think there is something I'm not understanding about when clauses and loops.

The following code works fine, when the condition is met (IaC.status == 400), the problem is that when the condition is not met, the 2nd task still runs, the loop tries to process IaC2, which doesn't exist, and the task fails with: "Invalid data passed to 'loop', it requires a list, got this instead:

I think this is actually expected behaviour for conditional statements and loops: ( https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#using-conditionals-in-loops ), but the solution described to skip the entire task is to use an empty iterator, but I have no idea how to merge that in with the json_query() statement.

Does anyone know how I can truly make the 2nd task below conditional?

    # The preceding code has made a RESTful API call to create a gitlab group, if the group
    # exists the return status is 400, so I need to look it up instead. In which case the
    # following code works fine. The problem is when IaC.status = 201 for some reason the loop 
    # in the 2nd task below tries to run, despite the when clause, and fails 
    # because the variable IaC2 doesn't exist
    - name: or if IaC already exists
      when: IaC.status == 400
      uri: 
        url: https://{{new_hostname}}/api/v4/groups
        method: GET
        headers: 
          Authorization: "Bearer {{token.json.access_token}}"
        body_format: json
        body:
          name: "IaC"
          top_level_only: true
        status_code: 200 
        validate_certs: yes
      register:  IaC2

    - name: json_query to find the IaC group id when status = "{{IaC.status}}"
      when: IaC.status == 400
      debug:
        var: item
      loop: "{{ IaC2 | community.general.json_query(jmesquery) }}"
      vars:
        jmesquery: "json[?name=='IaC'].id"
      register: group_id

You should condition your second task on the first task:

- name: json_query to find the IaC group id when status = "{{IaC.status}}"
  when: IaC2 is defined
  debug:
    var: item
  loop: "{{ IaC2 | community.general.json_query(jmesquery) }}"
  vars:
    jmesquery: "json[?name=='IaC'].id"
  register: group_id

I cannot reproduce your case on ansible 2.9.10 where the task is skipped as expected. But you can also help with a default. I guess this would work:

- name: json_query to find the IaC group id when status = "{{IaC.status}}"
  when: IaC2 is defined
  debug:
    var: item
  loop: "{{ IaC2 | community.general.json_query(jmesquery) | default([]) }}"
  vars:
    jmesquery: "json[?name=='IaC'].id"
  register: group_id

I have an answer of sorts. Firstly there were two things I didn't fully understand;

  • the register var (IaC2) is always created even if the uri task is skipped. And IaC2 has a totally different structure depending upon whether this task is executed or skipped.
  • The loop in the following task is always executed, so the when clause on the second task is pretty meaningless.

The actual problem was the jmesquery looking for the IaC2 variable to have a particular json structure, which it doesn't have in the case where the uri task is skipped.

So the solution that seems to work is checking to see if the uri task was skipped ( when: IaC2.skipped is defined ), and if it was skipped re-defining IaC2 in a empty list form that doesn't break the loop.

Code examples shown below.

So, it works, but I can't help thinking there must be an easier way of doing this?

    - name: If IaC already exists (IaC.status == 400) then get the list of existing groups.
      when: IaC.status == 400
      uri: 
        url: https://{{new_hostname}}/api/v4/groups
        method: GET
        headers: 
          Authorization: "Bearer {{token.json.access_token}}"
        body_format: json
        body:
          name: "IaC"
          top_level_only: true
        status_code: 200 
        validate_certs: no
      register:  IaC2


    # To avoid breaking the following loop that searches the groups in the case that 
    # the group was created, re-define IaC2 into a form that won't break it.
    - name: Not created group "IaC", so redefine IaC2 var so as not to break the following loop
      when: IaC2.skipped is defined
      set_fact:
        IaC2: '{{ {"json": []} }}'

    - name: json_query to find the IaC group id when status = "{{IaC.status}}"
      #when: IaC.status == 400 << Leave in or comment out, makes no difference
      debug:
        var: item
      loop: "{{ (IaC2 | community.general.json_query(jmesquery)) }}"
      vars:
        jmesquery: "json[?name=='IaC'].id"
      register: group_id
    
    - name: record the IaC group's id (400)
      when: IaC.status == 400      
      set_fact:
        IaC_group_id: "{{group_id.results[0].item}}"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM