将默认值插入 Ansible 中的字典列表

Inserting default values into list of dicts in Ansible

我有一个 Web 应用程序及其配置列表。简化的数据结构看起来像这样(字典列表)

web_app_details: 
- web_sourcedir: UWT_Optimus_UI
  web_destdir: 'E:\alexsapp'
  loadUserProfile: false

- web_sourcedir: UWT_Optimus_UI
  web_destdir: 'E:\bodhi'

现在,我不希望用户每次都指定一些默认值(单个字典)

defaults_only:
  identityType: LocalSystem
  enable32BitAppOnWin64: false
  loadUserProfile: true

如果用户未在 defaults_only 词典的任何字段中提供值,则应使用默认值。否则,应丢弃默认值。

所以结果是:

web_app_details: 
- web_sourcedir: UWT_Optimus_UI
  web_destdir: 'E:\alexsapp'
  identityType: LocalSystem
  enable32BitAppOnWin64: false
  loadUserProfile: false  # Because user provided the override value

- web_sourcedir: UWT_Optimus_UI
  web_destdir: 'E:\bodhi'
  identityType: LocalSystem
  enable32BitAppOnWin64: false
  loadUserProfile: true

我尝试了一些方法,例如

  1. 调用 Powershell 脚本在 Ansible 之外执行此操作
  2. 复制默认字典的次数与 web_app_detail 中一样多,然后使用组合过滤器
  3. 使用 JMESPath 查询插入 null 值:
- name: Pad web_app_details with null values
  set_fact:   
    web_app_details_with_defaults: "{{ web_app_details | json_query(jmesquery) }}"
  vars:
    jmesquery: 
      "[*].{web_sourcedir: web_sourcedir, web_destdir: web_destdir, web_cleancopy: web_cleancopy }"

但没有任何效果。
有人可以帮忙吗?

您可以重新创建该列表,添加带有 combine 过滤器的默认值。

这将利用这样一个事实,即在两个词典中定义的 属性 将被您要合并的词典覆盖。

所以,您将完成这项 set_fact 任务:

- set_fact:
    web_app_details_with_defaults: >-
      {{
        web_app_details_with_defaults | default([]) + [
          defaults_only | combine(item)
        ]
      }}
  loop: "{{ web_app_details }}"

给定两个任务:

- set_fact:
    web_app_details_with_defaults: >-
      {{
        web_app_details_with_defaults | default([]) + [
          defaults_only | combine(item)
        ]
      }}
  loop: "{{ web_app_details }}"
  vars:
    defaults_only:
      identityType: LocalSystem
      enable32BitAppOnWin64: false
      loadUserProfile: true
      
    web_app_details:
      - web_sourcedir: UWT_Optimus_UI
        web_destdir: 'E:\alexsapp'
        loadUserProfile: false

      - web_sourcedir: UWT_Optimus_UI
        web_destdir: 'E:\bodhi'

- debug:
    var: web_app_details_with_defaults

这会产生预期的结果:

web_app_details_with_defaults:
- enable32BitAppOnWin64: false
  identityType: LocalSystem
  loadUserProfile: false
  web_destdir: E:\alexsapp
  web_sourcedir: UWT_Optimus_UI
- enable32BitAppOnWin64: false
  identityType: LocalSystem
  loadUserProfile: true
  web_destdir: E:\bodhi
  web_sourcedir: UWT_Optimus_UI

有多种方法可以解决这个问题,但我的首选解决方案是:

- hosts: localhost
  vars:
    web_app_details:
    - web_sourcedir: UWT_Optimus_UI
      web_destdir: 'E:\alexsapp'
      loadUserProfile: false
    - web_sourcedir: UWT_Optimus_UI
      web_destdir: 'E:\bodhi'

    defaults_only:
      identityType: LocalSystem
      enable32BitAppOnWin64: false
      loadUserProfile: true

    web_app_details_with_defaults: "{{ [defaults_only] | product(web_app_details) | map('combine') }}"

  tasks:
    - debug:
        msg: "{{ web_app_details_with_defaults }}"

表达式 {{ [defaults_only] | product(web_app_details) | map('combine') }} 创建列表 [defaults_only](内联 single-element 列表)和 web_app_details 的乘积,然后使用 map 组合每个字典列表合并到一个字典中。

PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "msg": [
        {
            "enable32BitAppOnWin64": false,
            "identityType": "LocalSystem",
            "loadUserProfile": false,
            "web_destdir": "E:\alexsapp",
            "web_sourcedir": "UWT_Optimus_UI"
        },
        {
            "enable32BitAppOnWin64": false,
            "identityType": "LocalSystem",
            "loadUserProfile": true,
            "web_destdir": "E:\bodhi",
            "web_sourcedir": "UWT_Optimus_UI"
        }
    ]
}

如果您使用的是没有自动展开功能的旧版 Ansible,您需要在表达式中的某些点添加 | list 以将生成器转换为列表。可能只是 {{ [defaults_only] | product(web_app_details) | map('combine') | list }},但我还没有测试过。