Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent templating unused variables for {%include%} #68749

Merged
merged 1 commit into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 "$@"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ var_set_in_template }}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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') }}"