Given the tree for testing
shell> tree /tmp/test
/tmp/test
├── override
│ ├── dir1
│ │ └── file_B.txt
│ ├── file_2.txt
│ └── file_4.txt
└── template
├── dir1
│ ├── file_A.txt
│ └── file_B.txt
├── file_1.txt
└── file_2.txt
4 directories, 7 files
shell> find /tmp/test -type f | sort | xargs cat
override dir1 file_B.txt
override file_2.txt
override file_4.txt
template dir1 file_A.txt
template dir1 file_B.txt
template file_1.txt
template file_2.txt
Declare the paths and the list of overlays
remote_path: /tmp/test
local_path: /mnt/test
overlays:
- /tmp/test/override
- /tmp/test/template
There are more options how to copy the files
- unionfs
Install the package
- name: Install unionfs
package:
name: unionfs-fuse
state: present
run_once: true
delegate_to: localhost
when: install|d(false)|bool
Mount unionfs
- name: Mount unionfs
block:
- file:
state: directory
path: "{{ local_path }}"
- debug:
msg: "dirs={{ overlays|product(['=ro'])|map('join')|join(':') }}"
- mount:
state: mounted
path: "{{ local_path }}"
fstype: unionfs
opts: "allow_other,dirs={{ overlays|product(['=ro'])|map('join')|join(':') }}"
src: dummy
run_once: true
delegate_to: localhost
The mount point was created
shell> tree /mnt/test
/mnt/test
├── dir1
│ ├── file_A.txt
│ └── file_B.txt
├── file_1.txt
├── file_2.txt
└── file_4.txt
1 directory, 5 files
The files from override replaced the files from template
shell> find /mnt/test -type f | sort | xargs cat
template dir1 file_A.txt
override dir1 file_B.txt
template file_1.txt
override file_2.txt
override file_4.txt
Create the remote directory and synchronize the files
- name: Create {{ remote_path }}
file:
state: directory
path: "{{ remote_path }}"
- name: Sync {{ local_path }} to {{ remote_path }}
synchronize:
src: "{{ local_path }}/"
dest: "{{ remote_path }}"
The files were synchronized to the remote host
shell> ssh admin@test_13 find /tmp/test -type f | sort
/tmp/test/dir1/file_A.txt
/tmp/test/dir1/file_B.txt
/tmp/test/file_1.txt
/tmp/test/file_2.txt
/tmp/test/file_4.txt
shell> ssh admin@test_13 'find /tmp/test -type f | sort | xargs cat'
template dir1 file_A.txt
override dir1 file_B.txt
template file_1.txt
override file_2.txt
override file_4.txt
Example of a complete playbook for testing
- hosts: all
become: true
vars:
remote_path: /tmp/test
local_path: /mnt/test
overlays:
- /tmp/test/override
- /tmp/test/template
tasks:
- name: Install unionfs
package:
name: unionfs-fuse
state: present
run_once: true
delegate_to: localhost
when: install|d(false)|bool
- name: Mount unionfs
block:
- file:
state: directory
path: "{{ local_path }}"
- debug:
msg: "dirs={{ overlays|product(['=ro'])|map('join')|join(':') }}"
- mount:
state: mounted
path: "{{ local_path }}"
fstype: unionfs
opts: "allow_other,dirs={{ overlays|product(['=ro'])|map('join')|join(':') }}"
src: dummy
run_once: true
delegate_to: localhost
- name: Create {{ remote_path }}
file:
state: directory
path: "{{ remote_path }}"
- name: Sync {{ local_path }} to {{ remote_path }}
synchronize:
src: "{{ local_path }}/"
dest: "{{ remote_path }}"
- name: Umount {{ local_path }}
mount:
state: unmounted
path: "{{ local_path }}"
run_once: true
delegate_to: localhost
when: umount|d(false)|bool
- Link the overlays
You can link the overlays on your own if you can't use unionfs. Declare the variables
dirs: "{{ dirs_out.results|json_query('[].stdout_lines')|flatten|unique }}"
files: "{{ files_out.results|json_query('[].stdout_lines') }}"
files_dict: "{{ dict(overlays|zip([files.0, files.1|difference(files.0)])) }}"
Create the directories and link the overlays
- name: Link overlays
block:
- name: Find directories
command: "sh -c 'cd {{ item }}; find * -type d'"
loop: "{{ overlays }}"
register: dirs_out
changed_when: false
- debug:
var: dirs|to_yaml
- name: Create directories
file:
state: directory
path: "{{ item }}"
loop: "{{ [local_path]|product(dirs)|map('path_join') }}"
- name: Find files
command: "sh -c 'cd {{ item }}; find * -type f'"
loop: "{{ overlays }}"
register: files_out
changed_when: false
- debug:
var: files|to_yaml
- debug:
var: files_dict|to_yaml
- name: Link files
file:
state: link
src: "{{ (item.0.key, item.1)|path_join }}"
dest: "{{ (local_path, item.1)|path_join }}"
with_subelements:
- "{{ files_dict|dict2items }}"
- value
run_once: true
delegate_to: localhost
shell> tree /mnt/test
/mnt/test
├── dir1
│ ├── file_A.txt -> /tmp/test/template/dir1/file_A.txt
│ └── file_B.txt -> /tmp/test/override/dir1/file_B.txt
├── file_1.txt -> /tmp/test/template/file_1.txt
├── file_2.txt -> /tmp/test/override/file_2.txt
└── file_4.txt -> /tmp/test/override/file_4.txt
1 directory, 5 files
Create the remote directory and synchronize the files
- name: Create {{ remote_path }}
file:
state: directory
path: "{{ remote_path }}"
- name: Sync {{ local_path }} to {{ remote_path }}
synchronize:
src: "{{ local_path }}/"
dest: "{{ remote_path }}"
copy_links: true
Example of a complete playbook for testing
- hosts: all
become: true
vars:
remote_path: /tmp/test
local_path: /mnt/test
overlays:
- /tmp/test/override
- /tmp/test/template
dirs: "{{ dirs_out.results|json_query('[].stdout_lines')|flatten|unique }}"
files: "{{ files_out.results|json_query('[].stdout_lines') }}"
files_dict: "{{ dict(overlays|zip([files.0, files.1|difference(files.0)])) }}"
tasks:
- name: Link overlays
block:
- name: Find directories
command: "sh -c 'cd {{ item }}; find * -type d'"
loop: "{{ overlays }}"
register: dirs_out
changed_when: false
- debug:
var: dirs|to_yaml
- name: Create directories
file:
state: directory
path: "{{ item }}"
loop: "{{ [local_path]|product(dirs)|map('path_join') }}"
- name: Find files
command: "sh -c 'cd {{ item }}; find * -type f'"
loop: "{{ overlays }}"
register: files_out
changed_when: false
- debug:
var: files|to_yaml
- debug:
var: files_dict|to_yaml
- name: Link files
file:
state: link
src: "{{ (item.0.key, item.1)|path_join }}"
dest: "{{ (local_path, item.1)|path_join }}"
with_subelements:
- "{{ files_dict|dict2items }}"
- value
run_once: true
delegate_to: localhost
- name: Create {{ remote_path }}
file:
state: directory
path: "{{ remote_path }}"
- name: Sync {{ local_path }} to {{ remote_path }}
synchronize:
src: "{{ local_path }}/"
dest: "{{ remote_path }}"
copy_links: true
- name: Unlink {{ local_path }}
file:
state: absent
path: "{{ (local_path, item.1)|path_join }}"
with_subelements:
- "{{ files_dict|dict2items }}"
- value
run_once: true
delegate_to: localhost
when: umount|d(false)|bool