%global _empty_manifest_terminate_build 0 Name: python-ansible-merge-vars Version: 5.0.0 Release: 1 Summary: An Ansible action plugin to explicitly merge inventory variables License: Apache Software License URL: https://github.com/leapfrogonline/ansible-merge-vars Source0: https://mirrors.nju.edu.cn/pypi/web/packages/89/80/be7f510a6bdf653948b7cc044850386852eaee736d3423c54dc71771891e/ansible_merge_vars-5.0.0.tar.gz BuildArch: noarch %description # ansible_merge_vars: An action plugin for Ansible [](https://travis-ci.org/leapfrogonline/ansible-merge-vars) [](https://pypi.org/project/ansible_merge_vars/) An Ansible plugin to merge all variables in context with a certain suffix (lists or dicts only) and create a new variable that contains the result of this merge. This is an Ansible action plugin, which is basically an Ansible module that runs on the machine running Ansible rather than on the host that Ansible is provisioning. - [Installation](#installation) - [Usage](#usage) - [Merging dicts](#merging-dicts) - [Merging lists](#merging-lists) - [Verbosity](#verbosity) - [Example Playbooks](#example-playbooks) - [Contributing](#contributing) ## Compatibility This plugin is tested with the latest release of each minor version of Ansible >= `2.1`. Earlier releases of some minor versions may not be compatible. This plugin is not compatible with combinations of older versions of Ansible and newer versions of Python. The following combinations are tested: | Python | Ansible | |---------------|---------| | 2.7 | >= 2.1 | | >= 3.5, < 3.8 | >= 2.5 | | >= 3.8 | >= 2.8 | ## Installation 1. Pick a name that you want to use to call this plugin in Ansible playbooks. This documentation assumes you're using the name `merge_vars`. 1. `pip install ansible_merge_vars` 1. Create an `action_plugins` directory in the directory in which you run Ansible. By default, Ansible will look for action plugins in an `action_plugins` folder adjacent to the running playbook. For more information on this, or to change the location where ansible looks for action plugins, see [the Ansible docs](https://docs.ansible.com/ansible/dev_guide/developing_plugins.html#distributing-plugins). 1. Create a file called `merge_vars.py` (or whatever name you picked) in the `action_plugins` directory, with one line: ``` from ansible_merge_vars import ActionModule ``` 1. For Ansible less than 2.4: 1. Create the `library` directory if it's not created yet: ``` mkdir -p library ``` 1. Create an empty `merge_vars` (or whatever name you picked) file in your `library` directory: ``` touch library/merge_vars ``` Ansible action plugins are usually paired with modules (which run on the hosts being provisioned), and Ansible will automatically run an action plugin when you call of a module of the same name in a task. Prior to Ansible 2.4, if you want to call an action plugin by its name (`merge_vars`) in our tasks, you need an empty file called `merge_vars` in the place where ansible checks for custom modules; by default, this is a `library` directory adjacent to the running playbook. ## Usage The variables that you want to merge must be suffixed with `__to_merge`. They can be defined anywhere in the inventory, or by any other means; as long as they're in the context for the running play, they'll be merged. ### Merging dicts Let's say we've got a group `someenvironment` in `group_vars` with a file `users.yml`, with these contents: ```yaml users__someenvironment_users__to_merge: user1: bob user2: henry ``` and a group `somedatacenter` in `groups_vars` with a file `users.yml`, with these contents: ```yaml users__somedatacenter_users__to_merge: user3: sally user4: jane ``` and we're running a play against hosts that are in both of those groups. Then this task: ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' ``` will set a `merged_users` var (fact) available to all subsequent tasks that looks like this (if it were to be declared in raw yaml): ```yaml merged_users: user1: bob user2: henry user3: sally user4: jane ``` Note that the variables get merged in alphabetical order of their names, with values from later dicts replacing values from earlier dicts. So this setup: ```yaml users__someenvironment_users__to_merge: user1: bob user2: jekyll ``` ```yaml users__somedatacenter_users__to_merge: user2: hyde user3: sally ``` ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' ``` would set a `merged_users` var that looks like this (if it were to be declared in raw yaml): ```yaml merged_users: user1: bob user2: jekyll user3: sally ``` With great power comes great responsibility... ### Merging lists Let's say we've got a `someenvironment` group with an `open_ports.yml` file that looks like this: ```yaml open_ports__someenvironment_open_ports__to_merge: - 1 - 2 - 3 ``` and a `somedatacenter` group with an `open_ports.yml` file that looks like this: ```yaml open_ports__somedatacenter_open_ports__to_merge: - 3 - 4 - 5 ``` Then this task: ```yaml name: Merge open ports merge_vars: suffix_to_merge: open_ports__to_merge merged_var_name: merged_ports expected_type: 'list' ``` will set a `merged_ports` fact that looks like this (because the variables are merged in alphabetical order): ```yaml merged_ports: - 3 - 4 - 5 - 1 - 2 ``` Notice that `3` only appears once in the merged result. By default, this `merge_vars` plugin will de-dupe the resulting merged value. If you don't want to de-dupe the merged value, you have to declare the `dedup` argument: ```yaml name: Merge open ports merge_vars: suffix_to_merge: open_ports__to_merge merged_var_name: merged_ports dedup: false expected_type: 'list' ``` which will set this fact: ```yaml merged_ports: - 3 - 4 - 5 - 1 - 2 - 3 ``` A note about `dedup`: * It has no effect when the merged vars are dictionaries. ### Recursive merging ### When dealing with complex data structures, you may want to do a deep (recursive) merge. Suppose you have variables that define lists of users to add and select who should have admin privileges: ```yaml users__someenvironment_users__to_merge: users: - bob - henry admins: - bob ``` and ```yaml users__somedatacenter_users__to_merge: users: - sally - jane admins: - sally ``` You can request a recursive merge with: ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' recursive_dict_merge: True ``` and get: ```yaml merged_users: users: - sally - jane - bob - henry admins: - sally - bob ``` When merging dictionaries and the same key exists in both, the recursive merge checks the type of the value: * if the entry value is a list, it merges the values as lists (merge_list) * if the entry value is a dict, it merges the values (recursively) as dicts (merge_dict) * any other values: just replace (use last) ### Module options ### | parameter | required | default | choices | comments | | --------- | -------- | ------- | ------- | -------- | | suffix_to_merge | yes | | | Suffix of variables to merge. Must end with `__to_merge`. | | merged_var_name | yes | | <identifier> | Name of the target variable. | | expected_type | yes | | dict, list | Expected type of the merged variable (one of dict or list) | | dedup | no | yes | yes / no | Whether to remove duplicates from lists (arrays) after merging. | | recursive_dict_merge | no | no | yes / no | Whether to do deep (recursive) merging of dictionaries, or just merge only at top level and replace values | ## Verbosity Running ansible-playbook with `-v` will cause this plugin to output the order in which the keys are being merged: ``` PLAY [Example of merging lists] ************************************************ TASK [Merge port vars] ********************************************************* Merging vars in this order: [ u'group1_ports__to_merge', u'group2_ports__to_merge', u'group3_ports__to_merge'] ok: [localhost] => {"ansible_facts": {"merged_ports": [22, 1111, 443, 2222, 80]}, "changed": false} TASK [debug] ******************************************************************* ok: [localhost] => { "merged_ports": [ 22, 1111, 443, 2222, 80 ] } PLAY RECAP ********************************************************************* localhost : ok=6 changed=0 unreachable=0 failed=0 ``` ## Example Playbooks There are some example playbooks in the `examples` directory that show how the various features work in the context of an actual Ansible playbook. These example playbooks are run as part of the test suite for this plugin; if you would like to run them yourself, please see the [Contributing](#contributing) section for instructions on how to run the test suite. ## Contributing Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. There is only one prerequisite to working on this project locally: 1. You have the Python versions in the [.python-version](.python-version) installed and on your path (probably with [pyenv](https://github.com/pyenv/pyenv) A development workflow may look like this: 1. Clone this repository 1. Run `make dev-deps` * This will create a virtualenv `venv` in the root of this project and install all of the dependencies needed to build a release and run tests. 1. Run `make test-all` * This will use [tox](https://tox.readthedocs.io/en/latest/) to run the tests against different combinations of python versions and ansible releases. * It will also use [a script](bin/generate_tox_config.py) to query [PyPI](https://pypi.python.org) for the latest versions of Ansible, and add them to the `tox.ini` file if they're not there. 1. Updating the `tox.ini` file and running all the tests against all of the combinations of Ansible releases and Python versions takes a lot of time. To run aginst just one combination, you can list all of the combinations available and tell tox to only run the tests for one combination: ``` $ venv/bin/tox -l py27-ansible-2.1 py27-ansible-2.2 py27-ansible-2.3 py27-ansible-2.4 py27-ansible-2.5 py27-ansible-2.6 ... py35-ansible-2.5 py35-ansible-2.6 py36-ansible-2.7 py36-ansible-2.8 ... $ venv/bin/tox -e py36-ansible-2.5 ... ``` If you have any ideas about things to add or improve, or find any bugs to fix, we're all ears! Just a few guidelines: 1. Please write or update tests (either example-based tests, property-based tests, or both) for any code that you add, change, or remove. 1. Please add an example playbook or update an existing example playbook in the `examples` folder. These example playbooks serve as the integration tests for this plugin. 1. Please make sure that `make test-all` exits zero. This runs a code linter, all of the tests, and all of the examples against all supported versions of Python and Ansible. 1. If the linting seems too annoying, it probably is! Feel free to do what you need to do in the `.pylintrc` at the root of this repository to maintain sanity. Add it to your PR, and we'll most likely take it. Happy merging! %package -n python3-ansible-merge-vars Summary: An Ansible action plugin to explicitly merge inventory variables Provides: python-ansible-merge-vars BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-pip %description -n python3-ansible-merge-vars # ansible_merge_vars: An action plugin for Ansible [](https://travis-ci.org/leapfrogonline/ansible-merge-vars) [](https://pypi.org/project/ansible_merge_vars/) An Ansible plugin to merge all variables in context with a certain suffix (lists or dicts only) and create a new variable that contains the result of this merge. This is an Ansible action plugin, which is basically an Ansible module that runs on the machine running Ansible rather than on the host that Ansible is provisioning. - [Installation](#installation) - [Usage](#usage) - [Merging dicts](#merging-dicts) - [Merging lists](#merging-lists) - [Verbosity](#verbosity) - [Example Playbooks](#example-playbooks) - [Contributing](#contributing) ## Compatibility This plugin is tested with the latest release of each minor version of Ansible >= `2.1`. Earlier releases of some minor versions may not be compatible. This plugin is not compatible with combinations of older versions of Ansible and newer versions of Python. The following combinations are tested: | Python | Ansible | |---------------|---------| | 2.7 | >= 2.1 | | >= 3.5, < 3.8 | >= 2.5 | | >= 3.8 | >= 2.8 | ## Installation 1. Pick a name that you want to use to call this plugin in Ansible playbooks. This documentation assumes you're using the name `merge_vars`. 1. `pip install ansible_merge_vars` 1. Create an `action_plugins` directory in the directory in which you run Ansible. By default, Ansible will look for action plugins in an `action_plugins` folder adjacent to the running playbook. For more information on this, or to change the location where ansible looks for action plugins, see [the Ansible docs](https://docs.ansible.com/ansible/dev_guide/developing_plugins.html#distributing-plugins). 1. Create a file called `merge_vars.py` (or whatever name you picked) in the `action_plugins` directory, with one line: ``` from ansible_merge_vars import ActionModule ``` 1. For Ansible less than 2.4: 1. Create the `library` directory if it's not created yet: ``` mkdir -p library ``` 1. Create an empty `merge_vars` (or whatever name you picked) file in your `library` directory: ``` touch library/merge_vars ``` Ansible action plugins are usually paired with modules (which run on the hosts being provisioned), and Ansible will automatically run an action plugin when you call of a module of the same name in a task. Prior to Ansible 2.4, if you want to call an action plugin by its name (`merge_vars`) in our tasks, you need an empty file called `merge_vars` in the place where ansible checks for custom modules; by default, this is a `library` directory adjacent to the running playbook. ## Usage The variables that you want to merge must be suffixed with `__to_merge`. They can be defined anywhere in the inventory, or by any other means; as long as they're in the context for the running play, they'll be merged. ### Merging dicts Let's say we've got a group `someenvironment` in `group_vars` with a file `users.yml`, with these contents: ```yaml users__someenvironment_users__to_merge: user1: bob user2: henry ``` and a group `somedatacenter` in `groups_vars` with a file `users.yml`, with these contents: ```yaml users__somedatacenter_users__to_merge: user3: sally user4: jane ``` and we're running a play against hosts that are in both of those groups. Then this task: ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' ``` will set a `merged_users` var (fact) available to all subsequent tasks that looks like this (if it were to be declared in raw yaml): ```yaml merged_users: user1: bob user2: henry user3: sally user4: jane ``` Note that the variables get merged in alphabetical order of their names, with values from later dicts replacing values from earlier dicts. So this setup: ```yaml users__someenvironment_users__to_merge: user1: bob user2: jekyll ``` ```yaml users__somedatacenter_users__to_merge: user2: hyde user3: sally ``` ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' ``` would set a `merged_users` var that looks like this (if it were to be declared in raw yaml): ```yaml merged_users: user1: bob user2: jekyll user3: sally ``` With great power comes great responsibility... ### Merging lists Let's say we've got a `someenvironment` group with an `open_ports.yml` file that looks like this: ```yaml open_ports__someenvironment_open_ports__to_merge: - 1 - 2 - 3 ``` and a `somedatacenter` group with an `open_ports.yml` file that looks like this: ```yaml open_ports__somedatacenter_open_ports__to_merge: - 3 - 4 - 5 ``` Then this task: ```yaml name: Merge open ports merge_vars: suffix_to_merge: open_ports__to_merge merged_var_name: merged_ports expected_type: 'list' ``` will set a `merged_ports` fact that looks like this (because the variables are merged in alphabetical order): ```yaml merged_ports: - 3 - 4 - 5 - 1 - 2 ``` Notice that `3` only appears once in the merged result. By default, this `merge_vars` plugin will de-dupe the resulting merged value. If you don't want to de-dupe the merged value, you have to declare the `dedup` argument: ```yaml name: Merge open ports merge_vars: suffix_to_merge: open_ports__to_merge merged_var_name: merged_ports dedup: false expected_type: 'list' ``` which will set this fact: ```yaml merged_ports: - 3 - 4 - 5 - 1 - 2 - 3 ``` A note about `dedup`: * It has no effect when the merged vars are dictionaries. ### Recursive merging ### When dealing with complex data structures, you may want to do a deep (recursive) merge. Suppose you have variables that define lists of users to add and select who should have admin privileges: ```yaml users__someenvironment_users__to_merge: users: - bob - henry admins: - bob ``` and ```yaml users__somedatacenter_users__to_merge: users: - sally - jane admins: - sally ``` You can request a recursive merge with: ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' recursive_dict_merge: True ``` and get: ```yaml merged_users: users: - sally - jane - bob - henry admins: - sally - bob ``` When merging dictionaries and the same key exists in both, the recursive merge checks the type of the value: * if the entry value is a list, it merges the values as lists (merge_list) * if the entry value is a dict, it merges the values (recursively) as dicts (merge_dict) * any other values: just replace (use last) ### Module options ### | parameter | required | default | choices | comments | | --------- | -------- | ------- | ------- | -------- | | suffix_to_merge | yes | | | Suffix of variables to merge. Must end with `__to_merge`. | | merged_var_name | yes | | <identifier> | Name of the target variable. | | expected_type | yes | | dict, list | Expected type of the merged variable (one of dict or list) | | dedup | no | yes | yes / no | Whether to remove duplicates from lists (arrays) after merging. | | recursive_dict_merge | no | no | yes / no | Whether to do deep (recursive) merging of dictionaries, or just merge only at top level and replace values | ## Verbosity Running ansible-playbook with `-v` will cause this plugin to output the order in which the keys are being merged: ``` PLAY [Example of merging lists] ************************************************ TASK [Merge port vars] ********************************************************* Merging vars in this order: [ u'group1_ports__to_merge', u'group2_ports__to_merge', u'group3_ports__to_merge'] ok: [localhost] => {"ansible_facts": {"merged_ports": [22, 1111, 443, 2222, 80]}, "changed": false} TASK [debug] ******************************************************************* ok: [localhost] => { "merged_ports": [ 22, 1111, 443, 2222, 80 ] } PLAY RECAP ********************************************************************* localhost : ok=6 changed=0 unreachable=0 failed=0 ``` ## Example Playbooks There are some example playbooks in the `examples` directory that show how the various features work in the context of an actual Ansible playbook. These example playbooks are run as part of the test suite for this plugin; if you would like to run them yourself, please see the [Contributing](#contributing) section for instructions on how to run the test suite. ## Contributing Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. There is only one prerequisite to working on this project locally: 1. You have the Python versions in the [.python-version](.python-version) installed and on your path (probably with [pyenv](https://github.com/pyenv/pyenv) A development workflow may look like this: 1. Clone this repository 1. Run `make dev-deps` * This will create a virtualenv `venv` in the root of this project and install all of the dependencies needed to build a release and run tests. 1. Run `make test-all` * This will use [tox](https://tox.readthedocs.io/en/latest/) to run the tests against different combinations of python versions and ansible releases. * It will also use [a script](bin/generate_tox_config.py) to query [PyPI](https://pypi.python.org) for the latest versions of Ansible, and add them to the `tox.ini` file if they're not there. 1. Updating the `tox.ini` file and running all the tests against all of the combinations of Ansible releases and Python versions takes a lot of time. To run aginst just one combination, you can list all of the combinations available and tell tox to only run the tests for one combination: ``` $ venv/bin/tox -l py27-ansible-2.1 py27-ansible-2.2 py27-ansible-2.3 py27-ansible-2.4 py27-ansible-2.5 py27-ansible-2.6 ... py35-ansible-2.5 py35-ansible-2.6 py36-ansible-2.7 py36-ansible-2.8 ... $ venv/bin/tox -e py36-ansible-2.5 ... ``` If you have any ideas about things to add or improve, or find any bugs to fix, we're all ears! Just a few guidelines: 1. Please write or update tests (either example-based tests, property-based tests, or both) for any code that you add, change, or remove. 1. Please add an example playbook or update an existing example playbook in the `examples` folder. These example playbooks serve as the integration tests for this plugin. 1. Please make sure that `make test-all` exits zero. This runs a code linter, all of the tests, and all of the examples against all supported versions of Python and Ansible. 1. If the linting seems too annoying, it probably is! Feel free to do what you need to do in the `.pylintrc` at the root of this repository to maintain sanity. Add it to your PR, and we'll most likely take it. Happy merging! %package help Summary: Development documents and examples for ansible-merge-vars Provides: python3-ansible-merge-vars-doc %description help # ansible_merge_vars: An action plugin for Ansible [](https://travis-ci.org/leapfrogonline/ansible-merge-vars) [](https://pypi.org/project/ansible_merge_vars/) An Ansible plugin to merge all variables in context with a certain suffix (lists or dicts only) and create a new variable that contains the result of this merge. This is an Ansible action plugin, which is basically an Ansible module that runs on the machine running Ansible rather than on the host that Ansible is provisioning. - [Installation](#installation) - [Usage](#usage) - [Merging dicts](#merging-dicts) - [Merging lists](#merging-lists) - [Verbosity](#verbosity) - [Example Playbooks](#example-playbooks) - [Contributing](#contributing) ## Compatibility This plugin is tested with the latest release of each minor version of Ansible >= `2.1`. Earlier releases of some minor versions may not be compatible. This plugin is not compatible with combinations of older versions of Ansible and newer versions of Python. The following combinations are tested: | Python | Ansible | |---------------|---------| | 2.7 | >= 2.1 | | >= 3.5, < 3.8 | >= 2.5 | | >= 3.8 | >= 2.8 | ## Installation 1. Pick a name that you want to use to call this plugin in Ansible playbooks. This documentation assumes you're using the name `merge_vars`. 1. `pip install ansible_merge_vars` 1. Create an `action_plugins` directory in the directory in which you run Ansible. By default, Ansible will look for action plugins in an `action_plugins` folder adjacent to the running playbook. For more information on this, or to change the location where ansible looks for action plugins, see [the Ansible docs](https://docs.ansible.com/ansible/dev_guide/developing_plugins.html#distributing-plugins). 1. Create a file called `merge_vars.py` (or whatever name you picked) in the `action_plugins` directory, with one line: ``` from ansible_merge_vars import ActionModule ``` 1. For Ansible less than 2.4: 1. Create the `library` directory if it's not created yet: ``` mkdir -p library ``` 1. Create an empty `merge_vars` (or whatever name you picked) file in your `library` directory: ``` touch library/merge_vars ``` Ansible action plugins are usually paired with modules (which run on the hosts being provisioned), and Ansible will automatically run an action plugin when you call of a module of the same name in a task. Prior to Ansible 2.4, if you want to call an action plugin by its name (`merge_vars`) in our tasks, you need an empty file called `merge_vars` in the place where ansible checks for custom modules; by default, this is a `library` directory adjacent to the running playbook. ## Usage The variables that you want to merge must be suffixed with `__to_merge`. They can be defined anywhere in the inventory, or by any other means; as long as they're in the context for the running play, they'll be merged. ### Merging dicts Let's say we've got a group `someenvironment` in `group_vars` with a file `users.yml`, with these contents: ```yaml users__someenvironment_users__to_merge: user1: bob user2: henry ``` and a group `somedatacenter` in `groups_vars` with a file `users.yml`, with these contents: ```yaml users__somedatacenter_users__to_merge: user3: sally user4: jane ``` and we're running a play against hosts that are in both of those groups. Then this task: ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' ``` will set a `merged_users` var (fact) available to all subsequent tasks that looks like this (if it were to be declared in raw yaml): ```yaml merged_users: user1: bob user2: henry user3: sally user4: jane ``` Note that the variables get merged in alphabetical order of their names, with values from later dicts replacing values from earlier dicts. So this setup: ```yaml users__someenvironment_users__to_merge: user1: bob user2: jekyll ``` ```yaml users__somedatacenter_users__to_merge: user2: hyde user3: sally ``` ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' ``` would set a `merged_users` var that looks like this (if it were to be declared in raw yaml): ```yaml merged_users: user1: bob user2: jekyll user3: sally ``` With great power comes great responsibility... ### Merging lists Let's say we've got a `someenvironment` group with an `open_ports.yml` file that looks like this: ```yaml open_ports__someenvironment_open_ports__to_merge: - 1 - 2 - 3 ``` and a `somedatacenter` group with an `open_ports.yml` file that looks like this: ```yaml open_ports__somedatacenter_open_ports__to_merge: - 3 - 4 - 5 ``` Then this task: ```yaml name: Merge open ports merge_vars: suffix_to_merge: open_ports__to_merge merged_var_name: merged_ports expected_type: 'list' ``` will set a `merged_ports` fact that looks like this (because the variables are merged in alphabetical order): ```yaml merged_ports: - 3 - 4 - 5 - 1 - 2 ``` Notice that `3` only appears once in the merged result. By default, this `merge_vars` plugin will de-dupe the resulting merged value. If you don't want to de-dupe the merged value, you have to declare the `dedup` argument: ```yaml name: Merge open ports merge_vars: suffix_to_merge: open_ports__to_merge merged_var_name: merged_ports dedup: false expected_type: 'list' ``` which will set this fact: ```yaml merged_ports: - 3 - 4 - 5 - 1 - 2 - 3 ``` A note about `dedup`: * It has no effect when the merged vars are dictionaries. ### Recursive merging ### When dealing with complex data structures, you may want to do a deep (recursive) merge. Suppose you have variables that define lists of users to add and select who should have admin privileges: ```yaml users__someenvironment_users__to_merge: users: - bob - henry admins: - bob ``` and ```yaml users__somedatacenter_users__to_merge: users: - sally - jane admins: - sally ``` You can request a recursive merge with: ```yaml name: Merge user vars merge_vars: suffix_to_merge: users__to_merge merged_var_name: merged_users expected_type: 'dict' recursive_dict_merge: True ``` and get: ```yaml merged_users: users: - sally - jane - bob - henry admins: - sally - bob ``` When merging dictionaries and the same key exists in both, the recursive merge checks the type of the value: * if the entry value is a list, it merges the values as lists (merge_list) * if the entry value is a dict, it merges the values (recursively) as dicts (merge_dict) * any other values: just replace (use last) ### Module options ### | parameter | required | default | choices | comments | | --------- | -------- | ------- | ------- | -------- | | suffix_to_merge | yes | | | Suffix of variables to merge. Must end with `__to_merge`. | | merged_var_name | yes | | <identifier> | Name of the target variable. | | expected_type | yes | | dict, list | Expected type of the merged variable (one of dict or list) | | dedup | no | yes | yes / no | Whether to remove duplicates from lists (arrays) after merging. | | recursive_dict_merge | no | no | yes / no | Whether to do deep (recursive) merging of dictionaries, or just merge only at top level and replace values | ## Verbosity Running ansible-playbook with `-v` will cause this plugin to output the order in which the keys are being merged: ``` PLAY [Example of merging lists] ************************************************ TASK [Merge port vars] ********************************************************* Merging vars in this order: [ u'group1_ports__to_merge', u'group2_ports__to_merge', u'group3_ports__to_merge'] ok: [localhost] => {"ansible_facts": {"merged_ports": [22, 1111, 443, 2222, 80]}, "changed": false} TASK [debug] ******************************************************************* ok: [localhost] => { "merged_ports": [ 22, 1111, 443, 2222, 80 ] } PLAY RECAP ********************************************************************* localhost : ok=6 changed=0 unreachable=0 failed=0 ``` ## Example Playbooks There are some example playbooks in the `examples` directory that show how the various features work in the context of an actual Ansible playbook. These example playbooks are run as part of the test suite for this plugin; if you would like to run them yourself, please see the [Contributing](#contributing) section for instructions on how to run the test suite. ## Contributing Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. There is only one prerequisite to working on this project locally: 1. You have the Python versions in the [.python-version](.python-version) installed and on your path (probably with [pyenv](https://github.com/pyenv/pyenv) A development workflow may look like this: 1. Clone this repository 1. Run `make dev-deps` * This will create a virtualenv `venv` in the root of this project and install all of the dependencies needed to build a release and run tests. 1. Run `make test-all` * This will use [tox](https://tox.readthedocs.io/en/latest/) to run the tests against different combinations of python versions and ansible releases. * It will also use [a script](bin/generate_tox_config.py) to query [PyPI](https://pypi.python.org) for the latest versions of Ansible, and add them to the `tox.ini` file if they're not there. 1. Updating the `tox.ini` file and running all the tests against all of the combinations of Ansible releases and Python versions takes a lot of time. To run aginst just one combination, you can list all of the combinations available and tell tox to only run the tests for one combination: ``` $ venv/bin/tox -l py27-ansible-2.1 py27-ansible-2.2 py27-ansible-2.3 py27-ansible-2.4 py27-ansible-2.5 py27-ansible-2.6 ... py35-ansible-2.5 py35-ansible-2.6 py36-ansible-2.7 py36-ansible-2.8 ... $ venv/bin/tox -e py36-ansible-2.5 ... ``` If you have any ideas about things to add or improve, or find any bugs to fix, we're all ears! Just a few guidelines: 1. Please write or update tests (either example-based tests, property-based tests, or both) for any code that you add, change, or remove. 1. Please add an example playbook or update an existing example playbook in the `examples` folder. These example playbooks serve as the integration tests for this plugin. 1. Please make sure that `make test-all` exits zero. This runs a code linter, all of the tests, and all of the examples against all supported versions of Python and Ansible. 1. If the linting seems too annoying, it probably is! Feel free to do what you need to do in the `.pylintrc` at the root of this repository to maintain sanity. Add it to your PR, and we'll most likely take it. Happy merging! %prep %autosetup -n ansible-merge-vars-5.0.0 %build %py3_build %install %py3_install install -d -m755 %{buildroot}/%{_pkgdocdir} if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi pushd %{buildroot} if [ -d usr/lib ]; then find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/lib64 ]; then find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/bin ]; then find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/sbin ]; then find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst fi touch doclist.lst if [ -d usr/share/man ]; then find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst fi popd mv %{buildroot}/filelist.lst . mv %{buildroot}/doclist.lst . %files -n python3-ansible-merge-vars -f filelist.lst %dir %{python3_sitelib}/* %files help -f doclist.lst %{_docdir}/* %changelog * Wed May 31 2023 Python_Bot <Python_Bot@openeuler.org> - 5.0.0-1 - Package Spec generated