From 63fdc3f08f04dd30c4c1cb51f7192d5e6c481c8c Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Wed, 7 Feb 2018 10:58:29 +0100 Subject: [PATCH] Fix jinja2>=2.9 nested include vars (#35099) This fixes a bug when parent's local vars where not available in nested includes. The bug can only be seen with jinja>=2.9 which changes how the variable scopes work. Fixes #34886 --- lib/ansible/template/vars.py | 9 ++- .../targets/template/tasks/main.yml | 57 ++++++++++++------- .../targets/template/templates/for_loop.j2 | 4 ++ .../template/templates/for_loop_include.j2 | 3 + .../templates/for_loop_include_nested.j2 | 1 + 5 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 test/integration/targets/template/templates/for_loop.j2 create mode 100644 test/integration/targets/template/templates/for_loop_include.j2 create mode 100644 test/integration/targets/template/templates/for_loop_include_nested.j2 diff --git a/lib/ansible/template/vars.py b/lib/ansible/template/vars.py index 9bca62a300..a645aafade 100644 --- a/lib/ansible/template/vars.py +++ b/lib/ansible/template/vars.py @@ -121,4 +121,11 @@ class AnsibleJ2Vars(Mapping): ''' if locals is None: return self - return AnsibleJ2Vars(self._templar, self._globals, locals=locals, *self._extras) + + # FIXME run this only on jinja2>=2.9? + # prior to version 2.9, locals contained all of the vars and not just the current + # local vars so this was not necessary for locals to propagate down to nested includes + new_locals = self._locals.copy() + new_locals.update(locals) + + return AnsibleJ2Vars(self._templar, self._globals, locals=new_locals, *self._extras) diff --git a/test/integration/targets/template/tasks/main.yml b/test/integration/targets/template/tasks/main.yml index 92caa52e32..ed9e8dc040 100644 --- a/test/integration/targets/template/tasks/main.yml +++ b/test/integration/targets/template/tasks/main.yml @@ -32,23 +32,23 @@ template: src=foo.j2 dest={{output_dir}}/foo.templated mode=0644 register: template_result -- assert: - that: - - "'changed' in template_result" - - "'dest' in template_result" - - "'group' in template_result" - - "'gid' in template_result" - - "'md5sum' in template_result" +- assert: + that: + - "'changed' in template_result" + - "'dest' in template_result" + - "'group' in template_result" + - "'gid' in template_result" + - "'md5sum' in template_result" - "'checksum' in template_result" - - "'owner' in template_result" - - "'size' in template_result" - - "'src' in template_result" - - "'state' in template_result" - - "'uid' in template_result" + - "'owner' in template_result" + - "'size' in template_result" + - "'src' in template_result" + - "'state' in template_result" + - "'uid' in template_result" - name: verify that the file was marked as changed - assert: - that: + assert: + that: - "template_result.changed == true" # test for import with context on jinja-2.9 See https://github.com/ansible/ansible/issues/20494 @@ -69,6 +69,21 @@ - 'diff_result.stdout == ""' - "diff_result.rc == 0" +# test for nested include https://github.com/ansible/ansible/issues/34886 +- name: test if parent variables are defined in nested include + template: src=for_loop.j2 dest={{output_dir}}/for_loop.templated mode=0644 + +- name: save templated output + shell: "cat {{output_dir}}/for_loop.templated" + register: for_loop_out +- debug: var=for_loop_out +- name: verify variables got templated + assert: + that: + - '"foo" in for_loop_out.stdout' + - '"bar" in for_loop_out.stdout' + - '"bam" in for_loop_out.stdout' + # test for 'import as' on jinja-2.9 See https://github.com/ansible/ansible/issues/20494 - name: fill in a template using import as ala fails2 case in issue 20494 template: src=import_as.j2 dest={{output_dir}}/import_as.templated mode=0644 @@ -258,8 +273,8 @@ register: templated - name: verify that the file was marked as changed in check mode but was not created - assert: - that: + assert: + that: - "not templated.stat.exists" - "template_result is changed" @@ -272,13 +287,13 @@ check_mode: True - name: verify that the file was marked as not changes in check mode - assert: - that: + assert: + that: - "template_result is not changed" - "'templated_var_loaded' in lookup('file', '{{output_dir | expanduser}}/short.templated')" - name: change var for the template - set_fact: + set_fact: templated_var: "changed" - name: fill in a basic template with changed var in check mode @@ -287,8 +302,8 @@ check_mode: True - name: verify that the file was marked as changed in check mode but the content was not changed - assert: - that: + assert: + that: - "'templated_var_loaded' in lookup('file', '{{output_dir | expanduser }}/short.templated')" - "template_result is changed" diff --git a/test/integration/targets/template/templates/for_loop.j2 b/test/integration/targets/template/templates/for_loop.j2 new file mode 100644 index 0000000000..49fa412d25 --- /dev/null +++ b/test/integration/targets/template/templates/for_loop.j2 @@ -0,0 +1,4 @@ +{% for par_var in parent_vars %} +{% include 'for_loop_include.j2' %} + +{% endfor %} diff --git a/test/integration/targets/template/templates/for_loop_include.j2 b/test/integration/targets/template/templates/for_loop_include.j2 new file mode 100644 index 0000000000..b1a0ad7d4e --- /dev/null +++ b/test/integration/targets/template/templates/for_loop_include.j2 @@ -0,0 +1,3 @@ +{% if par_var is defined %} +{% include 'for_loop_include_nested.j2' %} +{% endif %} diff --git a/test/integration/targets/template/templates/for_loop_include_nested.j2 b/test/integration/targets/template/templates/for_loop_include_nested.j2 new file mode 100644 index 0000000000..368bce4b97 --- /dev/null +++ b/test/integration/targets/template/templates/for_loop_include_nested.j2 @@ -0,0 +1 @@ +{{ par_var }}