default filter on with_items with from_json

567 views
Skip to first unread message

rjwagn...@gmail.com

unread,
Aug 8, 2018, 4:10:11 PM8/8/18
to Ansible Project
I'm running into the infamous issue where with_items isn't skipped if when evaluates to false (https://github.com/ansible/ansible/issues/13791).  That said, I'm at loss as to how to workaround my particular issue.

Consider these two tasks (simplified for discussion):

#> cat b.yml
---
- hosts: 127.0.0.1
  connection
: local
  gather_facts
: False
  tasks
:
 
- name: get json
    shell
: "echo '{ \"a\": [ 1,2,3 ] }'"
   
register: b
   
when: X=='1'
 
- name: echo a array
    debug
: msg="{{ item }}"
    with_items
: "{{ (b.stdout | from_json).a }}"
   
when: X=='1'

If X is 1, the playbook runs as expected:
#> ansible-playbook b.yml -e'X=1'

PLAY
[127.0.0.1] ***************************************************************

TASK
[get json] ****************************************************************
changed
: [127.0.0.1]

TASK
[echo a array] ************************************************************
ok
: [127.0.0.1] => (item=1) => {
   
"item": 1,
   
"msg": 1
}
ok
: [127.0.0.1] => (item=2) => {
   
"item": 2,
   
"msg": 2
}
ok
: [127.0.0.1] => (item=3) => {
   
"item": 3,
   
"msg": 3
}

PLAY RECAP
*********************************************************************
127.0.0.1                  : ok=2    changed=1    unreachable=0    failed=0

But if X is not 1, then the 'get json' task doesn't run, so b is not defined, and the 'echo a array' task fails:
#> ansible-playbook b.yml -e'X=0'

PLAY
[127.0.0.1] ***************************************************************

TASK
[get json] ****************************************************************
skipping
: [127.0.0.1]

TASK
[echo a array] ************************************************************
fatal
:
 
[127.0.0.1]: FAILED! => {"failed": true, "msg": "Unexpected
templating type error occurred on ({{ (b.stdout | from_json).a }}):
expected string or buffer"
}

PLAY RECAP
*********************************************************************
127.0.0.1                  : ok=0    changed=0    unreachable=0    failed=1


I've seen examples where folks use the default() filter on the with_items: loop, but I can't figure out how to include a filter that'll work with the from_json and the .a.  Any ideas?

Rob

Kai Stian Olstad

unread,
Aug 8, 2018, 5:34:05 PM8/8/18
to ansible...@googlegroups.com
On 08.08.2018 22:10, rjwagn...@gmail.com wrote:
> I'm running into the infamous issue where with_items isn't skipped if
> when
> evaluates to false (https://github.com/ansible/ansible/issues/13791).
> That
> said, I'm at loss as to how to workaround my particular issue.

It's not that infamous, it's just how Jinja work, and when you
understand the concept it is easy to deal with.


> Consider these two tasks (simplified for discussion):
>
> #> cat b.yml
> ---
> - hosts: 127.0.0.1
> connection: local
> gather_facts: False
> tasks:
> - name: get json
> shell: "echo '{ \"a\": [ 1,2,3 ] }'"
> register: b
> when: X=='1'
> - name: echo a array
> debug: msg="{{ item }}"
> with_items: "{{ (b.stdout | from_json).a }}"
> when: X=='1'

When X=0 the variable b will be defined, it contains data about the
sipped reason.
But b will not contain the key stdout so this variable need to be
filtered through default filter.

Since b.stdout is filter through from_json the default filter need to
provide a empty json string to satisfy the filter.

with_items: "{{ (b.stdout | from_json).a }}"

becomes

with_items: "{{ (b.stdout | default('{}') | from_json).a }}"

but this will also fail since the empty {} json string doesn't contain a
key name "a" so this also need to go through the default filter.
Since with_items need a list this must be an empty list [], so it
becomes

with_items: "{{ (b.stdout | default('{}') | from_json).a | default([])
}}"

So the principle is, if a variable is not defined it need to go through
the default filter.

I hope this explanation gave some meaning.


--
Kai Stian Olstad

rjwagn...@gmail.com

unread,
Aug 9, 2018, 9:47:15 AM8/9/18
to Ansible Project
Thanks very much, Kai.  This does help my understanding.  I was trying to use default() after the b.stdout to generate the JSON (i.e., have default generate '{ "a": "[]" }') and wasn't having any luck because of all the nested quotes.  I still consider it counterintuitive that Ansible evaluates with_items: even if when: skips it, but no use arguing here.

Thanks again for your help.  Appreciate it.

Rob

Kai Stian Olstad

unread,
Aug 9, 2018, 12:06:48 PM8/9/18
to ansible...@googlegroups.com
On 09.08.2018 15:47, rjwagn...@gmail.com wrote:
> I still consider it counterintuitive that Ansible evaluates
> with_items: even if when: skips it, but no use arguing here.

That's because when is evaluated for each element in with_items, so
with_items must run before when.
Remember that you can use the item variable in when, just to illustrate

- debug: msg="I run"
when: item == True
with_items:
- True
- False
- True

with_items must run first so when has the correct value for the variable
item.

--
Kai Stian Olstad

rjwagn...@gmail.com

unread,
Aug 10, 2018, 5:04:41 PM8/10/18
to Ansible Project
Ah, yes.  Makes sense now.  Thanks Kai.

Rob
Reply all
Reply to author
Forward
0 new messages