Skip to content

Commit

Permalink
Templar: encapsulate _available_variables (#55435)
Browse files Browse the repository at this point in the history
Ensure variables are reset between iterations
  • Loading branch information
mkrizek authored and bcoca committed May 20, 2019
1 parent bd061fd commit 34e9d67
Show file tree
Hide file tree
Showing 14 changed files with 47 additions and 30 deletions.
2 changes: 1 addition & 1 deletion lib/ansible/executor/task_executor.py
Expand Up @@ -345,7 +345,7 @@ def _run_loop(self, items):
task_vars['ansible_loop']['previtem'] = items[item_index - 1]

# Update template vars to reflect current loop iteration
templar.set_available_variables(task_vars)
templar.available_variables = task_vars

# pause between loop iterations
if loop_pause and ran_once:
Expand Down
2 changes: 1 addition & 1 deletion lib/ansible/playbook/base.py
Expand Up @@ -342,7 +342,7 @@ def post_validate(self, templar):
'''

# save the omit value for later checking
omit_value = templar._available_variables.get('omit')
omit_value = templar.available_variables.get('omit')

for (name, attribute) in iteritems(self._valid_attrs):

Expand Down
2 changes: 1 addition & 1 deletion lib/ansible/playbook/conditional.py
Expand Up @@ -125,7 +125,7 @@ def _check_conditional(self, conditional, templar, all_vars):
'templating delimiters such as {{ }} or {%% %%}. '
'Found: %s' % conditional)
# make sure the templar is using the variables specified with this method
templar.set_available_variables(variables=all_vars)
templar.available_variables = all_vars

try:
# if the conditional is "unsafe", disable lookups
Expand Down
6 changes: 3 additions & 3 deletions lib/ansible/plugins/action/template.py
Expand Up @@ -144,10 +144,10 @@ def run(self, tmp=None, task_vars=None):
temp_vars = task_vars.copy()
temp_vars.update(generate_ansible_template_vars(source, dest))

old_vars = self._templar._available_variables
self._templar.set_available_variables(temp_vars)
old_vars = self._templar.available_variables
self._templar.available_variables = temp_vars
resultant = self._templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)
self._templar.set_available_variables(old_vars)
self._templar.available_variables = old_vars
except AnsibleAction:
raise
except Exception as e:
Expand Down
4 changes: 2 additions & 2 deletions lib/ansible/plugins/inventory/__init__.py
Expand Up @@ -349,7 +349,7 @@ class Constructable(object):
def _compose(self, template, variables):
''' helper method for plugins to compose variables for Ansible based on jinja2 expression and inventory vars'''
t = self.templar
t.set_available_variables(variables)
t.available_variables = variables
return t.template('%s%s%s' % (t.environment.variable_start_string, template, t.environment.variable_end_string), disable_lookups=True)

def _set_composite_vars(self, compose, variables, host, strict=False):
Expand All @@ -369,7 +369,7 @@ def _add_host_to_composed_groups(self, groups, variables, host, strict=False):
# process each 'group entry'
if groups and isinstance(groups, dict):
variables = combine_vars(variables, self.inventory.get_host(host).get_vars())
self.templar.set_available_variables(variables)
self.templar.available_variables = variables
for group_name in groups:
conditional = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % groups[group_name]
group_name = self._sanitize_group_name(group_name)
Expand Down
2 changes: 1 addition & 1 deletion lib/ansible/plugins/inventory/azure_rm.py
Expand Up @@ -348,7 +348,7 @@ def _get_hosts(self):

# FUTURE: fix underlying inventory stuff to allow us to quickly access known groupvars from reconciled host
def _filter_host(self, inventory_hostname, hostvars):
self.templar.set_available_variables(hostvars)
self.templar.available_variables = hostvars

for condition in self._filters:
# FUTURE: should warn/fail if conditional doesn't return True or False
Expand Down
5 changes: 2 additions & 3 deletions lib/ansible/plugins/inventory/generator.py
Expand Up @@ -101,9 +101,8 @@ def verify_file(self, path):
return valid

def template(self, pattern, variables):
t = self.templar
t.set_available_variables(variables)
return t.do_template(pattern)
self.templar.available_variables = variables
return self.templar.do_template(pattern)

def add_parents(self, inventory, child, parents, template_vars):
for parent in parents:
Expand Down
6 changes: 3 additions & 3 deletions lib/ansible/plugins/lookup/template.py
Expand Up @@ -68,7 +68,7 @@ def run(self, terms, variables, **kwargs):
variable_start_string = kwargs.get('variable_start_string', None)
variable_end_string = kwargs.get('variable_end_string', None)

old_vars = self._templar._available_variables
old_vars = self._templar.available_variables

for term in terms:
display.debug("File lookup term: %s" % term)
Expand Down Expand Up @@ -105,7 +105,7 @@ def run(self, terms, variables, **kwargs):
vars = deepcopy(variables)
vars.update(generate_ansible_template_vars(lookupfile))
vars.update(lookup_template_vars)
self._templar.set_available_variables(vars)
self._templar.available_variables = vars

# do the templating
res = self._templar.template(template_data, preserve_trailing_newlines=True,
Expand All @@ -116,6 +116,6 @@ def run(self, terms, variables, **kwargs):
raise AnsibleError("the template file %s could not be found for the lookup" % term)

# restore old variables
self._templar.set_available_variables(old_vars)
self._templar.available_variables = old_vars

return ret
2 changes: 1 addition & 1 deletion lib/ansible/plugins/lookup/vars.py
Expand Up @@ -66,7 +66,7 @@ class LookupModule(LookupBase):

def run(self, terms, variables=None, **kwargs):
if variables is not None:
self._templar.set_available_variables(variables)
self._templar.available_variables = variables
myvars = getattr(self._templar, '_available_variables', {})

self.set_options(direct=kwargs)
Expand Down
14 changes: 13 additions & 1 deletion lib/ansible/template/__init__.py
Expand Up @@ -451,7 +451,12 @@ def _get_extensions(self):

return jinja_exts

def set_available_variables(self, variables):
@property
def available_variables(self):
return self._available_variables

@available_variables.setter
def available_variables(self, variables):
'''
Sets the list of template variables this Templar instance will use
to template things, so we don't have to pass them around between
Expand All @@ -464,6 +469,13 @@ def set_available_variables(self, variables):
self._available_variables = variables
self._cached_result = {}

def set_available_variables(self, variables):
display.deprecated(
'set_available_variables is being deprecated. Use "@available_variables.setter" instead.',
version='2.13'
)
self.available_variables = variables

def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None,
convert_data=True, static_vars=None, cache=True, disable_lookups=False):
'''
Expand Down
10 changes: 5 additions & 5 deletions lib/ansible/template/vars.py
Expand Up @@ -60,7 +60,7 @@ def __init__(self, templar, globals, locals=None, *extras):
self._locals[key] = val

def __contains__(self, k):
if k in self._templar._available_variables:
if k in self._templar.available_variables:
return True
if k in self._locals:
return True
Expand All @@ -73,16 +73,16 @@ def __contains__(self, k):

def __iter__(self):
keys = set()
keys.update(self._templar._available_variables, self._locals, self._globals, *self._extras)
keys.update(self._templar.available_variables, self._locals, self._globals, *self._extras)
return iter(keys)

def __len__(self):
keys = set()
keys.update(self._templar._available_variables, self._locals, self._globals, *self._extras)
keys.update(self._templar.available_variables, self._locals, self._globals, *self._extras)
return len(keys)

def __getitem__(self, varname):
if varname not in self._templar._available_variables:
if varname not in self._templar.available_variables:
if varname in self._locals:
return self._locals[varname]
for i in self._extras:
Expand All @@ -93,7 +93,7 @@ def __getitem__(self, varname):
else:
raise KeyError("undefined variable: %s" % varname)

variable = self._templar._available_variables[varname]
variable = self._templar.available_variables[varname]

# HostVars is special, return it as-is, as is the special variable
# 'vars', which contains the vars structure
Expand Down
6 changes: 3 additions & 3 deletions lib/ansible/vars/manager.py
Expand Up @@ -318,7 +318,7 @@ def plugins_by_groups():
# and magic vars so we can properly template the vars_files entries
temp_vars = combine_vars(all_vars, self._extra_vars)
temp_vars = combine_vars(temp_vars, magic_variables)
self._templar.set_available_variables(temp_vars)
self._templar.available_variables = temp_vars

# we assume each item in the list is itself a list, as we
# support "conditional includes" for vars_files, which mimics
Expand Down Expand Up @@ -498,7 +498,7 @@ def _get_delegated_vars(self, play, task, existing_variables):
# as we're fetching vars before post_validate has been called on
# the task that has been passed in
vars_copy = existing_variables.copy()
self._templar.set_available_variables(vars_copy)
self._templar.available_variables = vars_copy

items = []
has_loop = True
Expand Down Expand Up @@ -533,7 +533,7 @@ def _get_delegated_vars(self, play, task, existing_variables):
if item is not None:
vars_copy[item_var] = item

self._templar.set_available_variables(vars_copy)
self._templar.available_variables = vars_copy
delegated_host_name = self._templar.template(task.delegate_to, fail_on_undefined=False)
if delegated_host_name != task.delegate_to:
cache_items = True
Expand Down
2 changes: 1 addition & 1 deletion test/units/plugins/action/test_action.py
Expand Up @@ -214,7 +214,7 @@ def env_prefix(**args):
self.assertEqual(env_string, "FOO=foo")

# test environment with a variable in it
templar.set_available_variables(variables=dict(the_var='bar'))
templar.available_variables = dict(the_var='bar')
mock_task.environment = [dict(FOO='{{the_var}}')]
env_string = action_base._compute_environment_string()
self.assertEqual(env_string, "FOO=bar")
Expand Down
14 changes: 10 additions & 4 deletions test/units/template/test_templar.py
Expand Up @@ -217,11 +217,17 @@ def test_templar_simple(self):
# test with fail_on_undefined=False
self.assertEqual(templar.template("{{bad_var}}", fail_on_undefined=False), "{{bad_var}}")

# test set_available_variables()
templar.set_available_variables(variables=dict(foo="bam"))
# test setting available_variables
templar.available_variables = dict(foo="bam")
self.assertEqual(templar.template("{{foo}}"), "bam")
# variables must be a dict() for set_available_variables()
self.assertRaises(AssertionError, templar.set_available_variables, "foo=bam")
# variables must be a dict() for available_variables setter
# FIXME Use assertRaises() as a context manager (added in 2.7) once we do not run tests on Python 2.6 anymore.
try:
templar.available_variables = "foo=bam"
except AssertionError as e:
pass
except Exception:
self.fail(e)

def test_templar_escape_backslashes(self):
# Rule of thumb: If escape backslashes is True you should end up with
Expand Down

0 comments on commit 34e9d67

Please sign in to comment.