Changes to when and conditionals in Ansible 2.2

529 views
Skip to first unread message

Dan Lang

unread,
Nov 2, 2016, 5:37:10 PM11/2/16
to Ansible Project
I'm testing existing roles and playbooks against Ansible 2.2, and I've noticed that several roles that I have written that take advantage of "when: variable is defined" or "when: variable is undefined" in tasks no longer work.  This happens when a variable is used from a previous task, but is empty because maybe the results were empty.   Here is an example:

- name: Find existing rules
  find: path=/etc/software/rules/
  register: existing_rules
- name: Clean rules
  file: path={{ item }} state=absent
  with_items: "{{ existing_rules.files | map(attribute='path') | list }}"
  when: existing_rules is defined

In this case, there were no existing files, so the variable is empty and I would expect this Clean rules task to be skipped, but instead Ansible errors with something like 
FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'files'"}

What are the options then to allow for this sort of conditional logic?  This definitely worked in Ansible 2.1, but I don't recall if any DEPRECATED warnings were given. 


Matt Martz

unread,
Nov 2, 2016, 5:44:18 PM11/2/16
to ansible...@googlegroups.com
In 2.1 this would have been a deprecation warning.  What you need is something like:

with_items: "{{ existing_rules.files | default([]) | map(attribute='path') | list }}"

That gives existing_rules.files a default of an empty list.

Also off note here, is that `when` statements are processed for each iteration of the with_items loop, and not before the with_items.  So your with_items needs to process correctly all the time.  As such, you could remove `when` statement altogether as it isn't doing what you wanted it to do.

--
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-project+unsubscribe@googlegroups.com.
To post to this group, send email to ansible-project@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/0240be2f-27d7-41f3-a188-7c346d01e593%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Matt Martz
@sivel
sivel.net

Dan Lang

unread,
Nov 2, 2016, 8:41:21 PM11/2/16
to Ansible Project
Thanks for the info Matt.  The Ansible docs, http://docs.ansible.com/ansible/playbooks_conditionals.html, indicate this is should work as before. Specifically, 

If a required variable has not been set, you can skip or fail using Jinja2’s defined test. For example:

tasks:
    - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
      when: foo is defined

    - fail: msg="Bailing out. this play requires 'bar'"
      when: bar is undefined

This is especially useful in combination with the conditional import of vars files (see below).


 Do you know where this behavior is documented? I'd like to update the docs and submit a PR.


On Wednesday, November 2, 2016 at 5:44:18 PM UTC-4, Matt Martz wrote:
In 2.1 this would have been a deprecation warning.  What you need is something like:

with_items: "{{ existing_rules.files | default([]) | map(attribute='path') | list }}"

That gives existing_rules.files a default of an empty list.

Also off note here, is that `when` statements are processed for each iteration of the with_items loop, and not before the with_items.  So your with_items needs to process correctly all the time.  As such, you could remove `when` statement altogether as it isn't doing what you wanted it to do.
On Wed, Nov 2, 2016 at 4:37 PM, Dan Lang <dan....@gmail.com> wrote:
I'm testing existing roles and playbooks against Ansible 2.2, and I've noticed that several roles that I have written that take advantage of "when: variable is defined" or "when: variable is undefined" in tasks no longer work.  This happens when a variable is used from a previous task, but is empty because maybe the results were empty.   Here is an example:

- name: Find existing rules
  find: path=/etc/software/rules/
  register: existing_rules
- name: Clean rules
  file: path={{ item }} state=absent
  with_items: "{{ existing_rules.files | map(attribute='path') | list }}"
  when: existing_rules is defined

In this case, there were no existing files, so the variable is empty and I would expect this Clean rules task to be skipped, but instead Ansible errors with something like 
FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'files'"}

What are the options then to allow for this sort of conditional logic?  This definitely worked in Ansible 2.1, but I don't recall if any DEPRECATED warnings were given. 


--
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 post to this group, send email to ansible...@googlegroups.com.

Matt Martz

unread,
Nov 2, 2016, 9:54:54 PM11/2/16
to ansible...@googlegroups.com
If you read a little further down, under the "Loops and Conditionals"[1] this information is present:

Combining when with with_items (see Loops), be aware that the when statement is processed separately for each item... If you need to skip the whole task depending on the loop variable being defined, used the |default filter to provide an empty iterator


To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+unsubscribe@googlegroups.com.
To post to this group, send email to ansible-project@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/f2cc398e-bb26-4fa1-a351-d743cd58d3b5%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Josh Smift

unread,
Nov 3, 2016, 9:54:04 AM11/3/16
to ansible...@googlegroups.com
DL> In this case, there were no existing files, so the variable is empty
DL> and I would expect this *Clean rules* task to be skipped

Hmm, isn't the real problem that after the first task, exiting_rules
*isn't* undefined any more?

Add a debug task (- debug: var=existing_rules), and I see things like

TASK [debug] *******************************************************************
ok: [localhost] => {
"existing_rules": {
"changed": false,
"examined": 0,
"files": [],
"matched": 0,
"msg": ""
}
}

So I think the "Clean rules" tries to do stuff because "when:
existing_rules is defined" evaluates to true.

DL> but instead Ansible errors with something like
DL>
DL> > FAILED! => {"failed": true, "msg": "'dict object' has no attribute
DL> > 'files'"}

Hmm, that's not what I get; in either 2.1 or 2.2, I see

TASK [Clean rules] *************************************************************

and then no further output -- which makes sense, because existing_rules is
defined, and has a zero-length 'files' list. I'm not sure why you're
getting the error you're getting.

Here's my (slightly modified) list of tasks, just to be sure:

- name: Find existing rules
find: path=/tmp/foo
register: existing_rules

- debug: var=existing_rules

- name: Clean rules
debug: msg="{{ item }}"
with_items: "{{ existing_rules.files | map(attribute='path') | list }}"
when: existing_rules is defined

/tmp/foo is an empty (but existent) directory.

-Josh (j...@care.com)

(apologies for the automatic corporate disclaimer that follows)

This email is intended for the person(s) to whom it is addressed and may contain information that is PRIVILEGED or CONFIDENTIAL. Any unauthorized use, distribution, copying, or disclosure by any person other than the addressee(s) is strictly prohibited. If you have received this email in error, please notify the sender immediately by return email and delete the message and any attachments from your system.
Reply all
Reply to author
Forward
0 new messages