Is there a way to replace files that are copied, when such file exists on host similarly as you can do with variables (you can override variable on host, to use custom value if needed)?
Currently I'm doing like this:
- name: Check if path exists
set_fact:
file_nginx_logo: "files/{{ inventory_hostname }}/maintenance_logo.png"
- name: Create Nginx Maintenance Image file (custom).
ansible.builtin.copy:
src: "files/{{ inventory_hostname }}/maintenance_logo.png"
dest: "{{ path_nginx_logo }}"
when: file_nginx_logo is file
- name: Create Nginx Maintenance Image file (default).
ansible.builtin.copy:
src: maintenance_logo.png
dest: "{{ path_nginx_logo }}"
when: file_nginx_logo is not file
But this is quite ugly as I need 3 (or 2) tasks to copy one file.. Maybe there is a place in inventory
path where if you put it, it would overwrite file defined on role?
I mean, something like:
files/my-host/my-file.txt
roles/my-role/files/my-file.txt
So if you call my-file.txt
on my-role
it would use the one from my-host
if such file exists there?..
CodePudding user response:
Here's a full blown example. Adapt to you needs.
Global test project stucture:
.
├── files
│ └── host2
│ └── maintenance_logo.txt
├── inventories
│ └── default
│ └── hosts.yml
├── roles
│ └── test
│ ├── files
│ │ └── maintenance_logo.txt
│ └── tasks
│ └── main.yml
└── test_playbook.yml
The test inventory in inventories/default/hosts.yml
points to localhost in all cases:
all:
vars:
ansible_connection: local
hosts:
host1:
host2:
As you can see, the role holds the default logo where we will fallback whereas there is specific logo for host2
in the files
dir adjacent to the playbook. (I used txt source files for this example for convenience but this will work with any type of file)
In roles/test/tasks/main.yml
we have a single task:
- name: Copy maintenance logo to target
vars:
candidate_files:
- "{{ inventory_hostname }}/maintenance_logo.txt"
- maintenance_logo.txt
ansible.builtin.copy:
src: "{{ item }}"
dest: /tmp/{{ inventory_hostname }}.png
loop: "{{ q('ansible.builtin.first_found', candidate_files) }}"
The key here is using the ansible.builtin.first_found
lookup which will exit on first found file (as its name obviously suggest...) and return its name.
This lookup obeys the ansible search path so we can use the file name relative to any correctly placed files
folder anywhere in playbook directory, roles, etc...
Note that I used inventory_hostname
in the destination file names as I faked all my test hosts to local in my inventory. You obviously do not have to do that on your separate targets (i.e. use whatever target name you wish)
Now the test_playbook.yml
to wrap it all up
---
- hosts: all
gather_facts: false
tasks:
- ansible.builtin.import_role:
name: test
Which gives:
$ ansible-playbook -i inventories/default/ test_playbook.yml
PLAY [all] *****************************************************************************************************************************************************************************************************************************
TASK [test : Copy maintenance logo to target] ******************************************************************************************************************************************************************************************
changed: [host2] => (item=/tmp/test/files/host2/maintenance_logo.txt)
changed: [host1] => (item=/tmp/test/roles/test/files/maintenance_logo.txt)
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
host1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0