Skip to content

Commit

Permalink
Prevent templating unused variables for {%include%} (ansible#68749)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkrizek committed Apr 14, 2020
1 parent 7d5177d commit ff1ba39
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 3 deletions.
@@ -0,0 +1,2 @@
bugfixes:
- "Prevent templating unused variables for {% include %} (https://github.com/ansible/ansible/issues/68699)"
38 changes: 37 additions & 1 deletion lib/ansible/template/__init__.py
Expand Up @@ -28,6 +28,7 @@
import time

from contextlib import contextmanager
from distutils.version import LooseVersion
from numbers import Number

try:
Expand Down Expand Up @@ -67,6 +68,8 @@

JINJA2_OVERRIDE = '#jinja2:'

from jinja2 import __version__ as j2_version

USE_JINJA2_NATIVE = False
if C.DEFAULT_JINJA2_NATIVE:
try:
Expand All @@ -76,7 +79,6 @@
except ImportError:
from jinja2 import Environment
from jinja2.utils import concat as j2_concat
from jinja2 import __version__ as j2_version
display.warning(
'jinja2_native requires Jinja 2.10 and above. '
'Version detected: %s. Falling back to default.' % j2_version
Expand Down Expand Up @@ -295,6 +297,40 @@ def resolve_or_missing(self, key):
self._update_unsafe(val)
return val

def get_all(self):
"""Return the complete context as a dict including the exported
variables. For optimizations reasons this might not return an
actual copy so be careful with using it.
This is to prevent from running ``AnsibleJ2Vars`` through dict():
``dict(self.parent, **self.vars)``
In Ansible this means that ALL variables would be templated in the
process of re-creating the parent because ``AnsibleJ2Vars`` templates
each variable in its ``__getitem__`` method. Instead we re-create the
parent via ``AnsibleJ2Vars.add_locals`` that creates a new
``AnsibleJ2Vars`` copy without templating each variable.
This will prevent unnecessarily templating unused variables in cases
like setting a local variable and passing it to {% include %}
in a template.
Also see ``AnsibleJ2Template``and
https://github.com/pallets/jinja/commit/d67f0fd4cc2a4af08f51f4466150d49da7798729
"""
if LooseVersion(j2_version) >= LooseVersion('2.9'):
if not self.vars:
return self.parent
if not self.parent:
return self.vars

if isinstance(self.parent, AnsibleJ2Vars):
return self.parent.add_locals(self.vars)
else:
# can this happen in Ansible?
return dict(self.parent, **self.vars)


class JinjaPluginIntercept(MutableMapping):
def __init__(self, delegatee, pluginloader, *args, **kwargs):
Expand Down
4 changes: 2 additions & 2 deletions lib/ansible/template/template.py
Expand Up @@ -26,9 +26,9 @@

class AnsibleJ2Template(jinja2.environment.Template):
'''
A helper class, which prevents Jinja2 from running _jinja2_vars through dict().
A helper class, which prevents Jinja2 from running AnsibleJ2Vars through dict().
Without this, {% include %} and similar will create new contexts unlike the special
one created in template_from_file. This ensures they are all alike, except for
one created in Templar.template. This ensures they are all alike, except for
potential locals.
'''

Expand Down
3 changes: 3 additions & 0 deletions test/integration/targets/template/runme.sh
Expand Up @@ -19,3 +19,6 @@ ansible-playbook corner_cases.yml -v "$@"

# Test for #57351
ansible-playbook filter_plugins.yml -v "$@"

# https://github.com/ansible/ansible/issues/68699
ansible-playbook unused_vars_include.yml -v "$@"
@@ -0,0 +1 @@
{{ var_set_in_template }}
@@ -0,0 +1,2 @@
{% set var_set_in_template=test_var %}
{% include "unused_vars_include.j2" %}
8 changes: 8 additions & 0 deletions test/integration/targets/template/unused_vars_include.yml
@@ -0,0 +1,8 @@
- hosts: localhost
gather_facts: no
vars:
test_var: foo
unused_var: "{{ undefined_var }}"
tasks:
- debug:
msg: "{{ lookup('template', 'unused_vars_template.j2') }}"

0 comments on commit ff1ba39

Please sign in to comment.