Collections have no home for default vars
The best practices for developing a collection insist on role specific variables (for good reason). This leads to many different variables across the collection with a shared value. A common example would be credentials, domain names and URLs. The Ansible Good Practices recommend creating implicit collection variables and referencing them in your roles’ defaults variables. This is really good advice that minimizes the inconvenience of having to defining multiple variables with the same value.
While this is a great solution for the roles, it adds an extra manual step onto the collection. These collection variables are only captured (hopefully) as part of the collection README. Any sort of tooling for the purpose of documentation generation (such as Docsible) or testing (such as ansible-lint) don’t have a specific place to look for these collection variables.
In my opinion this is more than just an inconvenience. I think it is a significant oversight worth addressing that would improve the quality of life for users and developers alike.
Reasons we need this:
Reduce the confusion
Right now if you look at three collections with roles you will see three different ways they present this information. Some do a great job of including this information but without a standard it may not be obvious where it is. Some may even provide the type (ie bool, str, int) and a description. Many of the collections though just skip documenting it at all.
Enable tooling to help
Maybe in this age of AI it would be possible to scrape READMEs and roles to figure out this information, but there are far easier ways to solve this problem. It has already been solved for roles with the meta/argument_specs.yml file. Having a standard specification would enable documentation and testing tools to make use of this information.
Encourage good behavior
I’m going to use a real collection as an example for this next point, but want to make sure that I do my best not to sound ungrateful for the hard work that has obviously gone into developing and maintaining this very helpful bit of code. So if one of the maintainers reads this, I’m not trying to call you out. Rather I think that standards and tooling should exist to catch potential issues early (before you have too many users to safely make dramatic changes).
Seriously - This collection has helped me a lot, THANK YOU!
dellemc-openmanage-ansible-modules
If you were going to try and use this collection’s roles you wouldn’t find any note in the collection’s main README as to what collection level variables are used (like many major collections). Instead you would have to go through each of the roles you intend to use one by one and hope that they included that information in the README/argument_specs.yml file (which this collection did well on both fronts).
The pitfall that this collection fell into though, was that they are using collection level variables, for basically everything. For example, the variable ‘hostname’ is used in every role of the collection. This variable would fail ansible-lint as it is because it isn’t role scoped, which is great. We definitely want role variables scoped to the role because we want to minimize collisions. What ansible-lint wouldn’t be able to determine easily is that this is a collection level variable without an easy way to override it.
Proposed solution(s)
At the role level
The argument_specs.yml file documentation doesn’t include parameters to specify what other sources that a variable will be set from if undefined. A good/common practice is to include variables in the defaults/main.yml that reference a collection level variable. For example:
—-
# Check if set at collection variable, otherwise ‘root’
my_role_username: “{{ my_collection_username | default(‘root’) }}”
I couldn’t find any guidance on if this is a “good” practice or not, but it would work. Other than the “description” field in the argument_specs.yml, I don’t see a good way to annotate it.
# Check if ENV set otherwise use ‘changeme’
my_role_password: “{{ lookup('env’,’MY_COLLECTION_PASSWORD’) | default(‘changeme’, true) }}”
# Check if set at collection level, otherwise ENV variable, otherwise ‘.’
my_role_path: “{{ my_collection_path | default(lookup('env’,’PATH’), true) | default(‘.’) }}”
If the argument specs file provided additional fields it would be possible for tools to correlate these shared values and use that commonality to determine if collection and/or role variables are being shared in a non-standard way.
With some extra keys the role argument specs could be used to generate the collection level variables and ENV variables. For example
# roles/my_role/meta/argument_specs.yml
---
argument_specs:
# roles/my_role/tasks/main.yml entry point
main:
short_description: Main entry point for the my_role role
description:
- This is a great role that does great things
author:
- John Doe
options:
my_role_username:
type: “str”
required: false
# Deprecate this parameter below
# default: ‘root’
# Instead of a single value, its a list where the first match wins
default_values:
# Indicate this is a collection variable for collection docs
- collection_var: my_collection_username
# If no key provided interpret literally
- ‘root’
description:
- “The username for this role”
my_role_password:
type: “str”
required: false
# Deprecate this parameter below
# default: ‘changeme’
# Instead of a single value, its a list where the first match wins
default_values:
# Indicate this is an ENV variable for collection docs
- environment: MY_COLLECTION_PASSWORD
# If no key provided interpret literally
- ‘changeme’
description:
- “The password for this role”
my_role_path:
type: “str”
required: false
# Deprecate this parameter below
# default: ‘’
# Instead of a single value, its a list where the first match wins
default_values:
# Indicate this is a collection variable for collection docs
- collection_var: my_collection_path
# Indicate this is an ENV variable for collection docs
- environment: PATH
# If no key provided interpret literally
- ‘’
description:
- “The path for this role”
At the collection level
Define a new file similar to the role’s argument_specs.yml that describes the collection level variables, their types, if they are required or not. I’m not sure a default value here would make sense as it would be a big change in logic that would create a lot of confusion. If there are ENV variables that are checked, they should be documented as well.
Wrapping up
I would love to see a day where a tool like ansible-lint catches me referencing a collection level variable that I didn’t document. Or where an automated documentation tool like Docsible can intuit all the roles that utilize a specific collection variable.
If there was a standard, it may also enable tools like Galaxy/Hub to parse that data and present in a standard way.