modules - ansible-¿Eliminar archivos no administrados del directorio?
command ansible module (7)
Aparentemente esto no es posible con ansible en este momento. Tuve una conversación con mdehaan en IRC y todo se reduce a no poder tener un gráfico acíclico de recursos dirigido , lo que hace que esto sea muy difícil.
Al pedirle a mdehaan un ejemplo, por ejemplo, administrar con autoridad un directorio sudoers.d, se le ocurrieron estas cosas:
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
Quiero copiar de forma recursiva sobre un directorio y renderizar todos los archivos .j2 allí como plantillas. Para esto estoy usando actualmente las siguientes líneas:
- template: >
src=/src/conf.d/{{ item }}
dest=/dest/conf.d/{{ item|replace(''.j2'','''') }}
with_lines: find /src/conf.d/ -type f -printf "%P/n"
Ahora estoy buscando una manera de eliminar archivos no administrados de este directorio. Por ejemplo, si /src/conf.d/
un archivo / plantilla de /src/conf.d/
también quiero que Ansible lo elimine de /dest/conf.d/
.
Hay alguna manera de hacer esto? Intenté juguetear con rsync --delete
, pero tengo un problema con las plantillas que eliminan su sufijo .j2
.
Aquí hay algo que se me ocurrió:
- 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 }}"
- Esto genera las plantillas (como lo desee) y registra los resultados en ''template_results''
- Los resultados se modifican para obtener una lista simple del "destino" de cada plantilla. Las plantillas omitidas (debido a una condición cuando no se muestra) no tienen un atributo de "invocación", por lo que se eliminan.
- "buscar" se usa para obtener una lista de todos los archivos que deberían estar ausentes, a menos que se escriba específicamente.
- luego se modifica para obtener una lista sin procesar de los archivos presentes, y luego se eliminan los archivos "supuestos para estar allí".
- Los restantes "files_to_delete" se eliminan.
Pros: Evitas que se muestren varias entradas "omitidas" durante las eliminaciones.
Contras: deberá concatenar cada plantilla_resultados.resultados si desea realizar varias tareas de plantilla antes de realizar la búsqueda / eliminación.
Hacemos esto con nuestros archivos nginx, ya que queremos que estén en un orden especial, provienen de plantillas, pero eliminamos las que no están administradas, esto funciona:
# 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
El truco (como puede ver) es que la plantilla y with_items tienen diferentes atributos en los resultados del registro. Luego los convierte en una lista de archivos que administra y luego obtiene una lista del directorio y elimina los que no están en esa lista.
Se podría hacer con menos código si ya tiene una lista de archivos. Pero en este caso estoy creando una lista indexada, así que necesito crear la lista también con el mapa.
Lo haría de esta manera, asumiendo que una variable definida como ''archivos_del_archivos'' es una lista.
- 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
Por lo general, no -unmanaged
archivos, pero -unmanaged
sufijo no -unmanaged
a su nombre. Ejemplos de tareas ansibles:
- 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
EXPLICACIÓN
Usos del comando Grep:
-
-r
hacer búsqueda recursiva -
--include=/*.list
- solo toma archivos con extensión .list durante la búsqueda recursiva -
-L ''^# Ansible''
: muestra los nombres de archivos que no tienen una línea que comience por ''# Ansible'' -
|| true
|| true
- esto se usa para ignorar los errores. Losignore_errors
de Ansible también funcionan, pero antes de ignorar el error, ansible lo mostrará en color rojo durante la ejecución de ansible-playbook, que es indeseable (al menos para mí).
Luego registro la salida del comando grep como una variable. Cuando grep muestra cualquier salida, configuro esta tarea como modificada (la línea changed_when
sea responsable de esto).
En la siguiente tarea, itero la salida de grep (es decir, los nombres de archivo devueltos por grep) y ejecuto el comando rename para agregar un sufijo a cada archivo.
Eso es todo. La próxima vez que ejecute el comando, la primera tarea debe ser verde y la segunda omitida.
Puede haber un par de maneras de manejar esto, pero ¿sería posible vaciar completamente el directorio de destino en una tarea antes del paso de la plantilla? ¿O tal vez colocar los archivos de plantilla en un directorio temporal y luego eliminar + cambiar el nombre en un paso posterior?
Quiero compartir mi experiencia con este caso.
Ansible de la versión 2.2 tiene un bucle with_filetree que proporciona una forma sencilla de cargar dirs, enlaces, archivos estáticos e incluso (!) Plantillas. Es la mejor manera de mantener mi directorio de configuración sincronizado.
- 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"
A continuación, es posible que desee eliminar los archivos no utilizados de config dir. Es sencillo. Recopilamos una lista de los archivos cargados y los archivos existentes en el servidor remoto, luego elimine la diferencia.
Pero podemos querer tener archivos no administrados en el directorio de configuración. He usado la funcionalidad de find
de la -prune
para evitar borrar carpetas con archivos no administrados.
PS _ (Y) _ seguro después de haber eliminado algunos archivos no administrados
- 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