Variables from not-activated roles get set

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))

Interesting… but it would need to be roles and dependencies that have these option, as import_role tasks run too late.

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.

import_role can run BEFORE roles, not sure how that is too late

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?

There are many playbooks, roles and hosts.

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.apt name 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.

Thanks!