Max Murphy
Max Murphy

Reputation: 1973

Ansible: check, do and check again

Is there a compact way of checking whether something is installed, installing it if necessary and then checking again? At the moment I have this verbose mess:

  - name: check whether X is installed.
    shell: is_installed >/dev/null || echo nope
    register: thing_is_installed
  - name: install something
    shell: install_thing
    when: thing_is_installed=="nope"
  - name: check that the install succeeded
    shell: is_installed

for arbitrary functions install_thing and is_installed? The big problem is that is_installed occurs twice, so there is a risk that someone will modify the one function call but not the other, on account of carelessness, oversight or any number of other reasons. Shell is just an example. You may recognise the pattern - it is the core building block of idenpotent build functions. is_installed defines the desired state and install_thing defines how to get to the desired state. I expect that there is a form that looks like this, I just can't find it:

- name: Install boondoggle
  shell:
    check: is_installed
    install: install_thing

Or this, which has the advantage that it can be used with any number of tests, not just shell:

- name: Install bongle
  check:
    shell: is_installed
  install:
    shell: install_thing

Upvotes: 0

Views: 423

Answers (2)

Steve E.
Steve E.

Reputation: 9353

If you have a lot of pre-existing shell scripts that you want to use, then your options are limited. Especially if the parameters and return values are inconsistent.

Using the shell module in Ansible is a last resort. Sometimes you have to use it, but consider modules like 'package' and 'pip' first. Also check ansible-galaxy for complete roles that may do what you need. It's easy to try and do it all, but sometimes the work has already been done by others and even if it's only 50% of what you need. That's still time saved.

With the shell option, there is a 'creates' option. This checks for the existance of a file known to be created by the shell script and can be used instead of 'check' -> 'do' -> 'check' in some circumstances. The documentation also has further detail on useful test strategies. Obviously it all depends on your particular needs.

Another alternative if your shell scripts are uniform is to write your own wrapper module for them. With a little python knowledge this is very easy to do. Once again, it's possible to find open source examples and work from one which is similar to what you need rather than start from scratch. The internal process of the module will still need to do check -> fix -> check, but within the Ansible playbooks, the code will be very neat and avoid repatition.

Upvotes: 2

Charles Duffy
Charles Duffy

Reputation: 295756

Absent a noop mode (that is, a mode in which you report failure but don't follow through with any installation in the event that you're not yet in your desired state), you might as well combine the two. I tend to use a pattern akin to the following:

  - name: "enforce-foo"
    command: /path/to/enforce-foo-script
    register: enforce_foo
    failed_when: "(enforce_foo.rc != 0) and (enforce_foo.rc != 2)"
    changed_when: "enforce_foo.rc != 2"

...here, we're using exit status 0 to indicate that we exited without needing to do anything, and exit status 2 to indicate that we made changes.

Upvotes: 1

Related Questions