Ansible - удалить неуправляемые файлы из каталога?
Я хочу рекурсивно скопировать через каталог и все файлы .j2 в качестве шаблонов. Для этого в настоящее время я использую следующие строки:
- template: >
src=/src/conf.d/{{ item }}
dest=/dest/conf.d/{{ item|replace('.j2','') }}
with_lines: find /src/conf.d/ -type f -printf "%P\n"
Теперь я ищу способ удалить неуправляемые файлы из этого каталога. Например, если я удаляю файл/шаблон из /src/conf.d/
, я хочу, чтобы Ansible удалял его из /dest/conf.d/
.
Есть ли способ сделать это? Я пробовал работать с rsync --delete
, но там у меня возникла проблема с шаблонами, которые удалили их суффикс .j2
.
Ответы
Ответ 1
Я сделал бы это так, предполагая, что переменная, определенная как "managed_files" вверху, является списком.
- shell: ls -1 /some/dir
register: contents
- file: path=/some/dir/{{ item }} state=absent
with_items: contents.stdout_lines
when: item not in managed_files
Ответ 2
Мы делаем это с нашими файлами nginx, так как мы хотим, чтобы они были в специальном порядке, поступали из шаблонов, но удаляли неуправляемые из них:
# loop through the nginx sites array and create a conf for each file in order
# file will be name 01_file.conf, 02_file.conf etc
- name: nginx_sites conf
template: >
src=templates/nginx/{{ item.1.template }}
dest={{ nginx_conf_dir }}/{{ '%02d' % item.0 }}_{{ item.1.conf_name|default(item.1.template) }}
owner={{ user }}
group={{ group }}
mode=0660
with_indexed_items: nginx_sites
notify:
- restart nginx
register: nginx_sites_confs
# flatten and map the results into simple list
# unchanged files have attribute dest, changed have attribute path
- set_fact:
nginx_confs: "{{ nginx_sites_confs.results|selectattr('dest', 'string')|map(attribute='dest')|list + nginx_sites_confs.results|selectattr('path', 'string')|map(attribute='path')|select|list }}"
when: nginx_sites
# get contents of conf dir
- shell: ls -1 {{ nginx_conf_dir }}/*.conf
register: contents
when: nginx_sites
# so we can delete the ones we don't manage
- name: empty old confs
file: path="{{ item }}" state=absent
with_items: contents.stdout_lines
when: nginx_sites and item not in nginx_confs
Трюк (как вы можете видеть) заключается в том, что шаблон и with_items имеют разные атрибуты в результатах регистра. Затем вы превращаете их в список файлов, которыми вы управляете, а затем получаете список каталога и удаляете те, которые не входят в этот список.
Может быть сделано с меньшим количеством кода, если у вас уже есть список файлов. Но в этом случае я создаю индексированный список, поэтому вам нужно создать список и с картой.
Ответ 3
Я хочу поделиться своим опытом с этим случаем.
Необязательный из 2.2 имеет loop_filetree loop, который обеспечивает простой способ загрузки dirs, ссылок, статических файлов и даже (!) шаблонов. Это лучший способ сохранить синхронизацию моего конфигурационного файла.
- name: etc config - Create directories
file:
path: "{{ nginx_conf_dir }}/{{ item.path }}"
state: directory
mode: 0755
with_filetree: etc/nginx
when: item.state == 'directory'
- name: etc config - Creating configuration files from templates
template:
src: "{{ item.src }}"
dest: "{{ nginx_conf_dir }}/{{ item.path | regex_replace('\\.j2$', '') }}"
mode: 0644
with_filetree: etc/nginx
when:
- item.state == "file"
- item.path | match('.+\.j2$') | bool
- name: etc config - Creating staic configuration files
copy:
src: "{{ item.src }}"
dest: "{{ nginx_conf_dir }}/{{ item.path }}"
mode: 0644
with_filetree: etc/nginx
when:
- item.state == "file"
- not (item.path | match('.+\.j2$') | bool)
- name: etc config - Recreate symlinks
file:
src: "{{ item.src }}"
dest: "{{ nginx_conf_dir }}/{{ item.path }}"
state: link
force: yes
mode: "{{ item.mode }}"
with_filetree: etc/nginx
when: item.state == "link"
Далее мы можем удалить неиспользуемые файлы из каталога config. Это просто.
Мы собираем список загруженных файлов и файлов на удаленном сервере, затем удаляем diffrence.
Но мы можем захотеть иметь неуправляемые файлы в каталоге config.
Я использовал -prune
функциональность find
, чтобы избежать очистки папок с неуправляемыми файлами.
PS _ (Y) _ уверен, что после удаления некоторых неуправляемых файлов
- name: etc config - Gathering managed files
set_fact:
__managed_file_path: "{{ nginx_conf_dir }}/{{ item.path | regex_replace('\\.j2$', '') }}"
with_filetree: etc/nginx
register: __managed_files
- name: etc config - Convert managed files to list
set_fact: managed_files="{{ __managed_files.results | map(attribute='ansible_facts.__managed_file_path') | list }}"
- name: etc config - Gathering exist files (excluding .ansible_keep-content dirs)
shell: find /etc/nginx -mindepth 1 -type d -exec test -e '{}/.ansible_keep-content' \; -prune -o -print
register: exist_files
changed_when: False
- name: etc config - Delete unmanaged files
file: path="{{ item }}" state=absent
with_items: "{{ exist_files.stdout_lines }}"
when:
- item not in managed_files
Ответ 4
Возможно, существует несколько способов справиться с этим, но возможно ли полностью очистить целевой каталог в задаче до шага шаблона? Или, возможно, отпустите шаблонные файлы во временный каталог, а затем удалите + переименовать на следующем шаге?
Ответ 5
Обычно я не удаляю файлы, но добавляю суффикс -unmanaged
к его имени.
Примеры возможных задач:
- name: Get sources.list.d files
shell: grep -r --include=\*.list -L '^# Ansible' /etc/apt/sources.list.d || true
register: grep_unmanaged
changed_when: grep_unmanaged.stdout_lines
- name: Add '-unmanaged' suffix
shell: rename 's/$/-unmanaged/' {{ item }}
with_items: grep_unmanaged.stdout_lines
ОБЪЯСНЕНИЕ
Команда Grep использует:
-
-r
сделать рекурсивный поиск
-
--include=\*.list
- брать только файлы
с расширением .list во время рекурсивного поиска
-
-L '^# Ansible'
- отображать имена файлов, у которых нет строки, начинающейся с '# Ansible'
-
|| true
- это используется для игнорирования ошибок. Ansible ignore_errors
также работает, но прежде, чем игнорировать ошибку, она будет отображаться красным цветом во время запуска загружаемого файла
что нежелательно (по крайней мере для меня).
Затем я регистрирую вывод команды grep как переменной. Когда grep отображает любой выход, я задал эту задачу как измененную (для нее отвечает строка changed_when
).
В следующей задаче я повторяю вывод grep (т.е. имена файлов, возвращаемые grep) и запускаю команду rename, чтобы добавить суффикс в каждый файл.
Это все. В следующий раз, когда вы запустите команду, первая задача должна быть зеленой, а вторая пропущена.
Ответ 6
Здесь что-то я придумал:
- template: src=/source/directory{{ item }}.j2 dest=/target/directory/{{ item }}
register: template_results
with_items:
- a_list.txt
- of_all.txt
- templates.txt
- set_fact:
managed_files: "{{ template_results.results|selectattr('invocation', 'defined')|map(attribute='invocation.module_args.dest')|list }}"
- debug:
var: managed_files
verbosity: 0
- find:
paths: "/target/directory/"
patterns: "*.txt"
register: all_files
- set_fact:
files_to_delete: "{{ all_files.files|map(attribute='path')|difference(managed_files) }}"
- debug:
var: all_files
verbosity: 0
- debug:
var: files_to_delete
verbosity: 0
- file: path={{ item }} state=absent
with_items: "{{ files_to_delete }}"
- Это создает шаблоны (как бы то ни было), и записывает результаты в "template_results"
- Результаты искажены, чтобы получить простой список "dest" каждого шаблона. Пропущенные шаблоны (из-за того, что условие, не показано) не имеют атрибута "invocation", поэтому они отфильтровываются.
- "find" затем используется для получения списка всех файлов, которые должны отсутствовать, если они специально не написаны.
- Затем это искажается, чтобы получить необработанный список файлов, а затем файлы "предполагается, что они есть".
- Остальные "files_to_delete" затем удаляются.
Плюсы: вы избегаете появления нескольких "пропущенных" записей во время удаления.
Минусы: вам нужно объединить каждый template_results.results, если вы хотите выполнить несколько задач шаблона, прежде чем выполнять поиск/удаление.
Ответ 7
По-видимому, это невозможно в настоящее время невозможно. У меня был разговор с mdehaan на IRC, и это сводится к тому, что он не имеет направленный ациклический график для ресурсов, что делает такие вещи очень тяжелыми.
Запрос mdehaan для примера, например. авторитетно управляя каталогом sudoers.d, он придумал следующие вещи:
14:17 < mdehaan> Robe: http://pastebin.com/yrdCZB0y
14:19 < Robe> mdehaan: HM
14:19 < Robe> mdehaan: that actually looks relatively sane
14:19 < mdehaan> thanks :)
14:19 < Robe> the problem I'm seeing is that I'd have to gather the managed files myself
14:19 < mdehaan> you would yes
14:19 < mdehaan> ALMOST
14:20 < mdehaan> you could do a fileglob and ... well, it would be a little gross
[..]
14:32 < mdehaan> eh, theoretical syntax, nm
14:33 < mdehaan> I could do it by writing a lookup plugin that filtered a list
14:34 < mdehaan> http://pastebin.com/rjF7QR24
14:34 < mdehaan> if that plugin existed, for instance, and iterated across lists in A that were also in B