roles and dependencies are realy limited. If you upgrade those to use import_role, then you could work around the fact that imports themselves are not skippable by using the defaults_from option.
roles:
- common-pcengine
tasks:
- import_role:
name: dhcpd
tasks_from: "{{ conditional }}"
defaults_from: "{{ conditional }}"
vars:
# This assumes an empty.yml file exists in tasks/ and defaults/
conditional: "{{ migrated_to_kea_dhcpd | default(false) | ternary('empty', 'main') }}"
# Task keywords like 'when' ONLY apply to the tasks within the import.
# By using the *_from arguments, you can make tasks, defaults, vars, and handlers conditional.
# when: not (migrated_to_kea_dhcpd | default(false))
In the example given, common-pcengine does have access to the defaults from dhcpd when it runs. It doesnāt run ātoo lateā.
But, keep in mind the dependencies of dhcpd would run regardless, so for that to work as expected, dependencies of dhcpd would need to be replaced with import_role tasks too.
I know this isnāt a good or convenient solution for you, but itās the only way to skip imports, so I thought Iād mention it. The legacy style imports (roles and dependencies) have no way to do this.
Iām still missing one thing: What is it the playbook knows (upon which it bases the decision of whether to import or include a role) that the common role doesnāt know?
For some, the playbook just lists the dhcpd4 role. (Or not.)
For some, the playbook just lists the install_server role, which used to depend on the dhcpd role, but has been changed to depend on the dhcpd4 role instead, without having to touch the common role.
For some, they are a group of almost identically set-up machines, thereās a group playbook, and experimentation with moving to KEA has started, so they include the dhcpd role by default, except if a hostvar is set, then they include the dhcpd4 role instead.
The common role doesnāt know about dhcpd{,4}. Its job is, however, to set up the APT repos. (It can also install packages collected from roles that run later, via "{{ [] + lookup('community.general.merge_variables', 'common_basepkgs_', initial_value=[], pattern_type='prefix') + common_extra_packages }}" in the ansible.builtin.aptname argument, so that the later roles do not have to invoke APT once again, as a speedup.) The dhcpd{,4} roles require the APT repos to be set up. (For now, due to this situation, they install their respective DHCP dƦmon in their own tasks.)
Ooh, if I formulate both like that, it does indeed work.
Runs (only the relevant parts):
tg@tgb1:/tmp/mwe $ ansible-playbook testplay.yml
TASK [common : variable in common role] *************************************************************************
ok: [localhost] => {
"msg": "variable foo=False"
}
tg@tgb1:/tmp/mwe $ ansible-playbook testplay.yml -e migrated_to_kea_dhcpd=true
TASK [common : variable in common role] *************************************************************************
ok: [localhost] => {
"msg": "variable foo=True"
}
TASK [extra : hi from role extra] *******************************************************************************
ok: [localhost] => {
"msg": "hi"
}
Changed MWE for that:
tg@tgb1:/tmp/mwe $ find . -type f -print0 | sort -z | while IFS= read -rd '' x; do printf '\n⣠%s\n' "$x"; cat "$x"; done
⣠./roles/common/tasks/main.yml
---
- name: variable in common role
debug:
msg: variable foo={{ foo | default(false) }}
⣠./roles/extra/defaults/empty.yml
---
⣠./roles/extra/defaults/main.yml
---
foo: true
⣠./roles/extra/tasks/empty.yml
---
⣠./roles/extra/tasks/main.yml
---
- name: hi from role extra
debug: msg=hi
⣠./testplay.yml
- hosts: localhost
become: false
gather_facts: false
tasks:
- import_role:
name: common
- import_role:
name: extra
tasks_from: "{{ conditional }}"
defaults_from: "{{ conditional }}"
vars:
conditional: "{{ migrated_to_kea_dhcpd | default(false) | bool | ternary('main', 'empty') }}"
Yeah, Iāll keep this possible solution in mind, but Iāve already submitted the way where we just accept the repo is present on more hosts than strictly necessary last week.