Search code examples
ansible

How to loop subelements with conditions based on string value?


I am working with nested dicts and have multiple dictionary files with identical key's. The dict and task itself is working, but I need to add the following condition. Dont run the task, if the subelement's state == absent

My dict:

qualitygate:
  - qualitygate_name: QA-dev
    operators:
      - metric: coverage
        operator: CT
        error: 1
        state: present
  - qualitygate_name: Department-xyc
    operators:
      - metric: coverage
        operator: CT
        error: 10
        state: absent
      - metric: duplicated_lines
        operator: GT
        error: 5
        state: present

Using my task:

- ansible.builtin.uri:
    url: "{{ protocol }}://{{ server_fqdn }}/api/qualitygates/create_condition"
    user: "{{ token }}"
    method: POST
    body_format: form-urlencoded
    body:
        error: "{{ item.1.error }}"
        metric: "{{ item.1.metric }}"
        gateName: "{{ item.0.qualitygate_name }}"
        op: "{{ item.1.operator }}"
  loop: "{{ qualitygate | subelements('operators', 'skip_missing=True') }}"
  when: qualitygate[item]['state'] is match("present")

How can I change the filter to only match the value "present"


Solution

  • The issue here is that item in your loop is not a single value. If you perform some simple diagnostics, like this:

    - loop: "{{ qualitygate | subelements('operators', 'skip_missing=True') }}"
      debug:
        msg: "{{ item }}"
    

    You'll see that item looks like this:

    [
        {
            "operators": [
                {
                    "error": 1,
                    "metric": "coverage",
                    "operator": "CT",
                    "state": "present"
                }
            ],
            "qualitygate_name": "QA-dev"
        },
        {
            "error": 1,
            "metric": "coverage",
            "operator": "CT",
            "state": "present"
        }
    ]
    

    This is described in the documentation:

    The ansible.builtin.subelements filter produces a product of an object and the subelement values of that object, similar to the ansible.builtin.subelements lookup. This lets you specify individual subelements to use in a template. For example, this expression:

    You want to use the second value in item -- which for each iteration represents a value from one of the operators lists -- in your conditional. That gives you something like this:

    - ansible.builtin.uri:
        url: "{{ protocol }}://{{ server_fqdn }}/api/qualitygates/create_condition"
        user: "{{ token }}"
        method: POST
        body_format: form-urlencoded
        body:
            error: "{{ item.1.error }}"
            metric: "{{ item.1.metric }}"
            gateName: "{{ item.0.qualitygate_name }}"
            op: "{{ item.1.operator }}"
      loop: "{{ qualitygate | subelements('operators', 'skip_missing=True') }}"
      when: item.1.state == "present"
    

    If we wrap everything in a debug task, like this:

    - debug:
        msg:
          ansible.builtin.uri:
            url: "{{ protocol }}://{{ server_fqdn }}/api/qualitygates/create_condition"
            user: "{{ token }}"
            method: POST
            body_format: form-urlencoded
            body:
              error: "{{ item.1.error }}"
              metric: "{{ item.1.metric }}"
              gateName: "{{ item.0.qualitygate_name }}"
              op: "{{ item.1.operator }}"
      loop: "{{ qualitygate | subelements('operators', 'skip_missing=True') }}"
      when: item.1.state == "present"
    

    That gives us:

    TASK [debug] **********************************************************************************************************************************************
    ok: [localhost] => (item=[{'qualitygate_name': 'QA-dev', 'operators': [{'metric': 'coverage', 'operator': 'CT', 'error': 1, 'state': 'present'}]}, {'metric': 'coverage', 'operator': 'CT', 'error': 1, 'state': 'present'}]) => {
        "msg": {
            "ansible.builtin.uri": {
                "body": {
                    "error": "1",
                    "gateName": "QA-dev",
                    "metric": "coverage",
                    "op": "CT"
                },
                "body_format": "form-urlencoded",
                "method": "POST",
                "url": "https://www.example.com/api/qualitygates/create_condition",
                "user": "mytoken"
            }
        }
    }
    skipping: [localhost] => (item=[{'qualitygate_name': 'Department-xyc', 'operators': [{'metric': 'coverage', 'operator': 'CT', 'error': 10, 'state': 'absent'}, {'metric': 'duplicated_lines', 'operator': 'GT', 'error': 5, 'state': 'present'}]}, {'metric': 'coverage', 'operator': 'CT', 'error': 10, 'state': 'absent'}])
    ok: [localhost] => (item=[{'qualitygate_name': 'Department-xyc', 'operators': [{'metric': 'coverage', 'operator': 'CT', 'error': 10, 'state': 'absent'}, {'metric': 'duplicated_lines', 'operator': 'GT', 'error': 5, 'state': 'present'}]}, {'metric': 'duplicated_lines', 'operator': 'GT', 'error': 5, 'state': 'present'}]) => {
        "msg": {
            "ansible.builtin.uri": {
                "body": {
                    "error": "5",
                    "gateName": "Department-xyc",
                    "metric": "duplicated_lines",
                    "op": "GT"
                },
                "body_format": "form-urlencoded",
                "method": "POST",
                "url": "https://www.example.com/api/qualitygates/create_condition",
                "user": "mytoken"
            }
        }
    }
    

    Which I think demonstrates exactly what you want.