Repository: ansible/ansible
Pull request: Find fix
Closes #50070
Fixes #50067

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Bugfix Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
find

Problem statement:
find: regex excludes without pattern raises internal errors by default
##### SUMMARY

Something like the following to just strip an entry

```
- hosts: all
  tasks:

    - name: Find all Open Stack source repos used by this job
      find:
        paths:
          - github.com
        file_type: directory
        depth: 2
        recurse: true
        use_regex: true
        excludes: project2
      register: found_repos

    - name: debug
      debug:
        msg: '{{ item.path }}'
      with_items: '{{ found_repos.files }}'
```

on a sample directory tree like

```
github.com/
├── name1
│   ├── project1
│   └── project2
└── name2
    └── project3
```

Raises

```
The full traceback is:
Traceback (most recent call last):
  File "/home/iwienand/.ansible/tmp/ansible-tmp-1545104068.6687615-164886312799083/AnsiballZ_find.py", line 113, in <module>
    _ansiballz_main()
  File "/home/iwienand/.ansible/tmp/ansible-tmp-1545104068.6687615-164886312799083/AnsiballZ_find.py", line 105, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/home/iwienand/.ansible/tmp/ansible-tmp-1545104068.6687615-164886312799083/AnsiballZ_find.py", line 48, in invoke_module
    imp.load_module('__main__', mod, module, MOD_DESC)
  File "/tmp/ansible_find_payload_yeEFBn/__main__.py", line 472, in <module>
  File "/tmp/ansible_find_payload_yeEFBn/__main__.py", line 441, in main
  File "/tmp/ansible_find_payload_yeEFBn/__main__.py", line 230, in pfilter
  File "/usr/lib64/python2.7/re.py", line 194, in compile
    return _compile(pattern, flags)
  File "/usr/lib64/python2.7/re.py", line 251, in _compile
    raise error, v # invalid expression
sre_constants.error: nothing to repeat
```
because ```patterns``` is defaulting to `*`

It was probably reasonable to assume that setting ```use_regex``` implied you were setting ```patterns``` before ```excludes``` was around (else why would you bother).  But I don't think an excludes with ```use_regex``` implies you should have to reset patterns to ```.*``` (or a valid regex)

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
files/find.py

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```
ansible-playbook 2.8.0.dev0 (devel 61b5adcf31) last updated 2018/12/18 13:39:29 (GMT +1100)
  config file = None
  configured module search path = ['/home/iwienand/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/iwienand/programs/ansible/lib/ansible
  executable location = ./env/bin/ansible-playbook
  python version = 3.7.1 (default, Nov 23 2018, 10:01:49) [GCC 8.2.1 20181105 (Red Hat 8.2.1-5)]
No config file found; using defaults
Parsed localhost, inventory source with host_list plugin

```

##### EXPECTED RESULTS

Can exclude without resetting patterns

Hints from issue discussion:
Files identified in the description:
* [lib/ansible/modules/files/find.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/files/find.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 @bcoca
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [`lib/ansible/modules/find.py`](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/find.py)

If these files are incorrect, 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 --->

Commit message: moved change to new location
added changelog

Patch to review:
diff --git a/changelogs/fragments/fix_find_default.yml b/changelogs/fragments/fix_find_default.yml
new file mode 100644
index 00000000000000..85c640ae958ce6
--- /dev/null
+++ b/changelogs/fragments/fix_find_default.yml
@@ -0,0 +1,2 @@
+bugfixes:
+  - find module, fix default pattern when use_regex is true.
diff --git a/lib/ansible/modules/find.py b/lib/ansible/modules/find.py
index 323d0223f26917..4ad1f057a01b94 100644
--- a/lib/ansible/modules/find.py
+++ b/lib/ansible/modules/find.py
@@ -29,7 +29,7 @@
               first letter of any of those words (e.g., "1w").
         type: str
     patterns:
-        default: '*'
+        default: []
         description:
             - One or more (shell or regex) patterns, which type is controlled by C(use_regex) option.
             - The patterns restrict the list of files to be returned to those whose basenames match at
@@ -41,6 +41,7 @@
             - This parameter expects a list, which can be either comma separated or YAML. If any of the
               patterns contain a comma, make sure to put them in a list to avoid splitting the patterns
               in undesirable ways.
+            - Defaults to '*', or '.*' when use_regex enabled
         type: list
         aliases: [ pattern ]
         elements: str
@@ -375,7 +376,7 @@ def main():
     module = AnsibleModule(
         argument_spec=dict(
             paths=dict(type='list', required=True, aliases=['name', 'path'], elements='str'),
-            patterns=dict(type='list', default=['*'], aliases=['pattern'], elements='str'),
+            patterns=dict(type='list', default=[], aliases=['pattern'], elements='str'),
             excludes=dict(type='list', aliases=['exclude'], elements='str'),
             contains=dict(type='str'),
             read_whole_file=dict(type='bool', default=False),
@@ -395,6 +396,16 @@ def main():
 
     params = module.params
 
+    # Set the default match pattern to either a match-all glob or
+    # regex depending on use_regex being set.  This makes sure if you
+    # set excludes: without a pattern pfilter gets something it can
+    # handle.
+    if not params['patterns']:
+        if params['use_regex']:
+            params['patterns'] = ['.*']
+        else:
+            params['patterns'] = ['*']
+
     filelist = []
 
     if params['age'] is None:
diff --git a/test/integration/targets/find/tasks/main.yml b/test/integration/targets/find/tasks/main.yml
index 2678c954127621..91d924719e4d3f 100644
--- a/test/integration/targets/find/tasks/main.yml
+++ b/test/integration/targets/find/tasks/main.yml
@@ -251,3 +251,24 @@
           # dir contents are considered until the depth exceeds the requested depth
           # there are 8 files/directories in the requested depth and 4 that exceed it by 1
           - files_with_depth.examined == 12
+- name: exclude with regex
+  find:
+    paths: "{{ output_dir_test }}"
+    recurse: yes
+    use_regex: true
+    exclude: .*\.ogg
+  register: find_test3
+# Note that currently sane ways of doing this with map() or
+# selectattr() aren't available in centos6 era jinja2 ...
+- set_fact:
+    find_test3_list: >-
+      [ {% for f in find_test3.files %}
+      {{ f.path }}
+      {% if not loop.last %},{% endif %}
+      {% endfor %}
+      ]
+- debug: var=find_test3_list
+- name: assert we skipped the ogg file
+  assert:
+    that:
+      - '"{{ output_dir_test }}/e/f/g/h/8.ogg" not in find_test3_list'


Code review comments:
diff_hunk: @@ -41,6 +41,7 @@
             - This parameter expects a list, which can be either comma separated or YAML. If any of the
               patterns contain a comma, make sure to put them in a list to avoid splitting the patterns
               in undesirable ways.
+            - Defaults to '*', or '.*' when use_regex enabled
path: lib/ansible/modules/find.py
text: @user1:
```suggestion
            - Defaults to '*' when C(use_regex=False), or '.*' when C(use_regex=True).
```