如何在 ansible 中扩展 dict,而不是覆盖它?

How to extend dict in ansible, not overwriting it?

假设我有一个 ansible 角色,它被导入任何地方都会将存储库克隆到指定路径,例如 "git_role"。

这是一种机制,当它包含在剧本中时,克隆存储库用于由一个角色完成的项目。

它是通过像

这样分配字典来完成的
repos:
  name_of_repo:
    url: "git@myrepo.com"
    path: "/path/on/local_system"
    branch: "branch_to_checkout"

而关于 "git_role" 的这条指令是这样处理克隆回购的:

- name: Clone repositories
  git:
    repo: "{{ item['value']['url'] }}"
    dest: "{{ item['value']['path'] }}"
    accept_hostkey: yes
  loop: "{{ repos | dict2items }}"

我希望 "repos" dict 可以由任何其他角色轻松扩展。例如,角色 "A" 定义字典 ​​"repos" 和角色 "A" 所需的回购协议,并且在我将角色 "git_role" 与角色 "A" 一起导入剧本之后。 "git_role" 将查看扩展字典 "repos" 并克隆所有描述的存储库。

例如,

角色 "B" 可以在字典 "repos" 和 "git_role" 中描述另一个仓库,在这种情况下克隆角色 "B" 所需的仓库等等。

Ansible dy 默认有:

hash_behaviour=replace

而且我不想更改它,因为开发人员不建议这样做。 reddit conversation

我调查了 "combine" 但它也覆盖了 dict...

谁能建议通过任何来源扩展一个特定命令的正确无痛方法?

连 dict 也不一定。 "git_role" 可以使用任何数据源并克隆所有指定的存储库。

(ansible 2.7.9)

可以合并来自不同角色的词典。让角色 role_A、role_B 和 role_C 具有默认变量。

$ cat roles/role_A/defaults/main.yml
repos_A:
  name_of_repo_A:
    url: "git@myrepo.com"
    path: "/path/on/local_system"
    branch: "branch_to_checkout"

$ cat roles/role_B/defaults/main.yml
repos_B:
  name_of_repo_B:
    url: "git@myrepo.com"
    path: "/path/on/local_system"
    branch: "branch_to_checkout"

$ cat roles/role_C/defaults/main.yml
repos_C:
  name_of_repo_C:
    url: "git@myrepo.com"
    path: "/path/on/local_system"
    branch: "branch_to_checkout"

如果字典的名字跟在角色的名字后面repos_<SECOND-PART-OF-ROLE-NAME>那么下面的剧本

- hosts: localhost
  roles:
    - role_A
    - role_B
    - role_C
  tasks:
    - set_fact:
        repos: "{{ repos|default({})|
                   combine(lookup('vars',
                                  'repos_' ~ item.split('_').1,
                                   default={}))
                   }}"
      loop: "{{ role_names }}"
    - debug:
        var: repos

给出组合目录

"repos": {
    "name_of_repo_A": {
        "branch": "branch_to_checkout", 
        "path": "/path/on/local_system", 
        "url": "git@myrepo.com"
    }, 
    "name_of_repo_B": {
        "branch": "branch_to_checkout", 
        "path": "/path/on/local_system", 
        "url": "git@myrepo.com"
    }, 
    "name_of_repo_C": {
        "branch": "branch_to_checkout", 
        "path": "/path/on/local_system", 
        "url": "git@myrepo.com"
    }
}

控制角色内部的回购

可以控制角色内部的repos。让我们把下面的任务放到角色中,引入变量repos_source

$ cat roles/role_A/tasks/main.yml
- set_fact:
    repos: {}
- set_fact:
    repos: "{{ repos|
               combine(lookup('vars',
                              'repos_' ~ item.split('_').1,
                               default={}))
                }}"
  loop: "{{ repos_source }}"
- debug:
    var: repos

下面的任务

- import_role:
    name: role_A
  vars:
    repos_source:
      - role_A
      - role_C

- import_role:
    name: role_B
  vars:
    repos_source:
      - role_A
      - role_B

- import_role:
    name: role_C
  vars:
    repos_source:
      - role_B
      - role_C

给予

TASK [role_A : debug] **********************************************************************************
ok: [localhost] => {
    "repos": {
        "name_of_repo_A": {
            "branch": "branch_to_checkout", 
            "path": "/path/on/local_system", 
            "url": "git@myrepo.com"
        }
        "name_of_repo_C": {
            "branch": "branch_to_checkout", 
            "path": "/path/on/local_system", 
            "url": "git@myrepo.com"
        }
    }
}

TASK [role_B : debug] **********************************************************************************
ok: [localhost] => {
    "repos": {
        "name_of_repo_A": {
            "branch": "branch_to_checkout", 
            "path": "/path/on/local_system", 
            "url": "git@myrepo.com"
        }, 
        "name_of_repo_B": {
            "branch": "branch_to_checkout", 
            "path": "/path/on/local_system", 
            "url": "git@myrepo.com"
        }
    }
}

TASK [role_C : debug] **********************************************************************************
ok: [localhost] => {
    "repos": {
        "name_of_repo_B": {
            "branch": "branch_to_checkout", 
            "path": "/path/on/local_system", 
            "url": "git@myrepo.com"
        }, 
        "name_of_repo_C": {
            "branch": "branch_to_checkout", 
            "path": "/path/on/local_system", 
            "url": "git@myrepo.com"
        }
    }
}


笔记 在 ansible 2.8 中使用 ansible_play_role_namesansible_role_names。参见 Special Variables