Skip to content

Instantly share code, notes, and snippets.

@s-hertel
Created May 5, 2021 15:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save s-hertel/725ecc719b5301e76c571aca58d39bd3 to your computer and use it in GitHub Desktop.
Save s-hertel/725ecc719b5301e76c571aca58d39bd3 to your computer and use it in GitHub Desktop.
2.9 - 2.12 action_groups overview

In Ansible <= 2.9 the module_defaults keyword supports a hardcoded list of groups to define common arguments for actions. The actions in each group are also hardcoded in core. For example, the group aws contains the modules ec2 and aws_s3, and can be used like this:

module_defaults:
  group/aws:
    aws_access_key_id: "{{ access_key }}"
    aws_secret_access_key: "{{ secret_key }}"
tasks:
  - aws_s3: ...
  - ec2: ...

In 2.10 and 2.11, the groups are still hardcoded in core, but resolution of the actions inside the groups is handed off to collections. Modules/action plugins that were never a part of core are able to use the group defaults. Each supported group name maps to a list of fully qualified group names defined in a collection's meta/runtime.yml.

Since the group names are restricted, only a small number of collections can make use of this. The implementation is inefficient and incomplete. Every host for every task with a module_defaults group (for example, inherited from the play level) searches for any of the current task's redirected names in the actions from each delegatee group. It is not bi-directional since the contents of action_groups are not resolved.

In 2.12 all collections will be able to define groups and use them directly with the fully qualified group names. For example:

# collections/ansible_collections/ns/coll/meta/runtime.yml
action_groups:
  testgroup:
    - module
    - another.collection.action
module_defaults:
  group/ns.coll.testgroup:
    ...
tasks:
  - ns.coll.module: ...
  - another.collection.action: ...

redirects

It's more likely that a group will be split into more specific subgroups rather than moved as a whole, so a routing entry for action_groups in a collection's meta/runtime.yml would need to support redirect lists instead of 1:1 redirection like plugins. This seemed like it could become confusing.

There was also a related problem that some groups would likely want to contain all the actions of another group (for example, the AWS collections might want to support using a common group name in addition to defining their own), and this would lead to duplicating and maintaining those lists separately.

To solve both needs and keep the future possibilities open (such as action group deprecation), each action group can include a metadata dictionary entry in its list of action/module strings. The only supported key is extend_group to include the actions from another group. This can be used as a redirect by only defining the metadata dictionary for a group.

action_groups:
  formerly_group:
    - metadata:
        extend_group: ns.coll.newgroup

The metadata is validated and a warning is emitted if the schema doesn't match the expectation. There's a toggle to disable the validation for backwards compatibility in the future.

group resolution

Each group in module_defaults is loaded from the collection and the modules/action plugins in it are resolved with the plugin loader. A group in module_defaults that can't be found is an error. Any resources contained in the action group (module/action plugin names or extending groups) that can't be located are displayed in -vvvvv for debugging purposes, but aren't an error, since they will be upon attempted use.

Since the actions in the group are resolved, instead of checking all of the redirect names in the task's plugin load context, membership can be checked just using the task's resolved plugin name. This fixes the non-bi-directional issue.

# collections/ansible_collections/ns/coll/meta/main.yml
plugin_routing:
  modules:
    - module:
        redirect: another.collection.module
action_groups:
  testgroup:
    - module
module_defaults:
  group/testgroup:
    ...
tasks:
  # uses the defaults in 2.12
  - another.collection.module:

caching

To minimize the performance impact of loading groups used by module_defaults and resolving the actions in them, this is done a single time per play at playbook parse time. The first time the group is defined in a play it's loaded, resolved, and cached, and subsequent uses load it from the cache. Since this occurs before variables are available, the top level keys in module_defaults are required to be static.

There is a group-to-actions cache to determine if a new group needs to be loaded + resolved and an action-to-groups cache to use post-fork when determining if the current action is part of a group.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment