As a safeguard against using an outdated playbook, I'd like to ensure that I have an updated copy of the git checkout before Ansible is allowed to modify anything on the servers.
This is how I've attempted to do it. This action is located in a file included by all play books:
- name: Ensure local git repository is up-to-date local_action: git pull register: command_result failed_when: "'Updating' in command_result.stdout"
The problem is that this command is run once for each node Ansible connects to, instead of only once for each playbook run. How can I avoid that?
For such requirements where we need one tasks to run only once on a batch of hosts and we will be running that from Ansible controller node, we have feature parameter named run_once. When we have this parameter mentioned in a task, that task will run only once on first host it finds despite the host batch.
The easiest way to run only one task in Ansible Playbook is using the tags statement parameter of the “ansible-playbook” command. The default behavior is to execute all the tags in your Playbook with --tags all .
You can also limit the hosts you target on a particular run with the --limit flag. Negated limit. Note that single quotes MUST be used to prevent bash interpolation.
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.
[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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With