Mark
Mark

Reputation: 2071

Ansible: Perform Cleanup on Task Failure

I'm currently writing an Ansible play that follows this general format and is run via a cron job:

pre_tasks:
  -Configuration / package installation

tasks:
  -Work with installed packages

post_tasks:
  -Cleanup / uninstall packages

The problem with the above is that sometimes a command in the tasks section fails, and when it does the post_tasks section doesn't run, leaving the system in a messy state. Is it possible to force the commands in post_tasks to run even if a failure or fatal error occurs?

My current approach is to apply ignore_errors: yes to everything under the tasks section, and then apply a when: conditional to each task to individually check if the prior command succeeded.

This solution seems like a hack, but it gets worse because even with ignore_errors: yes set, if a Fatal error is encountered for a task the entire play will still immediately fail, so I have to also run a cron'd bash script to manually check on things after reach play execution.

All I want is a guarantee that even if tasks fails, post_tasks will still run. I'm sure there is a way to do this without resorting to bash script wrappers.

Upvotes: 35

Views: 33790

Answers (4)

aderchox
aderchox

Reputation: 4074

@MarkTamsky is right, but here's also a quick explanation.

You can group multiple tasks into blocks and then task directives will be one level deeper (like the code in a "try" block in most OO languages), and at the higher level, you can control what happens if one or more of these block tasks fail(s) with rescue(like "catch"es after "try" blocks).

Also if you need some tasks to run at the end of the execution of the block tasks (i.e., no matter whether those block tasks fail or not) you can use always (like Python's "finally").

Example(directly copied from the documentation):

- name: Attempt and graceful roll back demo

  block: # <--- a block of tasks
    - name: Print a message
      ansible.builtin.debug:
        msg: 'I execute normally'

    - name: Force a failure
      ansible.builtin.command: /bin/false

    - name: Never print this
      ansible.builtin.debug:
        msg: 'I never execute, due to the above task failing, :-('

  rescue: # <--- a rescue
    - name: Print when errors
      ansible.builtin.debug:
        msg: 'I caught an error'

    - name: Force a failure in middle of recovery! >:-)
      ansible.builtin.command: /bin/false

    - name: Never print this
      ansible.builtin.debug:
        msg: 'I also never execute :-('

  always: # <--- an always
    - name: Always do this
      ansible.builtin.debug:
        msg: "This always executes"

NOTICE 1: Each block must be in a "tasks", i.e.:

 tasks:
   - name: some name
     block:
       - name: some name
         <module>

NOTICE 2: Every directive (e.g. when, become, etc.) at the block level will be applied to "each of the tasks in the block one by one" and not to the block itself.

Upvotes: 4

Marc
Marc

Reputation: 854

This feature became available in Ansible 2.0:

This is the documentation for the new stanza markers block, rescue, and always.

Upvotes: 63

Marco Capuccini
Marco Capuccini

Reputation: 71

You should use handlers (http://docs.ansible.com/ansible/playbooks_intro.html) and set:

force_handlers: true

Please give a look to KubeNow's integration test (https://github.com/kubenow/KubeNow/blob/master/test/integration-test.yml).

Upvotes: 3

Mxx
Mxx

Reputation: 9346

Don't use post_tasks block but rather have your cleanup process as part of regular tasks.

Upvotes: -15

Related Questions