Repository: ansible/ansible
Pull request: Add complex example of fileglob with loop
##### SUMMARY
<!--- Describe the change below, including rationale and design decisions -->
Fixes #48788
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Docs Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
`docs/docsite/rst/user_guide/complex_data_manipulation.rst`

Problem statement:
loop w/ fileglob does not accept lists
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Explain the problem briefly below -->
When trying to replace all `with_whatever:` with `loop: q()` I ran into the quirk that `q("fileglob", …)` does not accept lists and fails with
```
An unhandled exception occurred while running the lookup plugin 'fileglob'. Error was a <type 'exceptions.AttributeError'>, original message: 'list' object has no attribute 'rfind'"
```
Maybe I'm missing something. In that case, please be so kind and give me a working example, thanks!

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
fileglob

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.7.1
python version = 2.7.15 (default, Oct 15 2018, 15:24:06) [GCC 8.1.1 20180712 (Red Hat 8.1.1-5)]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
n/a
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
Fedora 28, x86_64

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
```bash
mkdir -p files/{foo,bar}
touch files/{foo,bar}/{baz,yatta}.conf
```
<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: localhost
  gather_facts: no
  vars:
    mygroups:
      - foo
      - bar
  tasks:
    - debug: var=item
      with_fileglob: '{{mygroups|map("regex_replace", "^(.*)$", "files/\1/*.conf")|list}}'

    - debug: var=item
      loop: '{{q("fileglob",
        mygroups|map("regex_replace", "^(.*)$", "files/\1/*.conf")|list
        )}}'
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
```
TASK [debug] ******************************************************************************                                                                                          
ok: [localhost] => (item=/tmp/ansible/files/foo/yatta.conf) => {                                                                                                      
    "item": "/tmp/ansible/files/foo/yatta.conf"                                                                                                                       
}
ok: [localhost] => (item=/tmp/ansible/files/foo/baz.conf) => {
    "item": "/tmp/ansible/files/foo/baz.conf"
}
ok: [localhost] => (item=/tmp/ansible/files/bar/yatta.conf) => {
    "item": "/tmp/ansible/files/bar/yatta.conf"
}
ok: [localhost] => (item=/tmp/ansible/files/bar/baz.conf) => {
    "item": "/tmp/ansible/files/bar/baz.conf"
}
TASK [debug] ******************************************************************************                                                                                          
ok: [localhost] => (item=/tmp/ansible/files/foo/yatta.conf) => {                                                                                                      
    "item": "/tmp/ansible/files/foo/yatta.conf"                                                                                                                       
}
ok: [localhost] => (item=/tmp/ansible/files/foo/baz.conf) => {
    "item": "/tmp/ansible/files/foo/baz.conf"
}
ok: [localhost] => (item=/tmp/ansible/files/bar/yatta.conf) => {
    "item": "/tmp/ansible/files/bar/yatta.conf"
}
ok: [localhost] => (item=/tmp/ansible/files/bar/baz.conf) => {
    "item": "/tmp/ansible/files/bar/baz.conf"
}
```

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
```
TASK [debug] ******************************************************************************                                                                                          
ok: [localhost] => (item=/tmp/ansible/files/foo/yatta.conf) => {                                                                                                      
    "item": "/tmp/ansible/files/foo/yatta.conf"                                                                                                                       
}
ok: [localhost] => (item=/tmp/ansible/files/foo/baz.conf) => {
    "item": "/tmp/ansible/files/foo/baz.conf"
}
ok: [localhost] => (item=/tmp/ansible/files/bar/yatta.conf) => {
    "item": "/tmp/ansible/files/bar/yatta.conf"
}
ok: [localhost] => (item=/tmp/ansible/files/bar/baz.conf) => {
    "item": "/tmp/ansible/files/bar/baz.conf"
}
TASK [debug] ******************************************************************************
fatal: [localhost]: FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'fileglob'. Error was a <type 'exceptions.AttributeError'>, original message: 
'list' object has no attribute 'rfind'"}
```

Hints from issue discussion:
Hi @philfry, thank you for submitting this issue!

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: thank_submitter --->
Files identified in the description:
* [lib/ansible/plugins/lookup/fileglob.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/lookup/fileglob.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 --->
```yaml
    - debug: var=item
      loop: '{{q("fileglob", *mylist)}}'
      vars:
        mylist: '{{mygroups|map("regex_replace", "^(.*)$", "files/\1/*.conf")|list}}'
```
That's a pretty nice one, thanks!
Is this the intended/preferred way or some kind of a hack?
the 'working' way
@philfry This is the intended way for this work. We need to create documentation for this.
will add example to  https://docs.ansible.com/ansible/latest/user_guide/complex_data_manipulation.html

Commit message: Explain Python unpacking notation

Patch to review:
diff --git a/docs/docsite/rst/user_guide/complex_data_manipulation.rst b/docs/docsite/rst/user_guide/complex_data_manipulation.rst
index 8466a3df2ceeae..8a8ebec538209d 100644
--- a/docs/docsite/rst/user_guide/complex_data_manipulation.rst
+++ b/docs/docsite/rst/user_guide/complex_data_manipulation.rst
@@ -136,6 +136,29 @@ Another way is to avoid adding elements to the list in the first place, so you c
           - "bar"
 
 
+Custom Fileglob Based on a Variable
+-----------------------------------
+
+This example uses `Python argument list unpacking <https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists>`_ to create a custom list of fileglobs based on a variable.
+
+.. code-block:: YAML+Jinja
+  :caption: Using fileglob with a list based on a variable.
+
+    - hosts: all
+      vars:
+        mygroups
+          - prod
+          - web
+      tasks:
+        - name: Copy a glob of files based on a list of groups
+          copy:
+            src: "{{ item }}"
+            dest: "/tmp/{{ item }}"
+          loop: '{{ q("fileglob", *globlist) }}'
+          vars:
+            globlist: '{{ mygroups | map("regex_replace", "^(.*)$", "files/\1/*.conf" | list }}'
+
+
 .. _complex_type_transformations:
 
 Complex Type transformations


Code review comments:
diff_hunk: @@ -136,6 +136,29 @@ Another way is to avoid adding elements to the list in the first place, so you c
           - "bar"
 
 
+Custom Fileglob Based on a Variable
+-----------------------------------
+
+This example uses `Python argument list unpacking <https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists>`_ to create a custom list of fileglobs based on a variable.
+
+.. code-block:: YAML+Jinja
+  :caption: Using fileglob with a list based on a variable.
+
+    - hosts: all
+      vars:
+        mygroups
+          - prod
+          - web
+      tasks:
+        - name: Copy a glob of files based on a list of groups
+          copy:
+            src: "{{ item }}"
+            dest: "/tmp/{{ item }}"
+          loop: '{{ q("fileglob", *globlist) }}'
+          vars:
+            globlist: '{{ mygroups | map("regex_replace", "^(.*)$", "files/\1/*.conf" | list }}'
path: docs/docsite/rst/user_guide/complex_data_manipulation.rst
text: @user1:
```suggestion
            globlist: '{{ mygroups | map("regex_replace", "^(.*)$", "files/\1/*.conf") | list }}'
```

@user2:
^ da missing parens ... sorry, thought it was obvious once pointed out ... forget not everyone has Jinja2 parser integrated neural pathways.

@author:
Gah. Sorry I couldn't find that.