Doubt regarding loops and conditionals

29 views
Skip to first unread message

Nuno Jordão

unread,
Feb 17, 2020, 3:11:50 PM2/17/20
to ansible...@googlegroups.com
Hello,

I have a doubt regarding loops and conditionals that maybe someone can make it clear to me. 
In the documentation is stated that:

"When combining Conditionals with a loop, the when: statement is processed separately for each item."

But if I do this:

    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: nulo is defined

where "nulo" is an undefined variable. Here the task skips instead of failing. If I read the documentation to the letter it should fail as it would only do the test inside the loop.
Also, this issue:

has a slightly different variation of my loop which fails instead of skipping.

My observed behaviour is of course the better one as allows to test the list before running loop, but should I trust it? It is the same in all versions of Ansible?

Thank you for your help. Regards,

Nuno Jordão

Mauricio Tavares

unread,
Feb 17, 2020, 3:41:55 PM2/17/20
to ansible...@googlegroups.com
On Mon, Feb 17, 2020 at 3:11 PM Nuno Jordão <nuno....@gmail.com> wrote:
>
> Hello,
>
> I have a doubt regarding loops and conditionals that maybe someone can make it clear to me.
> In the documentation is stated that:
>
> "When combining Conditionals with a loop, the when: statement is processed separately for each item."
>
I am not sure since in your example the when is applied to the
debug, i.e. if when is false the debug task is not run.

> But if I do this:
>
> - name: "test loop"
> debug:
> msg: "test"
> loop: "{{nulo}}"
> when: nulo is defined
>
That is an interesting way to loop.

> where "nulo" is an undefined variable. Here the task skips instead of failing. If I read the documentation to the letter it should fail as it would only do the test inside the loop.
> Also, this issue:
> https://github.com/ansible/ansible/issues/45976
>
> has a slightly different variation of my loop which fails instead of skipping.
>
The differences are more than slightly. having "null_var" as
the when test is not the same as "null_var is defined."

when: null_var is defined

is testing whether null_var is defined, returning true or false; it
could not care less about its contents though. So, it does not break
when it is not defined.

when: null_var | default(False)

would also work since it also gives an option for when null_var is not
defined. I believe

when: null_var

would work if (1) it is always defined and (2) you set it to true or
false or something. Someone more knowledgeable can check my claims.

> My observed behaviour is of course the better one as allows to test the list before running loop, but should I trust it? It is the same in all versions of Ansible?
>
> Thank you for your help. Regards,
>
> Nuno Jordão
>
> --
> You received this message because you are subscribed to the Google Groups "Ansible Project" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/CAEAA%3Dts5OMuUg535jFC0xwx2V6YiJ%3DoHAuW-XGKneejDC4QfkA%40mail.gmail.com.

Vladimir Botka

unread,
Feb 17, 2020, 4:10:18 PM2/17/20
to Nuno Jordão, ansible...@googlegroups.com
On Mon, 17 Feb 2020 20:11:07 +0000
Nuno Jordão <nuno....@gmail.com> wrote:

> - name: "test loop"
> debug:
> msg: "test"
> loop: "{{nulo}}"
> when: nulo is defined
>
> where "nulo" is an undefined variable. Here the task skips instead of
> failing.

Instead of testing whether the variable is defined or not, use the "default"
filter. For example

- name: "test loop"
debug:
msg: "test"
loop: "{{ nulo|default([]) }}"

The loop will be skipped if "nulo" is undefined. Test "nulo" separately if
you want the playbook to fail. For example

- name: Fail when nulo undefined
fail:
msg: "Variable nulo undefined"
when: nulo if undefined

HTH,

-vlado

Nuno Jordão

unread,
Feb 18, 2020, 9:46:16 AM2/18/20
to Vladimir Botka, ansible...@googlegroups.com
Hello,

Thank you for your responses.
Some times I dont like to use the loop: "{{ nulo|default([]) }}" because it doesn't say "skipping" and in complex playbook with lots of hosts I like to have that feedback. 

I tested several options and and everything works, even the case of the issue 45976 (should have tested that one before). I think that, probably, the behaviour changed, and the documentation should be more clear in this regard... The conditional is not applied only per item but also to the task...

  vars:
    tstbool: False
    tstnull:

  tasks:


    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo|default([])}}"

    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: nulo is defined

    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: nulo|default(False)


    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: nulo|default([])|length > 0


    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: tstbool
  
    - name: "test loop"
      debug:
        msg: "test"
      loop: "{{nulo}}"
      when: tstnull

Returns:

PLAY [localhost] *****************************************************************************************************

TASK [test loop] *****************************************************************************************************

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************
skipping: [localhost]

TASK [test loop] *****************************************************************************************************[DEPRECATION WARNING]: evaluating None as a bare variable, this behaviour will go away and you might need to add
|bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle.. This feature will be
removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
skipping: [localhost]

PLAY RECAP ***********************************************************************************************************localhost                  : ok=0    changed=0    unreachable=0    failed=0    skipped=6    rescued=0    ignored=0

The final count for skipped is 6 but the first task doesn't report skipping

Stefan Hornburg (Racke)

unread,
Feb 18, 2020, 10:06:12 AM2/18/20
to ansible...@googlegroups.com
On 2/18/20 3:45 PM, Nuno Jordão wrote:
> Hello,
>
> Thank you for your responses.
> Some times I dont like to use the loop: "{{ nulo|default([]) }}" because it doesn't say "skipping" and in complex
> playbook with lots of hosts I like to have that feedback. 
>
> I tested several options and and everything works, even the case of the issue 45976
> <https://github.com/ansible/ansible/issues/45976> (should have tested that one before). I think that, probably, the
> behaviour changed, and the documentation should be more clear in this regard... The conditional is not applied only per
> item but also to the task...
>

I suppose Ansible determines whether the conditional contains a loop variable. In that case it applies it for every
item. In the other case it evaluates it only once and skips the task when the conditional results in a false value.

The documentation seems not be completely accurate.

Regards
Racke
> --
> You received this message because you are subscribed to the Google Groups "Ansible Project" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to
> ansible-proje...@googlegroups.com <mailto:ansible-proje...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/ansible-project/CAEAA%3Dtv3rn4Wh_txsEr7P92ec5g7DSwWp6uhWRZDeHoyZQ-z2w%40mail.gmail.com
> <https://groups.google.com/d/msgid/ansible-project/CAEAA%3Dtv3rn4Wh_txsEr7P92ec5g7DSwWp6uhWRZDeHoyZQ-z2w%40mail.gmail.com?utm_medium=email&utm_source=footer>.


--
Ecommerce and Linux consulting + Perl and web application programming.
Debian and Sympa administration. Provisioning with Ansible.

signature.asc

Vladimir Botka

unread,
Feb 18, 2020, 2:23:00 PM2/18/20
to Nuno Jordão, ansible...@googlegroups.com
On Tue, 18 Feb 2020 14:45:34 +0000
Nuno Jordão <nuno....@gmail.com> wrote:

> Some times I dont like to use the loop: "{{ nulo|default([]) }}" because it
> doesn't say "skipping" and in complex playbook with lots of hosts I like to
> have that feedback.

You might want to find a plugin which fits your needs
https://docs.ansible.com/ansible/latest/plugins/callback.html#plugin-list

> ... I think that, probably, the behaviour changed, and
> the documentation should be more clear in this regard... The conditional is
> not applied only per item but also to the task...
>
> [...]
> - name: "test loop"
> debug:
> msg: "test"
> loop: "{{nulo}}"
> when: nulo is defined

You're right. "when" is also applied before the loop starts. Let me point to
2 more cases which describe the complexity. The task below

- debug:
msg: "test"
loop: "{{ nulo }}"
when:
- nulo is defined
- item is search('PATTERN')

is skipped because "when" stops evaluating a conjunction after the first
element fails

skipping: [localhost]


But when the order of the conditions is changed the task

- debug:
msg: "test"
loop: "{{ nulo }}"
when:
- item is search('PATTERN')
- nulo is defined

fails because the evaluation of "item" leads to the evaluation of "nulo"

fatal: [localhost]: FAILED! => {"msg": "'nulo' is undefined"}


As a result, I argue that "{{ nulo|default([]) }}" should be preferred in
term of "keep it simple stupid".
https://en.wikipedia.org/wiki/KISS_principle

It's been also discussed here
https://groups.google.com/forum/#!topic/ansible-project/Y1mGC3vrsng

HTH,

-vlado

Nuno Jordão

unread,
Feb 19, 2020, 6:38:05 AM2/19/20
to Vladimir Botka, ansible...@googlegroups.com
Well, after reading the other discussion, I think you are right. The safest is to use the default. The conditional can be unpredictable which was what I was afraid and why I started this thread. 
Ansible should avoid these situations. Or maybe provide a warning of potential unsafe situations.

About the plugins, I dont think it is possible to use those in AWX.

Thank you for your input.

Regards,

Nuno Jordão
Reply all
Reply to author
Forward
0 new messages