How to get an Ansible check to run only once in a playbook?
Since version 1.7 of Ansible you can use run_once: true
to only run a task one time and only on one host.
Updated
When I fist wrote my answer (2014-02-27), Ansible had no built-in support for running a task only once per playbook, not once per affected host that the playbook was run on. However, as tlo writes, support for this was introduced with run_once: true
in Ansible version 1.7.0 (released on 2014-08-06). With this feature, the example task definition from the question should be changed to
- name: Ensure local git repository is up-to-date
local_action: git pull
run_once: true
register: command_result
failed_when: "'Updating' in command_result.stdout"
to accomplish what is asked for.
Original Answer
[The following answer was my suggested solution for the particular problem of making sure that the local git branch is updated before Ansible runs the tasks of a playbook.]
I wrote the following Ansible callback plugin that will avoid playbook execution if the current git branch is out of sync (is either behind or has diverged) with the remote branch. To use it, place the following code in a file like callback_plugins/require_updated_git_branch.py
in your top-level Ansible playbook directory:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
import subprocess
import sys
from ansible.callbacks import display, banner
class CallbackModule(object):
"""Makes Ansible require that the current git branch is up to date.
"""
env_var_name = 'IGNORE_OUTDATED_GIT_BRANCH'
msg = 'OUTDATED GIT BRANCH: Your git branch is out of sync with the ' \
'remote branch. Please update your branch (git pull) before ' \
'continuing, or skip this test by setting the environment ' \
'variable {0}=yes.'.format(env_var_name)
out_of_sync_re = re.compile(r'Your branch (is behind|and .* have diverged)',
re.MULTILINE)
def __init__(self, *args, **kwargs):
if os.getenv(self.env_var_name, 'no') == 'yes':
self.disabled = True
def playbook_on_start(self):
subprocess.call(['git', 'fetch'])
if self.out_of_sync_re.search(subprocess.check_output([
'git', 'status', '--untracked-files=no'])):
display(banner(self.msg), color='bright purple')
sys.exit(1)
For example, when the local branch is behind the remote branch, the command ansible-playbook site.yml
halts early with the following output:
__________________________________________________________
/ OUTDATED GIT BRANCH: Your git branch is out of sync with \
| the remote branch. Please update your branch (git pull) |
| before continuing, or skip this test by setting the |
\ environment variable IGNORE_OUTDATED_GIT_BRANCH=yes. /
----------------------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
And, as the cow suggests, to turn off this check you can run the command like:
$ IGNORE_OUTDATED_GIT_BRANCH=yes ansible-playbook site.yml
This solution does not solve the general problem of avoiding to run any Ansible task more than once regardless of the number of hosts involved, but it ensures that outdated playbooks are not executed, and it handles the concern that you mentioned regarding my alias-based suggestion.