Repository: ansible/ansible

Issue:
include_vars should not error on files with non-matching extensions
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
include_vars

##### ANSIBLE VERSION
```
$ ansible --version
ansible 2.4.2.0
  config file = /home/xaeth/.ansible.cfg
  configured module search path = [u'/home/xaeth/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.14 (default, Jan 17 2018, 14:28:32) [GCC 7.2.1 20170915 (Red Hat 7.2.1-2)]
```

##### CONFIGURATION
```
$ ansible-config dump --only-changed
ANSIBLE_SSH_ARGS(/home/xaeth/.ansible.cfg) = -o ForwardAgent=yes -o PubKeyAuthentication=yes -o ControlMaster=auto -o ControlPersist=120s -o ConnectTimeout=15
ANSIBLE_SSH_CONTROL_PATH(/home/xaeth/.ansible.cfg) = ~/.ssh/sock/%%h:%%p
DEFAULT_HOST_LIST(env: ANSIBLE_INVENTORY) = [u'/home/xaeth/ansible/inventory']
DEFAULT_TRANSPORT(/home/xaeth/.ansible.cfg) = ssh
```

##### OS / ENVIRONMENT
N/A

##### SUMMARY
When using the [include_vars](http://docs.ansible.com/ansible/latest/include_vars_module.html) module if the directory or any of its nested directories contain a file that doesn't match the default or defined extension the task will fail.  By default `include_vars` looks for yml, yaml, and json files. If we add the files to the `ignore_files` parameter explicitly then things work. Seems like this shouldn't be necessary.

##### STEPS TO REPRODUCE
While this will happen regardless of how you manage the directory, I think my example shows a good use case for the why.  In this case I'm using is checking out a git repo that has files that need to be included in.  That repository has a README.md, Jenkinsfile, and Makefile along with the var files ending in a .yml extension.  The playbook checks out the git repo, and then tries to load all included files.  

```yaml
---
- hosts: localhost
  tasks:
  - name: Fetch answer data
    git:
      repo: "https://github.com/myrepo/myvars"
      version: master
      dest: vars/myvars/
      force: yes
  - include_vars:
      name: collected
      dir: vars/myvars
      depth: 1
  - debug:
       var: collected
```

##### EXPECTED RESULTS
```
TASK [debug] *****************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "collected": {
        "mysql": {
            "groups": [
                {
                    "name": "mysql-admin"
                }
            ], 
            "remote": [
                {
.. snip ..
```

##### ACTUAL RESULTS
```
TASK [include_vars] **********************************************************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"ansible_facts": {"collected": {}}, "ansible_included_var_files": [], "changed": false, "message": "/home/xaeth/Development/ansible/playbooks/vars/myvars/README.md does not have a valid extension: json, yaml, yml"}
	to retry, use: --limit @/home/xaeth/Development/ansible/playbooks/test-answer.retry
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/utilities/logic/include_vars.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/utilities/logic/include_vars.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @linuxdynasty
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/utilities/logic/include_vars.py b/lib/ansible/modules/utilities/logic/include_vars.py
--- a/lib/ansible/modules/utilities/logic/include_vars.py
+++ b/lib/ansible/modules/utilities/logic/include_vars.py
@@ -56,6 +56,12 @@
     description:
       - List of file extensions to read when using C(dir).
     default: [yaml, yml, json]
+  ignore_unkown_extensions:
+    version_added: "2.7"
+    description:
+      - Ignore unkown file extensions within the directory. This allows users to specify a directory containing vars files
+        that are intermingled with non vars files extension types (For example, a directory with a README in it and vars files)
+    default: False
   free-form:
     description:
       - This module allows you to specify the 'file' option directly without any other options.
diff --git a/lib/ansible/plugins/action/include_vars.py b/lib/ansible/plugins/action/include_vars.py
--- a/lib/ansible/plugins/action/include_vars.py
+++ b/lib/ansible/plugins/action/include_vars.py
@@ -32,7 +32,7 @@ class ActionModule(ActionBase):
     TRANSFERS_FILES = False
 
     VALID_FILE_EXTENSIONS = ['yaml', 'yml', 'json']
-    VALID_DIR_ARGUMENTS = ['dir', 'depth', 'files_matching', 'ignore_files', 'extensions']
+    VALID_DIR_ARGUMENTS = ['dir', 'depth', 'files_matching', 'ignore_files', 'extensions', 'ignore_unknown_extensions']
     VALID_FILE_ARGUMENTS = ['file', '_raw_params']
     VALID_ALL = ['name']
 
@@ -68,6 +68,7 @@ def _set_args(self):
 
         self.depth = self._task.args.get('depth', None)
         self.files_matching = self._task.args.get('files_matching', None)
+        self.ignore_unknown_extensions = self._task.args.get('ignore_unknown_extensions', False)
         self.ignore_files = self._task.args.get('ignore_files', None)
         self.valid_extensions = self._task.args.get('extensions', self.VALID_FILE_EXTENSIONS)
 
@@ -274,9 +275,15 @@ def _load_files_in_dir(self, root_dir, var_files):
                     stop_iter = True
 
             if not stop_iter and not failed:
-                if path.exists(filepath) and not self._ignore_file(filename):
-                    failed, err_msg, loaded_data = self._load_files(filepath, validate_extensions=True)
-                    if not failed:
-                        results.update(loaded_data)
+                if self.ignore_unknown_extensions:
+                    if path.exists(filepath) and not self._ignore_file(filename) and self._is_valid_file_ext(filename):
+                        failed, err_msg, loaded_data = self._load_files(filepath, validate_extensions=True)
+                        if not failed:
+                            results.update(loaded_data)
+                else:
+                    if path.exists(filepath) and not self._ignore_file(filename):
+                        failed, err_msg, loaded_data = self._load_files(filepath, validate_extensions=True)
+                        if not failed:
+                            results.update(loaded_data)
 
         return failed, err_msg, results


Test patch: