在 Ansible 中包含一组任务的块上出现问题循环

Issue looping on block containing a set of tasks in Ansible

提问人:Ashar 提问时间:11/18/2019 最后编辑:β.εηοιτ.βεAshar 更新时间:8/26/2023 访问量:49294

问:

我需要检查一个名为 deploy.db 的文件是否存在。如果它不存在,我需要执行一组我使用块的任务。

以下是我运行剧本的方式

ansible-playbook test.yml \
  -e Layer=APP \
  -e BASEPATH="/logs" \
  -e Filenames="file1,file2,file3"

以下是test.yml的剧本:

---
- name: "Play 1"
  hosts: localhost
  gather_facts: false
  tasks:
   - name: Construct 
     debug:
        msg: "Run"
   - block:
       - stat: path="{{ BASEPATH }}/deploy.db"
         register: currdb
       - file: path="{{ BASEPATH }}/deploy.db" state=touch recurse=no
         when: currdb.stat.exists == False
       - shell: "echo done>>{{ BASEPATH }}/deploy.db"
         when: currdb.stat.exists == False
     when: Layer == 'APP'
     with_items:
       - "{{ Filenames.split(',') }}" 

我在运行 playbook 时遇到以下错误:

ERROR! 'with_items' is not a valid attribute for a Block

The error appears to be in '/app/test.yml': line 9, column 6, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

   - block:
     ^ here 

经过一番研究,我了解到既不支持也不支持,解决方案是包含一个任务文件。with_itemsloopblock

但是,我不确定如何让它发挥作用。你能建议我需要哪些调整才能使我的剧本发挥作用吗?

考虑到我使用的是最新版本的 Ansible,还有其他解决方案吗?

ansible

评论

5赞 bitinerant 11/30/2019
早在 2015 年就请求在 Ansible 中支持此功能,并进行了详细讨论,最终于 2017 年底关闭。请参阅功能请求:循环块 #13262

答:

33赞 Zeitounator 11/18/2019 #1

TL的;博士

“with_items”不是块的有效属性

错误消息说明了一切:你不能遍历一个块。

如果需要循环访问一组任务,请将它们放在单独的文件中并使用include_tasks

实施(以及一些好的做法......

下面是基于示例的实现,说明了该解决方案。

由于您的问题和代码缺乏一定的精确性,并且由于我指出了一些不良做法,请注意:

  • 我修复了循环代码以有效地使用 u 循环(我推断它应该是文件)。请注意,在包含的文件中使用 of 来消除变量名称的歧义(即 )。filenamesdeploy.dbloop_controldb_filename
  • 我通过使用 ansible 模块代替并删除了阶段,使代码尽可能地幂等。copyshelltouch
  • 我将 var 名称转换为全部小写和下划线分隔符。
  • 为了确保复制任务在所有情况下都能正常工作,我将删除的任务替换为一个确保目录存在的任务。basepath
  • 我在每个值之后添加了一个过滤器和一个过滤器,以删除可能的重复项和因昏迷分隔列表中的错误而添加的最终空格。uniquefilenames.split(',')trim
  • 我使用了关键字和过滤器(为了额外的安全性),而不是与布尔值的裸露比较。notboolFalse

这是包含的文件create_db_each.yml

---
- name: Check if file exists
  stat:
    path: "{{ basepath }}/{{ db_filename }}"
  register: currdb

- name: Create the file with "done" line if not present
  copy:
    content: "done"
    dest: "{{ basepath }}/{{ db_filename }}"
  when: not currdb.stat.exists | bool

在以下 playbook 中使用create_db.yml

---
- name: "Create my dbs"
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Make sure the base directory exists
      file:
        path: "{{ basepath }}"
        state: directory

    - name: load each db
      include_tasks: "create_db_each.yml"
      when: layer == 'APP'
      loop: "{{ filenames.split(',') | unique | map('trim') }}"
      loop_control:
        loop_var: db_filename

这给了


笔记:

  • 仅首次运行,在您这边再次运行它以见证它到处报告正常
  • 请参阅 filenames 参数值以说明如何使用 uniquetrim

$ ansible-playbook -e basepath=/tmp/my/base/path -e "filenames='a.bla, b.toto, c , z.txt,a.bla'"  -e layer=APP create_db.yml

PLAY [Create my dbs] ************************************************

TASK [Make sure the base directory exists] **************************
changed: [localhost]

TASK [load each db] *************************************************
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=a.bla)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=b.toto)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=c)
included: /home/olcla/Sources/ZZ_tests/ansitests/create_db_each.yml for localhost => (item=z.txt)

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

TASK [Check if file exists] *****************************************
ok: [localhost]

TASK [Create the file with "done" line if not present] **************
changed: [localhost]

PLAY RECAP **********************************************************
localhost: ok=13   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

$ tree /tmp/my/base/path/
/tmp/my/base/path/
├── a.bla
├── b.toto
├── c
└── z.txt

$ for f in /tmp/my/base/path/*; do cat $f; echo; done
done
done
done
done

评论

1赞 Kiwy 10/19/2023
我仍然不明白这个设计选择,有一个可以循环的块的概念是有意义的。被迫将部分代码隔离在不同的文件中,使代码库 IMO 变得混乱和混乱。感谢您的详细解释。
0赞 Andrew Savinykh 10/27/2023
@Kiwy Ansible 是建立在“足够好”的设计之上的,但它们的目标不是提供出色的工具,而只是提供足够好的工具。如果项目中可以投入的开发时间有限,这可能是合理的(这种情况通常比没有情况要多)。如果您浏览存储库中的问题,您将看到许多存在解决方法的问题已关闭。这导致代码充满了笨拙,但仍然有效。
1赞 Kiwy 10/27/2023
@AndrewSavinykh 同意足够好就好了。然而,在代码方面足够好会导致大量混乱和可维护性差。我喜欢 Ansible,但是一旦发生比串行操作更复杂的任务,是的,您可以做到,但成本太高了。Ansible 由 RedHat 和 IBM 维护和支持。它不再是一个小型的单人项目。我发现很难接受像块和循环这样的基本开发东西实际上与其他任何地方的行为都不同。