Ignoring errors in check mode

Hello,

I’ve been tearing my hair out over a problem which seems simple to fix but is somehow full of pitfalls.

Basically I have a few playbooks where one task depends on the previous one. For example, I create a file based on a template and then as a next step symlink it, or I create a user and then register his SSH key in the authorized_keys file.

Problem is that passing --check will stop the template and user creation tasks from modifying the system, yet the next tasks still run their full validation and call on a failure. I could put “ignore_errors: True” on them, but then I wouldn’t get a real failure result in case they fail during a normal execution. I managed to find a reasonable workaround for the first case (the file module has a force=yes option to suppress the error), but the second one seems to have no such option and quite frankly I’m sure there will be other modules out there where the failure handling isn’t infinitely tweakable.

At this point I started looking at ignore_errors to see if I could pass it a variable. Here follows a sample playbook which I run with “-e ansible_check_mode=True” and --check :


  • hosts: all
    name: testing
    tasks:
  • name: print variable
    action: debug msg=“Does variable exist [{{ ansible_check_mode }}]”
  • name: Make symlink
    action: file src=“/ohwhat/hai” dest=“/there/youare” state=link
    ignore_errors: ansible_check_mode

The first task prints that the variable exists and is set to true, but the second task yields this unclear error:

ERROR: error while evaluating conditional: ansible_check_mode

Great. I thought that using a Jinja2 template might fix the issue:


ignore_errors: “{{ ansible_check_mode == True }}”

No luck, except that this yields a real traceback this time:

Traceback (most recent call last):
File “/usr/bin/ansible-playbook”, line 323, in
sys.exit(main(sys.argv[1:]))
File “/usr/bin/ansible-playbook”, line 263, in main
pb.run()
File “/usr/lib/python2.6/site-packages/ansible/playbook/init.py”, line 348, in run
if not self._run_play(play):
File “/usr/lib/python2.6/site-packages/ansible/playbook/init.py”, line 788, in _run_play
if not self._run_task(play, task, False):
File “/usr/lib/python2.6/site-packages/ansible/playbook/init.py”, line 493, in _run_task
task.ignore_errors = utils.check_conditional(cond, play.basedir, task.module_vars, fail_on_undefined=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR)
File “/usr/lib/python2.6/site-packages/ansible/utils/init.py”, line 265, in check_conditional
conditional = template.template(basedir, conditional, inject, fail_on_undefined=fail_on_undefined)
File “/usr/lib/python2.6/site-packages/ansible/utils/template.py”, line 121, in template
varname = template_from_string(basedir, varname, templatevars, fail_on_undefined)
File “/usr/lib/python2.6/site-packages/ansible/utils/template.py”, line 368, in template_from_string
res = jinja2.utils.concat(rf)
File “”, line 8, in root
File “/usr/lib/python2.6/site-packages/jinja2/runtime.py”, line 485, in _fail_with_undefined_error
raise self._undefined_exception(hint)
jinja2.exceptions.UndefinedError: ‘ansible_check_mode’ is undefined

I’ve tested this on Ansible 1.8.2 and 1.9.0 (both stable) and both went belly-up.

It seems this weirdness has been mentioned before in this mailing group, but their workaround of duplicating the task and then playing with when is triggering my DRY-sense (Don’t Repeat Yourself). At this point I think it needs some fix in Ansible to actually get this working while keeping playbooks simple.

While I’m creating this topic anyway, did anything ever come of a magic variable to detect check mode? So far I’ve been testing a horrible, horrible kludge which detects check mode by the behaviour of the command module, but a simple magic variable could do wonders because I wouldn’t need to duplicate that kludge in all my roles.

If you agree that something can be done about all of the above, please let me know. I wanted to run this by the mailing group first before throwing it on the bugtracker to get feedback on how to file it. If you have been reading so far, thank you for your time.

With best regards,

Albert

Nobody got any comments if and where to best report these errors? While my original mail was more of a rant than a constructive topic, I still have these errors when I try to put a variable in ignore_errors:

When I directly reference a variable:

ERROR: error while evaluating conditional: ansible_check_mode

When I use a jinja placeholder:

Traceback (most recent call last):
File “/usr/bin/ansible-playbook”, line 323, in
sys.exit(main(sys.argv[1:]))
File “/usr/bin/ansible-playbook”, line 263, in main
pb.run()
File “/usr/lib/python2.6/site-packages/ansible/playbook/init.py”, line 348, in run
if not self._run_play(play):
File “/usr/lib/python2.6/site-packages/ansible/playbook/init.py”, line 788, in _run_play
if not self._run_task(play, task, False):
File “/usr/lib/python2.6/site-packages/ansible/playbook/init.py”, line 493, in _run_task
task.ignore_errors = utils.check_conditional(cond, play.basedir, task.module_vars, fail_on_undefined=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR)
File “/usr/lib/python2.6/site-packages/ansible/utils/init.py”, line 265, in check_conditional
conditional = template.template(basedir, conditional, inject, fail_on_undefined=fail_on_undefined)
File “/usr/lib/python2.6/site-packages/ansible/utils/template.py”, line 121, in template
varname = template_from_string(basedir, varname, templatevars, fail_on_undefined)
File “/usr/lib/python2.6/site-packages/ansible/utils/template.py”, line 368, in template_from_string
res = jinja2.utils.concat(rf)
File “”, line 8, in root
File “/usr/lib/python2.6/site-packages/jinja2/runtime.py”, line 485, in _fail_with_undefined_error
raise self._undefined_exception(hint)
jinja2.exceptions.UndefinedError: ‘ansible_check_mode’ is undefined

I have since worked around the problem like this but I hate this kind of boilerplate:


  • hosts: all
    name: testing
    tasks:
  • name: print variable
    action: debug msg=“Does variable exist [{{ ansible_check_mode }}]”
  • name: Make symlink
    action: file src=“/ohwhat/hai” dest=“/there/youare” state=link
    ignore_errors: True
    register: symlinkresult
  • name: Fail if symlink could not be created
    action: fail msg=“Symlink could not be created”
    when: “{{ not ( ansible_check_mode) and (symlinkresult | failed) }}”

With best regards,

Albert

Not tried myself but I wonder if you could use failed_when to reduce the boilerplate a little: http://docs.ansible.com/ansible/playbooks_error_handling.html#controlling-what-defines-failure

Something like this (as I say, untested):

I believe I did try something like that though, IIRC it still failed the playbook because “failed_when:” is meant to turn an “Ok” status into “Failed” (e.g. when a command writes an error to stdout while its return status is 0), not turn a “Failed” status into “Ok”.

Once I tried this I went on to add “when: not ansible_check_mode” but now you can’t see if that tasks thinks it needs to be executed when running in check mode…

Anyhow, I guess I might try it once more. Thanks for your input.