如何在没有 no_log 的情况下不打印 Ansible 循环错误中的项目

如何在没有 no_log 的情况下不打印 Ansible 循环错误中的项目

当我执行一个带有循环的 ansible 模块时,该循环包含模块要使用的值的字典,包括机密信息,我可以使用 隐藏它loop_control.label,但在发生错误时则不能。

- ansible.module:
    arg1: "{{ item.name }}"
    arg2: "{{ item.some_value }}"
    arg3: "{{ item.secret }}"
    arg4: "{{ item.throw_error }}"
  loop: 
    - name: "item1"
      some_value: "my value"
      secret: "p4$$w0rd"
  loop_control:
    label: "{{ item.name }}"

我可以使用 隐藏它no_log: true,但它会隐藏所有内容,包括成功发生时以及错误消息,这可能完全没问题,并且当我看不到它时,我会对实际的错误一无所知。

这类似于停止反向代理中的所有日志,或记录所有内容(包括授权标头),这两种情况在生产环境中都是远远达不到要求的。

所以,有没有办法让它在发生错误时不记录循环项,但记录其他所有内容?它可以是一个任务标志,就像no_log,也可以是一些全局配置,包含在ansible.cfg文件中。

更新(2021-04-19)

弗拉基米尔的回答实际上这是一种解决方法,但使用它有几个缺点:

  1. 您必须jmespath在运行过滤器之前安装该库json_query
  2. 它包含更多代码并且使得整体代码的可读性降低。
  3. 如果你有很多循环(我的项目有超过 80 个循环),代码可以变成真的丑陋(也是因为第 2 点)。
  1. 如果父文件中有一个循环,则应在调试循环中使用loop_controlwith 。loop_var
  2. 错误和显示错误消息的位置会有所不同。成功消息也是如此。
  3. label任务中的循环debug将与主循环任务中的循环不同(这可能会使将成功和错误情况与实际项目关联起来变得更加困难)。
  4. 当一切正常时,这 2 个任务将被不必要地打印(成功消息与实际任务分开打印,当一切成功运行时,输出会变得更糟,大多数情况都是如此)。
  5. 更改和测试现有的循环任务,以及使任何新循环遵循此格式,将是维护负担(尤其是第 2 点,这让我想到如果我真的有所进步代码,或者让情况变得更糟)。

(而且可能还有更多缺点)

因此,我能说的最好的是,这是一个解决一个问题但又产生了其他问题的变通方法。

话虽如此,我还是要求一个解决方案,并且基于 Ansible 目前的工作方式,提供的解决方法(或一些类似的解决方法)似乎是实现我所要求的唯一方法。

我会权衡利弊,看看是否要实施这个解决方法,或者是否继续按现在的方式使用它。

答案1

有一种方法可以在多个任务中实现所需的行为ansible 的错误处理。您可以register输出no_log任务并在后续仅打印非机密部分debug

- hosts: localhost
  connection: local
  tasks:
  - block:
    - shell: "/bin/false"
      loop:
      - name: "item1"
        some_value: "my value"
        secret: "p4$$w0rd"
      - name: "item2"
        some_value: "my value"
        secret: "p4$$w0rd"
      loop_control:
        label: "{{ item.name }}"
      # Capture tasks output
      register: my_task
      no_log: true

    # Run this if task above fails
    rescue:

    # Print msg, stderr or whatever needed
    - debug:
        msg: "{{ item }}"
      loop: "{{ my_task | json_query('results[*].msg') }}"

    # finally fail a play
    - fail:

如果您需要始终打印输出(不仅仅是在任务失败时),请使用always而不是rescue条件fail任务:

- hosts: localhost
  connection: local
  tasks:
  - block:
    - shell: "/bin/true"
      loop:
      - name: "item1"
        some_value: "my value"
        secret: "p4$$w0rd"
      - name: "item2"
        some_value: "my value"
        secret: "p4$$w0rd"
      loop_control:
        label: "{{ item.name }}"
      register: my_task
      no_log: true

    always:
    - debug:
        msg: "{{ item }}"
      loop: "{{ my_task | json_query('results[*].msg') }}"

    - fail:
      when: my_task['failed'] | default(false)

更新(2021-04-20)

正如 Lucas 所指出的,上面的代码有许多缺点。主要思想是输出可以注册并在之后进行过滤。代码的其他部分是主观的例子。当然还有改进的空间。例如,以下是解决问题 1、4、6(和部分 2)的代码:

- hosts: localhost
  connection: local
  tasks:
  - block:
    - shell: "/bin/true"
      register: my_task
      no_log: true
      # Register all loop-related keys so same loop settings can be reused in debug
      <<: &loop
        loop:
        - name: "item1"
          some_value: "my value"
          secret: "p4$$w0rd"
        - name: "item2"
          some_value: "my value"
          secret: "p4$$w0rd"
        loop_control:
          label: "{{ item.name }}"
          index_var: index

    always:
    - debug:
        # match result with item by index
        msg: "{{ my_task['results'][index]['msg'] | default('ok') }}"
      # reuse loop from main task
      <<: *loop

    - fail:
      when: my_task['failed'] | default(false)

目前看来,没有其他解决方法就无法实现所需的行为。Ansible建议将日志写入安全位置以防其中包含敏感数据。

相关内容