Home > Mobile >  Use host specific file if it exists instead of the one from role?
Use host specific file if it exists instead of the one from role?

Time:01-08

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
  • Related