Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get an Ansible check to run only once in a playbook?

Tags:

git

ansible

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?

like image 411
Dag Høidahl Avatar asked Feb 27 '14 13:02

Dag Høidahl


People also ask

How do I make an Ansible task run only once?

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.

How do I run a specific part of my playbook in Ansible?

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 .

How do I limit Ansible hosts?

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.


1 Answers

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.

like image 162
Martin Thorsen Ranang Avatar answered Oct 13 '22 00:10

Martin Thorsen Ranang