1- hosts: testhost 2 tasks: 3 # basic test of FQ module lookup and that we got the right one (user-dir hosted) 4 - name: exec FQ module in a user-dir testns collection 5 testns.testcoll.testmodule: 6 register: testmodule_out 7 8 # verifies that distributed collection subpackages are visible under a multi-location namespace (testns exists in user and sys locations) 9 - name: exec FQ module in a sys-dir testns collection 10 testns.coll_in_sys.systestmodule: 11 register: systestmodule_out 12 13 # verifies that content-adjacent collections were automatically added to the installed content roots 14 - name: exec FQ module from content-adjacent collection 15 testns.content_adj.contentadjmodule: 16 register: contentadjmodule_out 17 18 # content should only be loaded from the first visible instance of a collection 19 - name: attempt to look up FQ module in a masked collection 20 testns.testcoll.plugin_lookup: 21 type: module 22 name: testns.testcoll.maskedmodule 23 register: maskedmodule_out 24 25 # ensure the ansible ns can have real collections added to it 26 - name: call an external module in the ansible namespace 27 ansible.bullcoll.bullmodule: 28 register: bullmodule_out 29 30 # ensure the ansible ns cannot override ansible.builtin externally 31 - name: call an external module in the ansible.builtin collection (should use the built in module) 32 ansible.builtin.ping: 33 register: builtin_ping_out 34 35 # action in a collection subdir 36 - name: test subdir action FQ 37 testns.testcoll.action_subdir.subdir_ping_action: 38 register: subdir_ping_action_out 39 40 # module in a collection subdir 41 - name: test subdir module FQ 42 testns.testcoll.module_subdir.subdir_ping_module: 43 register: subdir_ping_module_out 44 45 # module with a granular module_utils import (from (this collection).module_utils.leaf import thingtocall) 46 - name: exec module with granular module utils import from this collection 47 testns.testcoll.uses_leaf_mu_granular_import: 48 register: granular_out 49 50 # module with a granular nested module_utils import (from (this collection).module_utils.base import thingtocall, 51 # where base imports secondary from the same collection's module_utils) 52 - name: exec module with nested module utils from this collection 53 testns.testcoll.uses_base_mu_granular_nested_import: 54 register: granular_nested_out 55 56 # module with a flat module_utils import (import (this collection).module_utils.leaf) 57 - name: exec module with flat module_utils import from this collection 58 testns.testcoll.uses_leaf_mu_flat_import: 59 register: flat_out 60 61 # module with a full-module module_utils import using 'from' (from (this collection).module_utils import leaf) 62 - name: exec module with full-module module_utils import using 'from' from this collection 63 testns.testcoll.uses_leaf_mu_module_import_from: 64 register: from_out 65 66 # module with multiple levels of the same nested package name and imported as a function 67 - name: exec module with multiple levels of the same nested package name imported as a function 68 testns.testcoll.uses_nested_same_as_func: 69 register: from_nested_func 70 71 # module with multiple levels of the same nested package name and imported as a module 72 - name: exec module with multiple levels of the same nested package name imported as a module 73 testns.testcoll.uses_nested_same_as_module: 74 register: from_nested_module 75 76 # module using a bunch of collection-level redirected module_utils 77 - name: exec module using a bunch of collection-level redirected module_utils 78 testns.testcoll.uses_collection_redirected_mu: 79 register: from_redirected_mu 80 81 # module with bogus MU 82 - name: exec module with bogus MU 83 testns.testcoll.uses_mu_missing: 84 ignore_errors: true 85 register: from_missing_mu 86 87 # module with redirected MU, redirect collection not found 88 - name: exec module with a missing redirect target collection 89 testns.testcoll.uses_mu_missing_redirect_collection: 90 ignore_errors: true 91 register: from_missing_redir_collection 92 93 # module with redirected MU, redirect module not found 94 - name: exec module with a missing redirect target module 95 testns.testcoll.uses_mu_missing_redirect_module: 96 ignore_errors: true 97 register: from_missing_redir_module 98 99 - assert: 100 that: 101 - testmodule_out.source == 'user' 102 - systestmodule_out.source == 'sys' 103 - contentadjmodule_out.source == 'content_adj' 104 - not maskedmodule_out.plugin_path 105 - bullmodule_out.source == 'user_ansible_bullcoll' 106 - builtin_ping_out.source is not defined 107 - builtin_ping_out.ping == 'pong' 108 - subdir_ping_action_out is not changed 109 - subdir_ping_module_out is not changed 110 - granular_out.mu_result == 'thingtocall in leaf' 111 - granular_nested_out.mu_result == 'thingtocall in base called thingtocall in secondary' 112 - flat_out.mu_result == 'thingtocall in leaf' 113 - from_out.mu_result == 'thingtocall in leaf' 114 - from_out.mu2_result == 'thingtocall in secondary' 115 - from_out.mu3_result == 'thingtocall in subpkg.submod' 116 - from_out.mu4_result == 'thingtocall in subpkg_with_init' 117 - from_out.mu5_result == 'thingtocall in mod_in_subpkg_with_init' 118 - from_out.mu6_result == 'thingtocall in subpkg.submod' 119 - from_nested_func.mu_result == 'hello from nested_same' 120 - from_nested_module.mu_result == 'hello from nested_same' 121 - from_redirected_mu.mu_result == 'hello from ansible_collections.testns.content_adj.plugins.module_utils.sub1.foomodule' 122 - from_redirected_mu.mu_result2 == 'hello from testns.othercoll.formerly_testcoll_pkg.thing' 123 - from_redirected_mu.mu_result3 == 'hello from formerly_testcoll_pkg.submod.thing' 124 - from_missing_mu is failed 125 - "'Could not find imported module support' in from_missing_mu.msg" 126 - from_missing_redir_collection is failed 127 - "'unable to locate collection bogusns.boguscoll' in from_missing_redir_collection.msg" 128 - from_missing_redir_module is failed 129 - "'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" 130 131 132- hosts: testhost 133 tasks: 134 - name: exercise filters/tests/lookups 135 assert: 136 that: 137 - "'data' | testns.testcoll.testfilter == 'data_via_testfilter_from_userdir'" 138 - "'data' | testns.testcoll.testfilter2 == 'data_via_testfilter2_from_userdir'" 139 - "'data' | testns.testcoll.filter_subdir.test_subdir_filter == 'data_via_testfilter_from_subdir'" 140 - "'from_user' is testns.testcoll.testtest" 141 - "'from_user2' is testns.testcoll.testtest2" 142 - "'subdir_from_user' is testns.testcoll.test_subdir.subdir_test" 143 - lookup('testns.testcoll.mylookup') == 'mylookup_from_user_dir' 144 - lookup('testns.testcoll.mylookup2') == 'mylookup2_from_user_dir' 145 - lookup('testns.testcoll.lookup_subdir.my_subdir_lookup') == 'subdir_lookup_from_user_dir' 146 147 - debug: 148 msg: "{{ 'foo'|testns.testbroken.broken }}" 149 register: result 150 ignore_errors: true 151 152 - assert: 153 that: 154 - | 155 'This is a broken filter plugin.' in result.msg 156 157 - debug: 158 msg: "{{ 'foo'|missing.collection.filter }}" 159 register: result 160 ignore_errors: true 161 162 - assert: 163 that: 164 - result is failed 165 166# ensure that the synthetic ansible.builtin collection limits to builtin plugins, that ansible.legacy loads overrides 167# from legacy plugin dirs, and that a same-named plugin loaded from a real collection is not masked by the others 168- hosts: testhost 169 tasks: 170 - name: test unqualified ping from library dir 171 ping: 172 register: unqualified_ping_out 173 174 - name: test legacy-qualified ping from library dir 175 ansible.legacy.ping: 176 register: legacy_ping_out 177 178 - name: test builtin ping 179 ansible.builtin.ping: 180 register: builtin_ping_out 181 182 - name: test collection-based ping 183 testns.testcoll.ping: 184 register: collection_ping_out 185 186 - assert: 187 that: 188 - unqualified_ping_out.source == 'legacy_library_dir' 189 - legacy_ping_out.source == 'legacy_library_dir' 190 - builtin_ping_out.ping == 'pong' 191 - collection_ping_out.source == 'user' 192 193# verify the default value for the collections list is empty 194- hosts: testhost 195 tasks: 196 - name: sample default collections value 197 testns.testcoll.plugin_lookup: 198 register: coll_default_out 199 200 - assert: 201 that: 202 # in original release, collections defaults to empty, which is mostly equivalent to ansible.legacy 203 - not coll_default_out.collection_list 204 205 206# ensure that inheritance/masking works as expected, that the proper default values are injected when missing, 207# and that the order is preserved if one of the magic values is explicitly specified 208- name: verify collections keyword play/block/task inheritance and magic values 209 hosts: testhost 210 collections: 211 - bogus.fromplay 212 tasks: 213 - name: sample play collections value 214 testns.testcoll.plugin_lookup: 215 register: coll_play_out 216 217 - name: collections override block-level 218 collections: 219 - bogus.fromblock 220 block: 221 - name: sample block collections value 222 testns.testcoll.plugin_lookup: 223 register: coll_block_out 224 225 - name: sample task collections value 226 collections: 227 - bogus.fromtask 228 testns.testcoll.plugin_lookup: 229 register: coll_task_out 230 231 - name: sample task with explicit core 232 collections: 233 - ansible.builtin 234 - bogus.fromtaskexplicitcore 235 testns.testcoll.plugin_lookup: 236 register: coll_task_core 237 238 - name: sample task with explicit legacy 239 collections: 240 - ansible.legacy 241 - bogus.fromtaskexplicitlegacy 242 testns.testcoll.plugin_lookup: 243 register: coll_task_legacy 244 245 - assert: 246 that: 247 # ensure that parent value inheritance is masked properly by explicit setting 248 - coll_play_out.collection_list == ['bogus.fromplay', 'ansible.legacy'] 249 - coll_block_out.collection_list == ['bogus.fromblock', 'ansible.legacy'] 250 - coll_task_out.collection_list == ['bogus.fromtask', 'ansible.legacy'] 251 - coll_task_core.collection_list == ['ansible.builtin', 'bogus.fromtaskexplicitcore'] 252 - coll_task_legacy.collection_list == ['ansible.legacy', 'bogus.fromtaskexplicitlegacy'] 253 254- name: verify unqualified plugin resolution behavior 255 hosts: testhost 256 collections: 257 - testns.testcoll 258 - testns.coll_in_sys 259 - testns.contentadj 260 tasks: 261 # basic test of unqualified module lookup and that we got the right one (user-dir hosted, there's another copy of 262 # this one in the same-named collection in sys dir that should be masked 263 - name: exec unqualified module in a user-dir testns collection 264 testmodule: 265 register: testmodule_out 266 267 # use another collection to verify that we're looking in all collections listed on the play 268 - name: exec unqualified module in a sys-dir testns collection 269 systestmodule: 270 register: systestmodule_out 271 272 - assert: 273 that: 274 - testmodule_out.source == 'user' 275 - systestmodule_out.source == 'sys' 276 277# test keyword-static execution of a FQ collection-backed role with "tasks/main.yaml" 278- name: verify collection-backed role execution (keyword static) 279 hosts: testhost 280 collections: 281 # set to ansible.builtin only to ensure that roles function properly without inheriting the play's collections config 282 - ansible.builtin 283 vars: 284 test_role_input: keyword static 285 roles: 286 - role: testns.testcoll.testrole_main_yaml 287 tasks: 288 - name: ensure role executed 289 assert: 290 that: 291 - test_role_output.msg == test_role_input 292 - testrole_source == 'collection' 293 294 295# test dynamic execution of a FQ collection-backed role 296- name: verify collection-backed role execution (dynamic) 297 hosts: testhost 298 collections: 299 # set to ansible.builtin only to ensure that roles function properly without inheriting the play's collections config 300 - ansible.builtin 301 vars: 302 test_role_input: dynamic 303 tasks: 304 - include_role: 305 name: testns.testcoll.testrole 306 - name: ensure role executed 307 assert: 308 that: 309 - test_role_output.msg == test_role_input 310 - testrole_source == 'collection' 311 312# test task-static execution of a FQ collection-backed role 313- name: verify collection-backed role execution (task static) 314 hosts: testhost 315 collections: 316 - ansible.builtin 317 vars: 318 test_role_input: task static 319 tasks: 320 - import_role: 321 name: testns.testcoll.testrole 322 - name: ensure role executed 323 assert: 324 that: 325 - test_role_output.msg == test_role_input 326 - testrole_source == 'collection' 327 328 329# test a legacy playbook-adjacent role, ensure that play collections config is not inherited 330- name: verify legacy playbook-adjacent role behavior 331 hosts: testhost 332 collections: 333 - bogus.bogus 334 vars: 335 test_role_input: legacy playbook-adjacent 336 roles: 337 - testrole 338# FIXME: this should technically work to look up a playbook-adjacent role 339# - ansible.legacy.testrole 340 tasks: 341 - name: ensure role executed 342 assert: 343 that: 344 - test_role_output.msg == test_role_input 345 - testrole_source == 'legacy roles dir' 346 347 348# test dynamic execution of a FQ collection-backed role 349- name: verify collection-backed role execution in subdir (include) 350 hosts: testhost 351 vars: 352 test_role_input: dynamic (subdir) 353 tasks: 354 - include_role: 355 name: testns.testcoll.role_subdir.subdir_testrole 356 - name: ensure role executed 357 assert: 358 that: 359 - test_role_output.msg == test_role_input 360 - testrole_source == 'collection' 361 362 363# test collection-relative role deps (keyword static) 364- name: verify collection-relative role deps 365 hosts: testhost 366 vars: 367 outer_role_input: keyword static outer 368 test_role_input: keyword static inner 369 roles: 370 - testns.testcoll.calls_intra_collection_dep_role_unqualified 371 tasks: 372 - assert: 373 that: 374 - outer_role_output.msg == outer_role_input 375 - test_role_output.msg == test_role_input 376 - testrole_source == 'collection' 377 378# test collection-relative role deps (task static) 379- name: verify collection-relative role deps 380 hosts: testhost 381 vars: 382 outer_role_input: task static outer 383 test_role_input: task static inner 384 tasks: 385 - import_role: 386 name: testns.testcoll.calls_intra_collection_dep_role_unqualified 387 - assert: 388 that: 389 - outer_role_output.msg == outer_role_input 390 - test_role_output.msg == test_role_input 391 - testrole_source == 'collection' 392 393# test collection-relative role deps (task dynamic) 394- name: verify collection-relative role deps 395 hosts: testhost 396 vars: 397 outer_role_input: task dynamic outer 398 test_role_input: task dynamic inner 399 tasks: 400 - include_role: 401 name: testns.testcoll.calls_intra_collection_dep_role_unqualified 402 - assert: 403 that: 404 - outer_role_output.msg == outer_role_input 405 - test_role_output.msg == test_role_input 406 - testrole_source == 'collection' 407 408 409- name: validate static task include behavior 410 hosts: testhost 411 collections: 412 - bogus.bogus 413 tasks: 414 - import_tasks: includeme.yml 415 416 417- name: validate dynamic task include behavior 418 hosts: testhost 419 collections: 420 - bogus.bogus 421 tasks: 422 - include_tasks: includeme.yml 423 424 425- import_playbook: test_collection_meta.yml 426- name: Test FQCN handlers 427 hosts: testhost 428 vars: 429 handler_counter: 0 430 roles: 431 - testns.testcoll.test_fqcn_handlers 432 433- name: Ensure a collection role can call a standalone role 434 hosts: testhost 435 roles: 436 - testns.testcoll.call_standalone 437 438# Issue https://github.com/ansible/ansible/issues/69054 439- name: Test collection as string 440 hosts: testhost 441 collections: foo 442 tasks: 443 - debug: msg="Test" 444