Repository: ansible/ansible

Issue:
Regression in template {% include %} search path in v2.2
##### ISSUE TYPE
 
Bug Report

##### COMPONENT NAME

template lookup

##### ANSIBLE VERSION

`2.2.0.0`

##### CONFIGURATION

Nothing

##### OS / ENVIRONMENT

Gentoo control machine

##### SUMMARY

When using `{% include %}` in a template, templates in the `templates/` subfolder (relative to playbook) are not found anymore (they were in ansible v2.1).

##### STEPS TO REPRODUCE

I've created a minimal repo to reproduce the issue: https://github.com/savoirfairelinux/ansible-template-test

##### EXPECTED RESULTS

The contents of `foo.txt` and `bar.txt` in the debug message

##### ACTUAL RESULTS

This error:

```
TASK [debug] *******************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TemplateNotFound: bar.txt
fatal: [localhost]: FAILED! => {"failed": true, "msg": "Unexpected failure during module execution.", "stdout": ""}
```

Regression in template {% include %} search path in v2.2
##### ISSUE TYPE
 
Bug Report

##### COMPONENT NAME

template lookup

##### ANSIBLE VERSION

`2.2.0.0`

##### CONFIGURATION

Nothing

##### OS / ENVIRONMENT

Gentoo control machine

##### SUMMARY

When using `{% include %}` in a template, templates in the `templates/` subfolder (relative to playbook) are not found anymore (they were in ansible v2.1).

##### STEPS TO REPRODUCE

I've created a minimal repo to reproduce the issue: https://github.com/savoirfairelinux/ansible-template-test

##### EXPECTED RESULTS

The contents of `foo.txt` and `bar.txt` in the debug message

##### ACTUAL RESULTS

This error:

```
TASK [debug] *******************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TemplateNotFound: bar.txt
fatal: [localhost]: FAILED! => {"failed": true, "msg": "Unexpected failure during module execution.", "stdout": ""}
```



Discussion:
To follow up on this, I can also reproduce the issue on `devel` with this version:

```
ansible-playbook 2.3.0 (devel b606078d69) last updated 2016/11/24 11:46:57 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 351dd9344e) last updated 2016/11/24 11:47:50 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD 43bb97bc37) last updated 2016/11/24 11:47:52 (GMT -400)
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```

I'll start working on a PR soon.
To follow up on this, I can also reproduce the issue on `devel` with this version:

```
ansible-playbook 2.3.0 (devel b606078d69) last updated 2016/11/24 11:46:57 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 351dd9344e) last updated 2016/11/24 11:47:50 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD 43bb97bc37) last updated 2016/11/24 11:47:52 (GMT -400)
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```

I'll start working on a PR soon.

Resolving patch:
diff --git a/lib/ansible/plugins/lookup/template.py b/lib/ansible/plugins/lookup/template.py
--- a/lib/ansible/plugins/lookup/template.py
+++ b/lib/ansible/plugins/lookup/template.py
@@ -49,6 +49,14 @@ def run(self, terms, variables, **kwargs):
                     # set jinja2 internal search path for includes
                     if 'ansible_search_path' in variables:
                         searchpath = variables['ansible_search_path']
+                        # our search paths aren't actually the proper ones for jinja includes.
+                        # We want to search into the 'templates' subdir of each search path in
+                        # addition to our original search paths.
+                        newsearchpath = []
+                        for p in searchpath:
+                            newsearchpath.append(os.path.join(p, 'templates'))
+                            newsearchpath.append(p)
+                        searchpath = newsearchpath
                     else:
                         searchpath = [self._loader._basedir, os.path.dirname(lookupfile)]
                     self._templar.environment.loader.searchpath = searchpath


Test patch:
diff --git a/test/integration/targets/lookups/tasks/main.yml b/test/integration/targets/lookups/tasks/main.yml
--- a/test/integration/targets/lookups/tasks/main.yml
+++ b/test/integration/targets/lookups/tasks/main.yml
@@ -268,3 +268,14 @@
         - product.results[6]['item'] == ["C", "1"]
         - product.results[7]['item'] == ["C", "2"]
         - product.results[8]['item'] == ["C", "3"]
+
+# Template lookups
+
+# ref #18526
+- name: Test that we have a proper jinja search path in template lookup
+  set_fact:
+    hello_world: "{{ lookup('template', 'hello.txt') }}"
+
+- assert:
+    that:
+      - "hello_world|trim == 'Hello world!'"
diff --git a/test/integration/targets/lookups/templates/hello.txt b/test/integration/targets/lookups/templates/hello.txt
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/lookups/templates/hello.txt
@@ -0,0 +1 @@
+Hello {% include 'world.txt' %}!
diff --git a/test/integration/targets/lookups/templates/world.txt b/test/integration/targets/lookups/templates/world.txt
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/lookups/templates/world.txt
@@ -0,0 +1 @@
+world