Reputation: 1973
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
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
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