ansibleruntime-errorparameter-passingstring-formattingstat

Unable to get stat of existing directory in Ansible due to variable formatting issue


I'm trying to get the owner of the first directory passed to Ansible variable source_files.

We do not have control over the content of Ansible variable source_files as it is constructed through some OLAM API. I have hard coded it for this example.

Both the files of source_files variable exists as seen below:

[wluser@mylocalhost ~]$ ls -ltr '/tmp/my  private.txt' /tmp/files.sh  
-rw-r--r-- 1 wluser mygrp 0 Nov 12 21:52 /tmp/files.sh    
-rw-r--r-- 1 wluser mygrp 0 Nov 12 21:52 /tmp/my  private.txt

Playbook [wluser@mylocalhost ~]$ cat formatstring.yml

---
- name: "Play 1-Find the details here"

  hosts: localhost
  any_errors_fatal: True
  gather_facts: no

  tasks:

   - name: Set source_files variable
     set_fact:
       source_files: "'/tmp/my  private.txt','/tmp/files.sh'"
 
   - name: Print source_files
     debug:
       msg: "source_files: {{ source_files }}"
     
   - name: Loop Print source_files
     debug:
       msg: "{{ item }}"
     loop: "{{ source_files.split(',') }}"

   - name: Single Print source_files
     debug:
       msg: "{{ source_files.split(',')[0] }}"
   
   - name: Get directory owner
     ansible.builtin.stat:
       path: "{{ tomcat_home_item }}"
     register: dir_stat
     vars:
       tomcat_home_item: "{{ source_files.split(',')[0] | trim }}"

   - name: Store directory owner's UID in a variable
     set_fact:
       owner_uid: "{{ dir_stat.stat.pw_name | default('baduser') }}"

   - debug:
       msg: "Detected user is: {{ owner_uid }}"

   - name: Get directory owner
     ansible.builtin.stat:
       path: "{{ tomcat_home_item | regex_replace('^\\s*'|'\\s*$', '') }}"
     register: dir_stat
     vars:
       tomcat_home_item: "{{ source_files.split(',')[0] | trim }}"

   - name: Store directory owner's UID in a variable
     set_fact:
       owner_uid: "{{ dir_stat.stat.pw_name | default('baduser') }}"
 
   - debug:
       msg: "Detected user is: {{ owner_uid }}"

Output

[wluser@mylocalhost ~]$ ansible-playbook formatstring.yml -vvv
ansible-playbook 2.8.4
  config file = /etc/ansible/ansible.cfg
PLAYBOOK: formatstring.yml ****************************************************************************************************
1 plays in formatstring.yml

PLAY [Play 1-Find the details here] *******************************************************************************************
META: ran handlers

TASK [Set source_files variable] **********************************************************************************************
task path: /home/wluser/formatstring.yml:10
Sunday 12 November 2023  22:02:56 -0600 (0:00:00.044)       0:00:00.044 *******
ok: [localhost] => {
    "ansible_facts": {
        "source_files": "'/tmp/my  private.txt','/tmp/files.sh'"
    },
    "changed": false
}

TASK [Print source_files] *****************************************************************************************************
task path: /home/wluser/formatstring.yml:15
Sunday 12 November 2023  22:02:56 -0600 (0:00:00.018)       0:00:00.062 *******
ok: [localhost] => {
    "msg": "source_files: '/tmp/my  private.txt','/tmp/files.sh'"
}

TASK [Loop Print source_files] ************************************************************************************************
task path: /home/wluser/formatstring.yml:19
Sunday 12 November 2023  22:02:56 -0600 (0:00:00.017)       0:00:00.080 *******
ok: [localhost] => (item='/tmp/my  private.txt') => {
    "msg": "'/tmp/my  private.txt'"
}
ok: [localhost] => (item='/tmp/files.sh') => {
    "msg": "'/tmp/files.sh'"
}

TASK [Single Print source_files] **********************************************************************************************
task path: /home/wluser/formatstring.yml:24
Sunday 12 November 2023  22:02:56 -0600 (0:00:00.033)       0:00:00.114 *******
ok: [localhost] => {
    "msg": "'/tmp/my  private.txt'"
}

TASK [Get directory owner] ****************************************************************************************************
task path: /home/wluser/formatstring.yml:29
Sunday 12 November 2023  22:02:56 -0600 (0:00:00.023)       0:00:00.137 *******
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: wluser
ok: [localhost] => {
    "changed": false,
    "invocation": {
        "module_args": {
            "checksum_algorithm": "sha1",
            "follow": false,
            "get_attributes": true,
            "get_checksum": true,
            "get_md5": null,
            "get_mime": true,
            "path": "'/tmp/my  private.txt'"
        }
    },
    "stat": {
        "exists": false
    }
}

TASK [Store directory owner's UID in a variable] ******************************************************************************
task path: /home/wluser/formatstring.yml:36
Sunday 12 November 2023  22:02:57 -0600 (0:00:00.352)       0:00:00.489 *******
ok: [localhost] => {
    "ansible_facts": {
        "owner_uid": "baduser"
    },
    "changed": false
}

TASK [debug] ******************************************************************************************************************
task path: /home/wluser/formatstring.yml:40
Sunday 12 November 2023  22:02:57 -0600 (0:00:00.020)       0:00:00.510 *******
ok: [localhost] => {
    "msg": "Detected user is: baduser"
}

TASK [Get directory owner] ****************************************************************************************************
task path: /home/wluser/formatstring.yml:43
Sunday 12 November 2023  22:02:57 -0600 (0:00:00.019)       0:00:00.529 *******
fatal: [localhost]: FAILED! => {
    "msg": "template error while templating string: expected token 'name', got 'string'. String: {{ tomcat_home_item | regex_replace('^\\\\s*'|'\\\\s*$', '') }}"
}

NO MORE HOSTS LEFT ************************************************************************************************************
PLAY RECAP ********************************************************************************************************************
localhost                  : ok=7    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

As you can see, I'm getting baduser instead of correct user wluser due to quoting/formatting issues with variable source_files.

I then attempted to remove the quotes but that got me runtime error.

Can you please suggest how can i get the stats to the file considering the formatting of source_files mentioned initially in the playbook cannot be changed.


Solution

  • How can I get the stats to the file considering the formatting of source_files

    Assuming the output of the ls -ltr command is correct, as well the content of source_files. For files

    ~/test$ ls -ltr 'test file' 'test path' 'test path/test file'
    -rw-r--r--. 1 ansible_user users 0 Nov 13 08:00 test file
    -rw-r--r--. 1 ansible_user users 0 Nov 13 08:00 test path/test file
    
    test path:
    total 0
    -rw-r--r--. 1 ansible_user users 0 Nov 13 08:00 test file
    

    a minimal example playbook

    
    ```---
    - hosts: localhost
      become: false
      gather_facts: false
    
      vars:
    
        FILE: 'test file'
        PATH: 'test path'
    
      tasks:
    
      - set_fact:
          SOURCE_FILES: "'test file','test path/test file'"
    
      - stat:
          path: 'test file'
        register: result
    
      - debug:
          msg: "{{ result.stat.path }}"
    
      - stat:
          path: "{{ FILE }}"
        register: result
    
      - debug:
          msg: "{{ result.stat.path }}"
    
      - stat:
          path: "{{ PATH }}/{{ FILE }}"
        register: result
    
      - debug:
          msg: "{{ result.stat.path }}"
    
      - debug:
          msg:
            - "Content: {{ SOURCE_FILES }}"
            - "Type: {{ SOURCE_FILES | type_debug }}"
            - "List of files: {{ SOURCE_FILES | split(',') }}"
    
      - stat:
          path: "{{ SOURCE_FILES | split(',') | first | replace(\"'\",'') }}"
        register: result
    
      - debug:
          msg: "{{ result.stat.path }}"
    
      - stat:
          path: "{{ SOURCE_FILES | split(',') | last | replace(\"'\",'') }}"
        register: result
    
      - debug:
          msg: "{{ result.stat.path }}"
    

    will result into an output of

    TASK [stat] ******************************************************
    ok: [localhost]
    
    TASK [debug] *****************************************************
    ok: [localhost] =>
      msg: test file
    
    TASK [stat] ******************************************************
    ok: [localhost]
    
    TASK [debug] *****************************************************
    ok: [localhost] =>
      msg: test file
    
    TASK [stat] ******************************************************
    ok: [localhost]
    
    TASK [debug] *****************************************************
    ok: [localhost] =>
      msg: test path/test file
    
    TASK [debug] *****************************************************
    ok: [localhost] =>
      msg:
      - 'Content: ''test file'',''test path/test file'''
      - 'Type: AnsibleUnicode'
      - 'List of files: [u"''test file''", u"''test path/test file''"]'
    
    TASK [stat] ******************************************************
    ok: [localhost]
    
    TASK [debug] *****************************************************
    ok: [localhost] =>
      msg: test file
    
    TASK [stat] ******************************************************
    ok: [localhost]
    
    TASK [debug] *****************************************************
    ok: [localhost] =>
      msg: test path/test file
    

    If the content of source_files is a Comma Separated Value (CSV) string with explicit quoted values and quote character single quote (') (... annot: which it makes non-RFC conform then), to make a list of file names out of it again

    • Split on delimiter comma (,) via split(',')
    • Remove the explicit quote character single quote (') for all values via replace(\"'\",'')

    Please take not that files can not contain single quote anymore as that would run into an error again. It would fail in example for

    ~/test$ touch "andrew's.file"
    ~/test$ ls -al and*
    -rw-r--r--. 1 ansible_user users 0 Nov 13 09:30 andrew's.file
    -rw-r--r--. 1 ansible_user users 0 Nov 13 09:30 and.yml
    

    Thanks To