Repository: ansible/ansible

Issue:
Template with lookup plugin gives wrong failure message if binary files are involved
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report


##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
- template with lookup plugin to fetch files

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.2.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
 
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
default settings
##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
CentOS Linux release 7.2.1511 (Core)
##### SUMMARY
<!--- Explain the problem briefly -->
Templating fails with wrong message given below if there is a binary file. This occur when template module is used with lookup plugin.
The file that caused the failure is not indicated

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
Include a binary file in list of files to be templated., which is fetched using lookup plugin.

<!--- Paste example playbooks or commands between quotes below -->
```
- name: Templates all .j2 files recursively
  template: src={{ item }} dest={{base_package_dir}}/{{release_no}}/{{build_no}}/{{ item|replace(autodeploy_path , 'config') }}  
  with_items: "{{ lookup('pipe', 'find '+ autodeploy_path + '  -type f -regextype posix-egrep -regex \".*\\.(j2)$\"').split('\n') }}"
  tags: property_template_2

```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Expected that Ansible will indicate the file that caused the template failure. e.g. Error templating file binaryxxxx.file
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
The task fails, other files are properly templated but the files that have problems are not indicated.

<!--- Paste verbatim command output between quotes below -->
```
[WARNING]: Failure using method (v2_runner_item_on_failed) in callback plugin (<ansible.plugins.callback.default.CallbackModule object at
0x359afd0>): 'ascii' codec can't decode byte 0xef in position 101: ordinal not in range(128)
```



Discussion:
resolved_by_pr https://github.com/ansible/ansible/pull/26288

Resolving patch:
diff --git a/lib/ansible/plugins/action/template.py b/lib/ansible/plugins/action/template.py
--- a/lib/ansible/plugins/action/template.py
+++ b/lib/ansible/plugins/action/template.py
@@ -21,7 +21,7 @@
 
 from ansible import constants as C
 from ansible.errors import AnsibleError, AnsibleFileNotFound
-from ansible.module_utils._text import to_bytes, to_native, to_text
+from ansible.module_utils._text import to_text
 from ansible.plugins.action import ActionBase
 from ansible.template import generate_ansible_template_vars
 from ansible.utils.hashing import checksum_s
@@ -44,7 +44,7 @@ def get_checksum(self, dest, all_vars, try_directory=False, source=None, tmp=Non
                 dest_stat = self._execute_remote_stat(dest, all_vars=all_vars, follow=False, tmp=tmp)
 
         except AnsibleError as e:
-            return dict(failed=True, msg=to_native(e))
+            return dict(failed=True, msg=to_text(e))
 
         return dest_stat['checksum']
 
@@ -88,7 +88,7 @@ def run(self, tmp=None, task_vars=None):
                 source = self._find_needle('templates', source)
             except AnsibleError as e:
                 result['failed'] = True
-                result['msg'] = to_native(e)
+                result['msg'] = to_text(e)
 
         if 'failed' in result:
             return result
@@ -156,7 +156,7 @@ def run(self, tmp=None, task_vars=None):
             self._templar.set_available_variables(old_vars)
         except Exception as e:
             result['failed'] = True
-            result['msg'] = type(e).__name__ + ": " + str(e)
+            result['msg'] = "%s: %s" % (type(e).__name__, to_text(e))
             return result
         finally:
             self._loader.cleanup_tmp_file(tmp_source)


Test patch: