- hosts: testhost tasks: # basic test of FQ module lookup and that we got the right one (user-dir hosted) - name: exec FQ module in a user-dir testns collection testns.testcoll.testmodule: register: testmodule_out # verifies that distributed collection subpackages are visible under a multi-location namespace (testns exists in user and sys locations) - name: exec FQ module in a sys-dir testns collection testns.coll_in_sys.systestmodule: register: systestmodule_out # verifies that content-adjacent collections were automatically added to the installed content roots - name: exec FQ module from content-adjacent collection testns.content_adj.contentadjmodule: register: contentadjmodule_out # content should only be loaded from the first visible instance of a collection - name: attempt to look up FQ module in a masked collection testns.testcoll.plugin_lookup: type: module name: testns.testcoll.maskedmodule register: maskedmodule_out # ensure the ansible ns can have real collections added to it - name: call an external module in the ansible namespace ansible.bullcoll.bullmodule: register: bullmodule_out # ensure the ansible ns cannot override ansible.builtin externally - name: call an external module in the ansible.builtin collection (should use the built in module) ansible.builtin.ping: register: builtin_ping_out # action in a collection subdir - name: test subdir action FQ testns.testcoll.action_subdir.subdir_ping_action: register: subdir_ping_action_out # module in a collection subdir - name: test subdir module FQ testns.testcoll.module_subdir.subdir_ping_module: register: subdir_ping_module_out # module with a granular module_utils import (from (this collection).module_utils.leaf import thingtocall) - name: exec module with granular module utils import from this collection testns.testcoll.uses_leaf_mu_granular_import: register: granular_out # module with a granular nested module_utils import (from (this collection).module_utils.base import thingtocall, # where base imports secondary from the same collection's module_utils) - name: exec module with nested module utils from this collection testns.testcoll.uses_base_mu_granular_nested_import: register: granular_nested_out # module with a flat module_utils import (import (this collection).module_utils.leaf) - name: exec module with flat module_utils import from this collection testns.testcoll.uses_leaf_mu_flat_import: register: flat_out # module with a full-module module_utils import using 'from' (from (this collection).module_utils import leaf) - name: exec module with full-module module_utils import using 'from' from this collection testns.testcoll.uses_leaf_mu_module_import_from: register: from_out # module with multiple levels of the same nested package name and imported as a function - name: exec module with multiple levels of the same nested package name imported as a function testns.testcoll.uses_nested_same_as_func: register: from_nested_func # module with multiple levels of the same nested package name and imported as a module - name: exec module with multiple levels of the same nested package name imported as a module testns.testcoll.uses_nested_same_as_module: register: from_nested_module # module using a bunch of collection-level redirected module_utils - name: exec module using a bunch of collection-level redirected module_utils testns.testcoll.uses_collection_redirected_mu: register: from_redirected_mu # module with bogus MU - name: exec module with bogus MU testns.testcoll.uses_mu_missing: ignore_errors: true register: from_missing_mu # module with redirected MU, redirect collection not found - name: exec module with a missing redirect target collection testns.testcoll.uses_mu_missing_redirect_collection: ignore_errors: true register: from_missing_redir_collection # module with redirected MU, redirect module not found - name: exec module with a missing redirect target module testns.testcoll.uses_mu_missing_redirect_module: ignore_errors: true register: from_missing_redir_module - assert: that: - testmodule_out.source == 'user' - systestmodule_out.source == 'sys' - contentadjmodule_out.source == 'content_adj' - not maskedmodule_out.plugin_path - bullmodule_out.source == 'user_ansible_bullcoll' - builtin_ping_out.source is not defined - builtin_ping_out.ping == 'pong' - subdir_ping_action_out is not changed - subdir_ping_module_out is not changed - granular_out.mu_result == 'thingtocall in leaf' - granular_nested_out.mu_result == 'thingtocall in base called thingtocall in secondary' - flat_out.mu_result == 'thingtocall in leaf' - from_out.mu_result == 'thingtocall in leaf' - from_out.mu2_result == 'thingtocall in secondary' - from_out.mu3_result == 'thingtocall in subpkg.submod' - from_out.mu4_result == 'thingtocall in subpkg_with_init' - from_out.mu5_result == 'thingtocall in mod_in_subpkg_with_init' - from_out.mu6_result == 'thingtocall in subpkg.submod' - from_nested_func.mu_result == 'hello from nested_same' - from_nested_module.mu_result == 'hello from nested_same' - from_redirected_mu.mu_result == 'hello from ansible_collections.testns.content_adj.plugins.module_utils.sub1.foomodule' - from_redirected_mu.mu_result2 == 'hello from testns.othercoll.formerly_testcoll_pkg.thing' - from_redirected_mu.mu_result3 == 'hello from formerly_testcoll_pkg.submod.thing' - from_missing_mu is failed - "'Could not find imported module support' in from_missing_mu.msg" - from_missing_redir_collection is failed - "'unable to locate collection bogusns.boguscoll' in from_missing_redir_collection.msg" - from_missing_redir_module is failed - "'Could not find imported module support code for ansible_collections.testns.testcoll.plugins.modules.uses_mu_missing_redirect_module' in from_missing_redir_module.msg" - hosts: testhost tasks: - name: exercise filters/tests/lookups assert: that: - "'data' | testns.testcoll.testfilter == 'data_via_testfilter_from_userdir'" - "'data' | testns.testcoll.testfilter2 == 'data_via_testfilter2_from_userdir'" - "'data' | testns.testcoll.filter_subdir.test_subdir_filter == 'data_via_testfilter_from_subdir'" - "'from_user' is testns.testcoll.testtest" - "'from_user2' is testns.testcoll.testtest2" - "'subdir_from_user' is testns.testcoll.test_subdir.subdir_test" - lookup('testns.testcoll.mylookup') == 'mylookup_from_user_dir' - lookup('testns.testcoll.mylookup2') == 'mylookup2_from_user_dir' - lookup('testns.testcoll.lookup_subdir.my_subdir_lookup') == 'subdir_lookup_from_user_dir' - debug: msg: "{{ 'foo'|testns.testbroken.broken }}" register: result ignore_errors: true - assert: that: - | 'This is a broken filter plugin.' in result.msg - debug: msg: "{{ 'foo'|missing.collection.filter }}" register: result ignore_errors: true - assert: that: - result is failed # ensure that the synthetic ansible.builtin collection limits to builtin plugins, that ansible.legacy loads overrides # from legacy plugin dirs, and that a same-named plugin loaded from a real collection is not masked by the others - hosts: testhost tasks: - name: test unqualified ping from library dir ping: register: unqualified_ping_out - name: test legacy-qualified ping from library dir ansible.legacy.ping: register: legacy_ping_out - name: test builtin ping ansible.builtin.ping: register: builtin_ping_out - name: test collection-based ping testns.testcoll.ping: register: collection_ping_out - assert: that: - unqualified_ping_out.source == 'legacy_library_dir' - legacy_ping_out.source == 'legacy_library_dir' - builtin_ping_out.ping == 'pong' - collection_ping_out.source == 'user' # verify the default value for the collections list is empty - hosts: testhost tasks: - name: sample default collections value testns.testcoll.plugin_lookup: register: coll_default_out - assert: that: # in original release, collections defaults to empty, which is mostly equivalent to ansible.legacy - not coll_default_out.collection_list # ensure that inheritance/masking works as expected, that the proper default values are injected when missing, # and that the order is preserved if one of the magic values is explicitly specified - name: verify collections keyword play/block/task inheritance and magic values hosts: testhost collections: - bogus.fromplay tasks: - name: sample play collections value testns.testcoll.plugin_lookup: register: coll_play_out - name: collections override block-level collections: - bogus.fromblock block: - name: sample block collections value testns.testcoll.plugin_lookup: register: coll_block_out - name: sample task collections value collections: - bogus.fromtask testns.testcoll.plugin_lookup: register: coll_task_out - name: sample task with explicit core collections: - ansible.builtin - bogus.fromtaskexplicitcore testns.testcoll.plugin_lookup: register: coll_task_core - name: sample task with explicit legacy collections: - ansible.legacy - bogus.fromtaskexplicitlegacy testns.testcoll.plugin_lookup: register: coll_task_legacy - assert: that: # ensure that parent value inheritance is masked properly by explicit setting - coll_play_out.collection_list == ['bogus.fromplay', 'ansible.legacy'] - coll_block_out.collection_list == ['bogus.fromblock', 'ansible.legacy'] - coll_task_out.collection_list == ['bogus.fromtask', 'ansible.legacy'] - coll_task_core.collection_list == ['ansible.builtin', 'bogus.fromtaskexplicitcore'] - coll_task_legacy.collection_list == ['ansible.legacy', 'bogus.fromtaskexplicitlegacy'] - name: verify unqualified plugin resolution behavior hosts: testhost collections: - testns.testcoll - testns.coll_in_sys - testns.contentadj tasks: # basic test of unqualified module lookup and that we got the right one (user-dir hosted, there's another copy of # this one in the same-named collection in sys dir that should be masked - name: exec unqualified module in a user-dir testns collection testmodule: register: testmodule_out # use another collection to verify that we're looking in all collections listed on the play - name: exec unqualified module in a sys-dir testns collection systestmodule: register: systestmodule_out - assert: that: - testmodule_out.source == 'user' - systestmodule_out.source == 'sys' # test keyword-static execution of a FQ collection-backed role with "tasks/main.yaml" - name: verify collection-backed role execution (keyword static) hosts: testhost collections: # set to ansible.builtin only to ensure that roles function properly without inheriting the play's collections config - ansible.builtin vars: test_role_input: keyword static roles: - role: testns.testcoll.testrole_main_yaml tasks: - name: ensure role executed assert: that: - test_role_output.msg == test_role_input - testrole_source == 'collection' # test dynamic execution of a FQ collection-backed role - name: verify collection-backed role execution (dynamic) hosts: testhost collections: # set to ansible.builtin only to ensure that roles function properly without inheriting the play's collections config - ansible.builtin vars: test_role_input: dynamic tasks: - include_role: name: testns.testcoll.testrole - name: ensure role executed assert: that: - test_role_output.msg == test_role_input - testrole_source == 'collection' # test task-static execution of a FQ collection-backed role - name: verify collection-backed role execution (task static) hosts: testhost collections: - ansible.builtin vars: test_role_input: task static tasks: - import_role: name: testns.testcoll.testrole - name: ensure role executed assert: that: - test_role_output.msg == test_role_input - testrole_source == 'collection' # test a legacy playbook-adjacent role, ensure that play collections config is not inherited - name: verify legacy playbook-adjacent role behavior hosts: testhost collections: - bogus.bogus vars: test_role_input: legacy playbook-adjacent roles: - testrole # FIXME: this should technically work to look up a playbook-adjacent role # - ansible.legacy.testrole tasks: - name: ensure role executed assert: that: - test_role_output.msg == test_role_input - testrole_source == 'legacy roles dir' # test dynamic execution of a FQ collection-backed role - name: verify collection-backed role execution in subdir (include) hosts: testhost vars: test_role_input: dynamic (subdir) tasks: - include_role: name: testns.testcoll.role_subdir.subdir_testrole - name: ensure role executed assert: that: - test_role_output.msg == test_role_input - testrole_source == 'collection' # test collection-relative role deps (keyword static) - name: verify collection-relative role deps hosts: testhost vars: outer_role_input: keyword static outer test_role_input: keyword static inner roles: - testns.testcoll.calls_intra_collection_dep_role_unqualified tasks: - assert: that: - outer_role_output.msg == outer_role_input - test_role_output.msg == test_role_input - testrole_source == 'collection' # test collection-relative role deps (task static) - name: verify collection-relative role deps hosts: testhost vars: outer_role_input: task static outer test_role_input: task static inner tasks: - import_role: name: testns.testcoll.calls_intra_collection_dep_role_unqualified - assert: that: - outer_role_output.msg == outer_role_input - test_role_output.msg == test_role_input - testrole_source == 'collection' # test collection-relative role deps (task dynamic) - name: verify collection-relative role deps hosts: testhost vars: outer_role_input: task dynamic outer test_role_input: task dynamic inner tasks: - include_role: name: testns.testcoll.calls_intra_collection_dep_role_unqualified - assert: that: - outer_role_output.msg == outer_role_input - test_role_output.msg == test_role_input - testrole_source == 'collection' - name: validate static task include behavior hosts: testhost collections: - bogus.bogus tasks: - import_tasks: includeme.yml - name: validate dynamic task include behavior hosts: testhost collections: - bogus.bogus tasks: - include_tasks: includeme.yml - import_playbook: test_collection_meta.yml - name: Test FQCN handlers hosts: testhost vars: handler_counter: 0 roles: - testns.testcoll.test_fqcn_handlers - name: Ensure a collection role can call a standalone role hosts: testhost roles: - testns.testcoll.call_standalone # Issue https://github.com/ansible/ansible/issues/69054 - name: Test collection as string hosts: testhost collections: foo tasks: - debug: msg="Test"