49

我想递归地复制一个目录并将其中的所有.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删除后缀的模板的问题。

4

8 回答 8

54

我会这样做,假设顶部定义为“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
于 2013-08-02T12:18:47.527 回答
12

我们使用我们的 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

诀窍(如您所见)是 template 和 with_items 在注册结果中具有不同的属性。然后将它们转换为您管理的文件列表,然后获取目录列表并删除不在该列表中的文件。

如果您已经有文件列表,则可以用更少的代码完成。但在这种情况下,我正在创建一个索引列表,因此还需要使用 map 创建列表。

于 2015-05-21T11:28:09.550 回答
6

我想分享我对这个案例的经验。

Ansible 从 2.2 开始有了 with_filetree 循环,提供了上传目录、链接、静态文件甚至(!)模板的简单方法。这是保持我的配置目录同步的最佳方式。

- 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"

接下来我们可能想从配置目录中删除未使用的文件。这很简单。我们收集上传文件的列表和远程服务器上存在的文件,然后删除差异。

但是我们可能希望在配置目录中有非托管文件。我使用-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
于 2017-07-11T11:00:01.470 回答
3

这是我想出的:

- 模板:src=/source/directory{{ item }}.j2 dest=/target/directory/{{ item }}
  注册:template_results
  with_items:
    - a_list.txt
    - of_all.txt
    - 模板.txt
- 设置事实:
    managed_files: "{{ template_results.results|selectattr('invocation', 'defined')|map(attribute='invocation.module_args.dest')|list }}"

- 调试:
    var: managed_files
    详细程度:0

- 寻找:
    路径:“/目标/目录/”
    模式:“*.txt”
  注册:all_files
- 设置事实:
    files_to_delete: "{{ all_files.files|map(attribute='path')|difference(managed_files) }}"

- 调试:
    var: all_files
    详细程度:0
- 调试:
    var: files_to_delete
    详细程度:0

- 文件:path={{ item }} state=absent
  with_items: "{{ files_to_delete }}"
  • 这会生成模板(无论您想要哪种方式),并将结果记录在“template_results”中
  • 结果被破坏以获得每个模板的“dest”的简单列表。跳过的模板(由于 when 条件,未显示)没有“调用”属性,因此它们被过滤掉了。
  • 然后使用“查找”来获取所有应该不存在的文件的列表,除非特别编写。
  • 然后对其进行修改以获取存在的文件的原始列表,然后删除“应该存在”的文件。
  • 然后删除剩余的“files_to_delete”。

优点:您可以避免在删除过程中出现多个“跳过”的条目。

缺点:如果您想在查找/删除之前执行多个模板任务,则需要连接每个 template_results.results。

于 2017-08-11T20:07:13.660 回答
2

可能有几种方法可以处理这个问题,但是是否可以在模板步骤之前完全清空任务中的目标目录?或者可能将模板文件放入临时目录,然后在后续步骤中删除+重命名?

于 2013-05-05T22:41:15.550 回答
1

通常我不会删除文件,但-unmanaged会在其名称中添加后缀。示例 ansible 任务:

- 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- 这用于忽略错误。Ansibleignore_errors也可以工作,但在忽略错误之前,ansible 会在 ansible-playbook 运行期间将其显示为红色,这是不希望的(至少对我而言)。

然后我将 grep 命令的输出注册为变量。当 grep 显示任何输出时,我将此任务设置为已更改(该行changed_when对此负责)。

在下一个任务中,我迭代 grep 输出(即 grep 返回的文件名)并运行 rename 命令为每个文件添加后缀。

就这样。下次运行命令时,第一个任务应该是绿色的,第二个任务应该被跳过。

于 2014-05-06T16:16:59.883 回答
0

显然,目前这对于 ansible 是不可能的。我在 IRC 上与 mdehaan 进行了对话,归结为 ansible 没有资源的有向无环图,这使得这样的事情变得非常困难。

向 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
于 2013-08-02T12:43:23.870 回答
0

我正在使用 Ansible 版本 2.9.20

---
# tasks file for delete_unmanaged_files
- name: list files in dest
  shell: ls -1 dest/conf.d
  register: files_in_dest

- name: list files in src
  shell: ls -1 src/conf.d
  register: files_in_src

- name: Managed files - dest
  command: echo {{ item|replace('.j2','') }}
  with_items: "{{ files_in_dest.stdout_lines }}"
  register: managed_files_dest

- name: Managed files - src
  command: echo {{ item|replace('.j2','') }}
  with_items: "{{ files_in_src.stdout_lines }}"
  register: managed_files_src

- name: Convert src managed files to list
  set_fact: managed_files_src_list="{{ managed_files_src.results | map(attribute='stdout') | list }}"

- name: Delete unmanaged files in dest
  file: path=dest/conf.d/{{ item.stdout }} state=absent
  with_items: "{{ managed_files_dest.results }}"
  when: item.stdout not in managed_files_src_list

我认为根据此问题的用例,我发现上述解决方案可能会对您有所帮助。在这里,我创建了 6 个任务。

解释:

  • Task-1 & Task-2 将帮助将文件名存储在变量“files_in_dest”和“files_in_src”中。
  • Task-3 & Task-4 将继承来自 Task-1 & Task-2 的输出,然后替换 j2 文件(用例需要)。然后这些任务会将输出存储在“managed_files_dest”和“managed_files_src”变量中。
  • Task-5 会将“managed_files_src”的输出转换为列表,这样我们就可以将 src 目录中的所有当前文件(当前状态)存储在一个适当的或单个列表中,然后我们可以在下一个任务中使用这个列表来了解dest 目录中的非托管文件。
  • 任务 6 将删除 dest 中的非托管文件。
于 2021-08-08T16:24:09.147 回答