From 48ac1b2f153060c411aeb4c4184cbc583d66b330 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Fri, 20 Mar 2026 06:19:00 -0600 Subject: [PATCH] test: ensure role gathers the facts it uses by having test clear_facts before include_role The role gathers the facts it uses. For example, if the user uses `ANSIBLE_GATHERING=explicit`, the role uses the `setup` module with the facts and subsets it requires. This change allows us to test this. Before every role invocation, the test will use `meta: clear_facts` so that the role starts with no facts. Create a task file tests/tasks/run_role_with_clear_facts.yml to do the tasks to clear the facts and run the role. Note that this means we don't need to use `gather_facts` for the tests. Some vars defined using `ansible_facts` have been changed to be defined with `set_fact` instead. This is because of the fact that `vars` are lazily evaluated - the var might be referenced when the facts have been cleared, and will issue an error like `ansible_facts["distribution"] is undefined`. This is typically done for blocks that have a `when` condition that uses `ansible_facts` and the block has a role invocation using run_role_with_clear_facts.yml These have been rewritten to define the `when` condition using `set_fact`. This is because the `when` condition is evaluated every time a task is invoked in the block, and if the facts are cleared, this will raise an undefined variable error. Signed-off-by: Rich Megginson --- tests/tasks/run_role_with_clear_facts.yml | 37 ++++++++++++++ tests/tests_basic.yml | 60 +++++++++-------------- tests/tests_default.yml | 6 +-- tests/tests_facts.yml | 7 ++- tests/tests_user_units.yml | 20 +++----- 5 files changed, 75 insertions(+), 55 deletions(-) create mode 100644 tests/tasks/run_role_with_clear_facts.yml diff --git a/tests/tasks/run_role_with_clear_facts.yml b/tests/tasks/run_role_with_clear_facts.yml new file mode 100644 index 0000000..e1516e0 --- /dev/null +++ b/tests/tasks/run_role_with_clear_facts.yml @@ -0,0 +1,37 @@ +--- +# Task file: clear_facts, run linux-system-roles.systemd. +# Include this with include_tasks or import_tasks +# Input: +# - __sr_tasks_from: tasks_from to run - same as tasks_from in include_role +# - __sr_public: export private vars from role - same as public in include_role +# - __sr_failed_when: set to false to ignore role errors - same as failed_when in include_role +- name: Clear facts + meta: clear_facts + +# note that you can use failed_when with import_role but not with include_role +# so this simulates the __sr_failed_when false case +# Q: Why do we need a separate task to run the role normally? Why not just +# run the role in the block and rethrow the error in the rescue block? +# A: Because you cannot rethrow the error in exactly the same way as the role does. +# It might be possible to exactly reconstruct ansible_failed_result but it's not worth the effort. +- name: Run the role with __sr_failed_when false + when: + - __sr_failed_when is defined + - not __sr_failed_when + block: + - name: Run the role + include_role: + name: linux-system-roles.systemd + tasks_from: "{{ __sr_tasks_from | default('main') }}" + public: "{{ __sr_public | default(false) }}" + rescue: + - name: Ignore the failure when __sr_failed_when is false + debug: + msg: Ignoring failure when __sr_failed_when is false + +- name: Run the role normally + include_role: + name: linux-system-roles.systemd + tasks_from: "{{ __sr_tasks_from | default('main') }}" + public: "{{ __sr_public | default(false) }}" + when: __sr_failed_when | d(true) diff --git a/tests/tests_basic.yml b/tests/tests_basic.yml index f4b7b3b..ff2f303 100644 --- a/tests/tests_basic.yml +++ b/tests/tests_basic.yml @@ -2,26 +2,24 @@ --- - name: Ensure that deploying unit files work correctly hosts: all - gather_facts: false vars: systemd_unit_file_templates: - nested/dir/templates/foo.service.j2 - roles: - - linux-system-roles.systemd tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Verify that unit file was deployed stat: path: /etc/systemd/system/foo.service - name: Ensure that deploying unit file dropins work correctly hosts: all - gather_facts: false vars: systemd_dropins: - nested/dir/templates/foo.service.conf.j2 - roles: - - linux-system-roles.systemd tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Stat the dropin directory path stat: path: /etc/systemd/system/foo.service.d @@ -38,13 +36,12 @@ - name: Ensure that we can start units using the role hosts: all - gather_facts: false vars: systemd_started_units: - foo.service - roles: - - linux-system-roles.systemd tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Make sure that foo.service main state is active assert: that: @@ -59,12 +56,9 @@ - name: Ensure that we can restart units using the role hosts: all - gather_facts: false vars: systemd_restarted_units: - foo.service - roles: - - linux-system-roles.systemd pre_tasks: - name: Save MainPID of foo.service before restart # noqa command-instead-of-module @@ -72,6 +66,8 @@ register: pid_before_restart changed_when: false tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Get MainPID of foo.service # noqa command-instead-of-module command: systemctl show -p MainPID foo.service @@ -85,30 +81,26 @@ - name: Ensure that we can reload units hosts: all - gather_facts: false vars: systemd_reloaded_units: - foo.service - roles: - - linux-system-roles.systemd pre_tasks: - name: Delete reload flag file file: path: /tmp/foo-service-reloaded state: absent tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Reload operation should created flag file stat: path: /tmp/foo-service-reloaded - name: Ensure that we can enable unit files hosts: all - gather_facts: false vars: systemd_enabled_units: - foo.service - roles: - - linux-system-roles.systemd pre_tasks: - name: Save UnitFileState before calling role # noqa command-instead-of-module @@ -116,6 +108,8 @@ register: unit_file_state_before changed_when: false tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Get UnitFileState= # noqa command-instead-of-module command: systemctl show -p UnitFileState foo.service @@ -130,13 +124,12 @@ - name: Ensure that we can disable unit files hosts: all - gather_facts: false vars: systemd_disabled_units: - foo.service - roles: - - linux-system-roles.systemd tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Get UnitFileState= - 2 # noqa command-instead-of-module command: systemctl show -p UnitFileState foo.service @@ -150,13 +143,10 @@ - name: Ensure that we can mask unit files hosts: all - gather_facts: false vars: # It is not possible to mask admin units files in /etc/systemd/system. systemd_masked_units: - sshd.service - roles: - - linux-system-roles.systemd pre_tasks: - name: Save UnitFileState before calling role - 2 # noqa command-instead-of-module @@ -164,6 +154,8 @@ register: sshd_state_before changed_when: false tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Get UnitFileState= - 3 # noqa command-instead-of-module command: systemctl show -p UnitFileState sshd.service @@ -180,13 +172,12 @@ - name: Ensure that we can unmask unit files hosts: all - gather_facts: false vars: systemd_unmasked_units: - sshd.service - roles: - - linux-system-roles.systemd tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Get UnitFileState= - 4 # noqa command-instead-of-module command: systemctl show -p UnitFileState sshd.service @@ -200,20 +191,18 @@ - name: Ensure that we can stop units hosts: all - gather_facts: false vars: systemd_stopped_units: - foo.service - roles: - - linux-system-roles.systemd tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml - name: Foo.service shouldn't be in systemd_units fail: when: ansible_facts['systemd_units']['foo.service'] is defined - name: Test unmask and start hosts: all - gather_facts: false vars: # we need a # * system service provided in /usr/lib/systemd/system @@ -247,8 +236,7 @@ test_unit: "{{ __find_test_unit.stdout | trim }}" - name: Ensure test unit is running and unmasked - include_role: - name: linux-system-roles.systemd + include_tasks: tasks/run_role_with_clear_facts.yml vars: systemd_started_units: - "{{ test_unit }}" @@ -269,8 +257,7 @@ - test_unit_state.stdout is search("SubState=running") - name: Stop and mask test unit - include_role: - name: linux-system-roles.systemd + include_tasks: tasks/run_role_with_clear_facts.yml vars: systemd_stopped_units: - "{{ test_unit }}" @@ -292,8 +279,7 @@ and test_unit_state.stdout is search('SubState=dead') }}" - name: Ensure test unit is running and unmasked - 2 - include_role: - name: linux-system-roles.systemd + include_tasks: tasks/run_role_with_clear_facts.yml vars: systemd_started_units: - "{{ test_unit }}" diff --git a/tests/tests_default.yml b/tests/tests_default.yml index a77d55e..a123693 100644 --- a/tests/tests_default.yml +++ b/tests/tests_default.yml @@ -2,6 +2,6 @@ --- - name: Ensure that the role runs with no arguments hosts: all - gather_facts: false - roles: - - linux-system-roles.systemd + tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml diff --git a/tests/tests_facts.yml b/tests/tests_facts.yml index 247ecc1..60bf99b 100644 --- a/tests/tests_facts.yml +++ b/tests/tests_facts.yml @@ -2,9 +2,12 @@ --- - name: Ensure that basic features of role work correctly hosts: all - roles: - - linux-system-roles.systemd tasks: + - name: Run the role + include_tasks: tasks/run_role_with_clear_facts.yml + vars: + __sr_public: true + - name: Print units facts debug: msg: "{{ systemd_units }}" diff --git a/tests/tests_user_units.yml b/tests/tests_user_units.yml index c0ddda8..a1a9a22 100644 --- a/tests/tests_user_units.yml +++ b/tests/tests_user_units.yml @@ -2,7 +2,6 @@ --- - name: Ensure that the role works with a mix of user and system units hosts: all - gather_facts: false vars: systemd_fail_if_too_old: false # allow test to pass on el7 __bar_service_name: bar.service @@ -67,10 +66,9 @@ loop: "{{ __users }}" - name: Run role to create and start units - include_role: - name: linux-system-roles.systemd - public: true + include_tasks: tasks/run_role_with_clear_facts.yml vars: + __sr_public: true systemd_unit_files: "{{ __systemd_unit_files }}" systemd_unit_file_templates: "{{ __systemd_unit_file_templates }}" systemd_dropins: "{{ __systemd_dropins }}" @@ -136,8 +134,7 @@ scope: "{{ '--system' if item.user == 'root' else '--user' }}" - name: Run role to enable units - include_role: - name: linux-system-roles.systemd + include_tasks: tasks/run_role_with_clear_facts.yml vars: systemd_enabled_units: "{{ __systemd_enabled_units }}" @@ -156,8 +153,7 @@ scope: "{{ '--system' if item.user == 'root' else '--user' }}" - name: Run role to disable units - include_role: - name: linux-system-roles.systemd + include_tasks: tasks/run_role_with_clear_facts.yml vars: systemd_disabled_units: "{{ __systemd_disabled_units }}" @@ -176,8 +172,7 @@ scope: "{{ '--system' if item.user == 'root' else '--user' }}" - name: Run role to stop units - include_role: - name: linux-system-roles.systemd + include_tasks: tasks/run_role_with_clear_facts.yml vars: systemd_stopped_units: "{{ __systemd_stopped_units }}" @@ -187,10 +182,9 @@ loop: "{{ __all_units }}" - name: Run role to remove unit files and dropins - include_role: - name: linux-system-roles.systemd - public: true + include_tasks: tasks/run_role_with_clear_facts.yml vars: + __sr_public: true systemd_unit_files: "{{ __systemd_unit_files | map('combine', __absent) | list }}" systemd_unit_file_templates: "{{ __systemd_unit_file_templates |