Issue 1:
ISSUE TYPE

Bug Report

COMPONENT NAME

template expansion

ANSIBLE VERSION

ansible 2.2.1.0
config file =
configured module search path = Default w/o overrides

CONFIGURATION

N/A

OS / ENVIRONMENT

Ubuntu 16.04. ansible installed via pip.

$ pip list --format=legacy
ansible (2.2.1.0)
cffi (1.9.1)
configobj (5.0.6)
crit (0.0.1)
cryptography (1.7.1)
dynagen (0.9.0)
enum34 (1.1.6)
idna (2.2)
ipaddr (2.1.11)
ipaddress (1.0.18)
Jinja2 (2.9.4)
MarkupSafe (0.23)
paramiko (2.1.1)
pip (9.0.1)
protobuf (3.1.0.post1)
pyasn1 (0.1.9)
pycparser (2.17)
pycrypto (2.6.1)
python-apt (1.1.0b1)
PyYAML (3.12)
setuptools (33.1.1)
six (1.10.0)
wheel (0.29.0)

SUMMARY

When one template imports another one, and then includes another template, it barfs with "KeyError: 'undefined variable: 0'"

STEPS TO REPRODUCE

# test.yml
- hosts: localhost
tasks:
- template:
src: foo
dest: /tmp/foo

# templates/foo
{% import 'qux' as qux with context %}
hello world
{{ qux.wibble }}
{% include 'bar' %}

# templates/bar
Goodbye

# templates/qux
{% set wibble = "WIBBLE" %}

EXPECTED RESULTS

/tmp/foo to be created with:

hello world
WIBBLE
Goodbye

ACTUAL RESULTS

TASK [template] ****************************************************************
task path: /home/ubuntu/bug/test.yml:3
fatal: [localhost]: FAILED! => {
"changed": false,
"failed": true,
"invocation": {
"module_args": {
"dest": "/tmp/foo",
"src": "foo"
},
"module_name": "template"
},
"msg": "KeyError: 'undefined variable: 0'"
}

ADDITIONAL INFO

If you change templates/foo to this, it works fine:

hello world
{% include 'bar' %}

And so does this:

{% import 'qux' as qux with context %}
hello world
{{ qux.wibble }}

So it appears to be something to do with the combination of import and include which is causing it to barf.

However if you remove the "with context", like this:

{% import 'qux' as qux %}
hello world
{{ qux.wibble }}

then it barfs in a different way:

TASK [template] ****************************************************************
task path: /home/ubuntu/bug/test.yml:3
fatal: [localhost]: FAILED! => {
"changed": false,
"failed": true,
"invocation": {
"module_args": {
"dest": "/tmp/foo",
"src": "foo"
},
"module_name": "template"
},
"msg": "AttributeError: 'NoneType' object has no attribute 'add_locals'"
}

So there is a potentially separate issue, that 'import ... with context' works, but 'import' by itself does not, in Ansible.

The difference is that in the latter case, jinja2 will cache the import with the template (since it doesn't depend on any of the dynamic context in that particular expansion of the template)

Finally: this appears not to be a bug with jinja2 itself. Using jinja2 API directly runs the test case with no problem:

#!/usr/bin/python
from jinja2 import Environment, FileSystemLoader
env = Environment(
loader=FileSystemLoader('templates'),
)
template = env.get_template('foo')
print template.render()

gives:

$ python test.py

hello world
WIBBLE
Goodbye

Issue 2:
ISSUE TYPE

Bug Report

COMPONENT NAME

Template

ANSIBLE VERSION

ansible 2.3.0 (devel 0aad46c85b) last updated 2017/01/09 16:11:21 (GMT -400)
config file = /home/jadams/.ansible.cfg
configured module search path = Default w/o overrides

CONFIGURATION

Tested on vanilla / no extra configuration

OS / ENVIRONMENT

N/A but tested from Fedora 25

SUMMARY

When Jinja2 dependency is >= 2.9.0, nested templates inside of loops defined in a parent template do not receive the local scope. For example:

{% for item in foo %}
{% include "other_template.j2" %}
{% endfor %

In the above example, other_template.j2 would not have the item variable defined.

STEPS TO REPRODUCE

# Example Playbook
- hosts: localhost
connection: local
vars:
foo:
- bar
- baz
tasks:
- name: Test Template
template: src=parent.j2 dest=/tmp/template.out

# parent.j2
{% for item in foo %}
{% include "subtemplate.j2" %}
{% endfor %}

#subtemplate.j2
{{ item }}

EXPECTED RESULTS

Expected the output of the following file:

bar
baz

ACTUAL RESULTS

TASK [Test Template] ************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "AnsibleUndefinedVariable: 'item' is undefined"}

I believe this is because of the Jinja2 Context changes mentioned here in combination with Ansible overriding default Jinja2 functions. I wasn't able to track down the root cause in my limited testing.