如何编写一个可以 运行 任务失败的 ansible 处理程序?

How do I write an ansible handler that can run a task on failure?

我想使用 ansible 2.5.2 来执行一些升级任务并在失败时回滚到之前的安装。如果没有更改,我会通知所有配置更改以防止升级。我最初想在该通知的处理程序中使用 block:rescue: 语句。

然而,由于尚不支持 (https://github.com/ansible/ansible/issues/14270),我改为采用建议的解决方法,将另一个任务包含在通知块中,最终得到等同于以下内容的内容:

upgrade
|-handlers
  main.yml
  - name: upgrade task
    include: upgrade.yml
|-tasks
  main.yml
  - name: Update configuration 1
    template: src=conf.j2 dest={{ conf_dir }}/conf.conf
    notify: upgrade task
  - name: Update configuration 2
    template: src=conf2.j2 dest={{ conf_dir }}/conf2.conf
    notify: upgrade task

  upgrade.yml
  - block:
    - debug: msg="Starting upgrade"
    - name: Simulate failure of first of many upgrade tasks
      command: /bin/false
    rescue:
    - debug: msg="Upgrade failed, rolling back now"
    - name: Rollback deploy
      include: rollback.yml
    always:
    - debug: msg="Upgrade complete"

  rollback.yml
    ....

正如预期的那样失败了:

RUNNING HANDLER [upgrade : Simulate failure of first of many upgrade tasks] 
fatal: [hostname]: FAILED!

但没有 Upgrade failedUpgrade complete 被打印并且 rollback.yml 没有被执行。

可能性似乎是:

  1. 我的任务中有一个错误正在悄悄阻止救援块 运行。
  2. 包含的剧本中有一个尚未引发的 ansible 错误。
  3. 不支持我尝试执行的操作。

任何人都可以发现问题或给我一些关于以支持的方式构建此问题的指示吗?我能想到的唯一替代方法是使用 listen 对处理程序进行分组,将它们标记为已更改但在失败时未失败,并让它们通知回滚任务,但乍一看这似乎非常脆弱。

答案似乎是 2. 和 3。如果示例块移动到 main.yml,它运行完美,排除 1.

在我问题中链接的功能请求的评论中,另一个人提到在 2016 年遇到了同样的问题,所以我现在提出了一个新错误:https://github.com/ansible/ansible/issues/40130

为了回答关于我建议的解决方法的更好替代方案的问题,可以使用 register: 作为允许块移动到 tasks/main.yml 的条件:

- name: Update configuration 1
  template: src=conf.j2 dest={{ conf_dir }}/conf.conf
  register: update1

- name: Update configuration 2
  template: src=conf2.j2 dest={{ conf_dir }}/conf2.conf
  register: update2

- block:
  - debug: msg="Starting upgrade"
    when: update1.changed or update2.changed

  - name: Simulate failure of first of many upgrade tasks
    command: /bin/false
    when: update1.changed or update2.changed

  rescue:
  - debug: msg="Upgrade failed, rolling back now"

  - name: Rollback deploy
    include: rollback.yml

  always:
  - debug: msg="Upgrade complete"

这仍然很脆弱,因为您需要记住每次添加配置任务时都要更新块中的所有内容。如果您对手动刷新处理程序没问题,则以下方法消除了这种担忧并且更加简洁恕我直言:

- name: Update configuration 1
  template: src=conf.j2 dest={{ conf_dir }}/conf.conf
  notify: trigger upgrade

- name: Update configuration 2
  template: src=conf2.j2 dest={{ conf_dir }}/conf2.conf
  notify: trigger upgrade

- meta: flush_handlers

- block:
  - debug: msg="Starting upgrade"
    when: upgrade is defined and upgrade.changed
    ...

其中 trigger upgradehandlers/main.yml 中的处理程序,它进行虚拟更改,即:

- name: trigger upgrade
  shell: echo "Upgrading"
  register: upgrade

我认为这是在错误修复或处理程序中允许块之前可以做的最好的事情。