summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Krizek <martin.krizek@gmail.com>2020-04-14 10:27:02 +0200
committerGitHub <noreply@github.com>2020-04-14 10:27:02 +0200
commitff1ba39c8af0bb1e28932823814bbdd5aa45cd37 (patch)
tree88daff8a3366a0b4a3fa221f54be61942a6e710e
parent7d5177d6a06154c19ed6b400bdb2b17beeee9606 (diff)
downloadansible-ff1ba39c8af0bb1e28932823814bbdd5aa45cd37.tar.gz
Prevent templating unused variables for {%include%} (#68749)
Fixes #68699
-rw-r--r--changelogs/fragments/68699-prevent-templating-all-vars-when-copying-context.yml2
-rw-r--r--lib/ansible/template/__init__.py38
-rw-r--r--lib/ansible/template/template.py4
-rwxr-xr-xtest/integration/targets/template/runme.sh3
-rw-r--r--test/integration/targets/template/templates/unused_vars_include.j21
-rw-r--r--test/integration/targets/template/templates/unused_vars_template.j22
-rw-r--r--test/integration/targets/template/unused_vars_include.yml8
7 files changed, 55 insertions, 3 deletions
diff --git a/changelogs/fragments/68699-prevent-templating-all-vars-when-copying-context.yml b/changelogs/fragments/68699-prevent-templating-all-vars-when-copying-context.yml
new file mode 100644
index 0000000000..11ed78f59a
--- /dev/null
+++ b/changelogs/fragments/68699-prevent-templating-all-vars-when-copying-context.yml
@@ -0,0 +1,2 @@
+bugfixes:
+ - "Prevent templating unused variables for {% include %} (https://github.com/ansible/ansible/issues/68699)"
diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py
index fe20267e01..10fa6e128b 100644
--- a/lib/ansible/template/__init__.py
+++ b/lib/ansible/template/__init__.py
@@ -28,6 +28,7 @@ import re
import time
from contextlib import contextmanager
+from distutils.version import LooseVersion
from numbers import Number
try:
@@ -67,6 +68,8 @@ NON_TEMPLATED_TYPES = (bool, Number)
JINJA2_OVERRIDE = '#jinja2:'
+from jinja2 import __version__ as j2_version
+
USE_JINJA2_NATIVE = False
if C.DEFAULT_JINJA2_NATIVE:
try:
@@ -76,7 +79,6 @@ if C.DEFAULT_JINJA2_NATIVE:
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
@@ -295,6 +297,40 @@ class AnsibleContext(Context):
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):
diff --git a/lib/ansible/template/template.py b/lib/ansible/template/template.py
index fe11471d6c..5a555883a0 100644
--- a/lib/ansible/template/template.py
+++ b/lib/ansible/template/template.py
@@ -26,9 +26,9 @@ __all__ = ['AnsibleJ2Template']
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.
'''
diff --git a/test/integration/targets/template/runme.sh b/test/integration/targets/template/runme.sh
index abdd62f28b..61511ac82f 100755
--- a/test/integration/targets/template/runme.sh
+++ b/test/integration/targets/template/runme.sh
@@ -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 "$@"
diff --git a/test/integration/targets/template/templates/unused_vars_include.j2 b/test/integration/targets/template/templates/unused_vars_include.j2
new file mode 100644
index 0000000000..457cbbc0e1
--- /dev/null
+++ b/test/integration/targets/template/templates/unused_vars_include.j2
@@ -0,0 +1 @@
+{{ var_set_in_template }}
diff --git a/test/integration/targets/template/templates/unused_vars_template.j2 b/test/integration/targets/template/templates/unused_vars_template.j2
new file mode 100644
index 0000000000..28afc90214
--- /dev/null
+++ b/test/integration/targets/template/templates/unused_vars_template.j2
@@ -0,0 +1,2 @@
+{% set var_set_in_template=test_var %}
+{% include "unused_vars_include.j2" %}
diff --git a/test/integration/targets/template/unused_vars_include.yml b/test/integration/targets/template/unused_vars_include.yml
new file mode 100644
index 0000000000..ff31b70d3c
--- /dev/null
+++ b/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') }}"