sloweriang
sloweriang

Reputation: 345

how to iterate over list from json in ansible

I tried to register an output to a variable, but i couldnt filter the way i want it.

output:

oc get hpa -o json |jq -r '.items[].spec'
    {
      "maxReplicas": 3,
      "minReplicas": 1,
      "scaleTargetRef": {
        "apiVersion": "apps.openshift.io/v1",
        "kind": "DeploymentConfig",
        "name": "hello-openshift"
      },
      "targetCPUUtilizationPercentage": 70
    }
    {
      "maxReplicas": 4,
      "minReplicas": 2,
      "scaleTargetRef": {
        "apiVersion": "apps/v1",
        "kind": "Deployment",
        "name": "testrhel"
      },
      "targetCPUUtilizationPercentage": 79
    }

Register the output to variable

- name: check for existing
  shell: oc get hpa -o json |jq -r '.items[].spec'
  register: existing

I would like to loop the output.name and compare it to another variable.

- name: set_fact
  exist: {% if item.name == newvar and item.kind == newvar2 %}yes{%else%}no{%endif%}
  loop:
    - "{{ existing }}" 

- name: task
  shell: do something
  when: exist == yes

Thanks in advance.

edit: currently i am using below to get my comparison for the variables.

- name: Get existing hpa output
  shell: oc get hpa -o json -n {{ namespace }} |jq -r '.'
  register: tempvar
      
- name: set hpa variable to fact
  set_fact:
  existing_deploy: "{{ tempvar.stdout}}"

- name: Comparing existing hpa to new config
  set_fact:
    hpa_exist: "{% if deploy_type == item.spec.scaleTargetRef.kind|lower and deploy_name == item.spec.scaleTargetRef.name|lower %}yes{% else %}no{% endif %}"
  with_items:
    - "{{ existing_deploy['items'] }}"

but the variable got overwrite when i trying to use when condition

- name: task a
  include_tasks: a.yml
  when: hpa_exist
    
- name: task b
  include_tasks: b.yml
  when: not hpa_exist

deploymentconfig/hello-openshift condition always fail even when it is true. leading to execute task b, which is not supposed to

Upvotes: 1

Views: 3418

Answers (1)

toydarian
toydarian

Reputation: 4574

Check out the documentation of the shell module.
The output of the shell on stdout will be in <var>.stdout (so in existing.stdout in your case.)

Once you got that, you obviously have json as text, but you want to parse it. To do that, use the from_json filter as shown in this answer.

Summa summarum your task should look like this:

- name: set_fact
  set_fact:
    exist: {% if item['scaleTargetRef']['name'] == newvar and item['scaleTargetRef']['kind']  == newvar2 %}yes{% else %}no{% endif %}
  loop: "{{ existing.stdout | from_json}}" 

But your output needs to be a valid list, so basically, it needs to look like this:

    [{
      "maxReplicas": 3,
      "minReplicas": 1,
      "scaleTargetRef": {
        "apiVersion": "apps.openshift.io/v1",
        "kind": "DeploymentConfig",
        "name": "hello-openshift"
      },
      "targetCPUUtilizationPercentage": 70
    },
    {
      "maxReplicas": 4,
      "minReplicas": 2,
      "scaleTargetRef": {
        "apiVersion": "apps/v1",
        "kind": "Deployment",
        "name": "testrhel"
      },
      "targetCPUUtilizationPercentage": 79
    }]

But you might actually have a logic error, because you are looping over the list and overwriting the variable exist on every turn. So you will end up with one variable exist in the end and that will hold the value of the last iteration.
Check out how to register variables with a loop if you need the output of every iteration.

If you want to do something for each item that meets the condition, you can do this:

- name: check for existing
  shell: oc get hpa -o json | jq -r '.items[].spec'
  register: existing

- name: include tasks a
  include_tasks: a.yml
  when:
    - deploy_type == item['scaleTargetRef']['kind'] | lower
    - deploy_name == item['scaleTargetRef']['name'] | lower
  loop: "{{  existing.stdout | from_json }}"

- name: include tasks b
  include_tasks: b.yml
  when: (deploy_type != item['scaleTargetRef']['kind'] | lower) or
        (deploy_name != item['scaleTargetRef']['name'] | lower)
  loop: "{{  existing.stdout | from_json }}" 

You do not need any of the set_fact stuff in that case.

Upvotes: 1

Related Questions