From 0f68ad8193ac17488e339a258f8c63fdae399c26 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Thu, 30 May 2013 14:32:03 -0400 Subject: [PATCH] Optionally fail task on undefined variables This patch introduces a new configuration option called error_on_undefined_vars, which defaults to false. If this option is set to true, then a task which has unevaluated variables in its arguments will fail instead of running. Output looks like this: TASK: [set rabbitmq password] ************************************************* fatal: [10.20.0.7] => Undefined variables: rabbitmq_user, rabbitmq_password --- lib/ansible/constants.py | 2 ++ lib/ansible/errors.py | 3 +++ lib/ansible/runner/__init__.py | 25 ++++++++++++++++++------- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 95842a71e8a..6632bdc51d2 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -110,6 +110,8 @@ ANSIBLE_NOCOWS = get_config(p, DEFAULTS, 'nocows', 'ANSIBLE_NOCO ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None) ZEROMQ_PORT = int(get_config(p, 'fireball', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099)) +DEFAULT_UNDEFINED_VAR_BEHAVIOR = get_config(p, DEFAULTS, 'error_on_undefined_vars', 'ANSIBLE_ERROR_ON_UNDEFINED_VARS', False) + # non-configurable things DEFAULT_SUDO_PASS = None DEFAULT_REMOTE_PASS = None diff --git a/lib/ansible/errors.py b/lib/ansible/errors.py index 29562b7f658..de0e6e38f3c 100644 --- a/lib/ansible/errors.py +++ b/lib/ansible/errors.py @@ -32,3 +32,6 @@ class AnsibleConnectionFailed(AnsibleError): class AnsibleYAMLValidationFailed(AnsibleError): pass + +class AnsibleUndefinedVariable(AnsibleError): + pass diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 82568e021e6..d373d6f9983 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -30,6 +30,7 @@ import base64 import sys import shlex import pipes +import re import ansible.constants as C import ansible.inventory @@ -128,7 +129,8 @@ class Runner(object): check=False, # don't make any changes, just try to probe for potential changes diff=False, # whether to show diffs for template files that change environment=None, # environment variables (as dict) to use inside the command - complex_args=None # structured data in addition to module_args, must be a dict + complex_args=None, # structured data in addition to module_args, must be a dict + error_on_undefined_vars=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR # ex. False ): if not complex_args: @@ -163,6 +165,7 @@ class Runner(object): self.is_playbook = is_playbook self.environment = environment self.complex_args = complex_args + self.error_on_undefined_vars = error_on_undefined_vars self.callbacks.runner = self @@ -435,11 +438,11 @@ class Runner(object): raise errors.AnsibleError("args must be a dictionary, received %s" % complex_args) result = self._executor_internal_inner( - host, - self.module_name, - self.module_args, - inject, - port, + host, + self.module_name, + self.module_args, + inject, + port, complex_args=complex_args ) results.append(result.result) @@ -626,7 +629,7 @@ class Runner(object): path = pipes.quote(path) # The following test needs to be SH-compliant. BASH-isms will # not work if /bin/sh points to a non-BASH shell. - test = "rc=0; [ -r \"%s\" ] || rc=2; [ -f \"%s\" ] || rc=1; [ -d \"%s\" ] && rc=3" % ((path,) * 3) + test = "rc=0; [ -r \"%s\" ] || rc=2; [ -f \"%s\" ] || rc=1; [ -d \"%s\" ] && rc=3" % ((path,) * 3) md5s = [ "(/usr/bin/md5sum %s 2>/dev/null)" % path, # Linux "(/sbin/md5sum -q %s 2>/dev/null)" % path, # ? @@ -673,8 +676,16 @@ class Runner(object): # ***************************************************** + def _contains_undefined_vars(self, module_args): + ''' return true if there are undefined variables ''' + return '{{' in module_args + def _copy_module(self, conn, tmp, module_name, module_args, inject, complex_args=None): ''' transfer a module over SFTP, does not run it ''' + if self.error_on_undefined_vars and self._contains_undefined_vars(module_args): + vars = re.findall(r'{{(.*?)}}', module_args) + raise errors.AnsibleUndefinedVariable("Undefined variables: %s" % + ', '.join(vars)) # FIXME if complex args is none, set to {}