ff47d3f766
* Make sure collection is a list if a str is given * Call field validation early on collections Because we are doing work on modifying the collections value before it is actually validated, we can validate it ourselves early to make sure the user supplies either a string or list. Dicts are not valid. The new validation allows us to simplify the _ensure_default_collection() function. And since the field is now static, we no longer need to specify a default for it, which also allows us to simplify the function. Since the default is now removed, we can also remove the sanity/ignore.txt entry for collectionsearch.py. New unit tests are added (and the existing one modified) that allow us to make sure that we throw a parser error if a user specifies something other than a string or list for the collections value everywhere it can be specified. * Revert removing the collection default The default is actually used, so restore it. * Fix unit tests in test_helpers.py affected by early collection validation
415 lines
14 KiB
YAML
415 lines
14 KiB
YAML
- 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
|
|
|
|
- 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'
|
|
|
|
- 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
|
|
|
|
|
|
- name: test a collection-hosted connection plugin against a host from a collection-hosted inventory plugin
|
|
hosts: dynamic_host_a
|
|
vars:
|
|
ansible_connection: testns.testcoll.localconn
|
|
ansible_localconn_connectionvar: from_play
|
|
tasks:
|
|
- raw: echo 'hello world'
|
|
register: connection_out
|
|
|
|
- assert:
|
|
that:
|
|
- connection_out.stdout == "localconn ran echo 'hello world'"
|
|
# ensure that the connection var we overrode above made it into the running config
|
|
- connection_out.stderr == "connectionvar is from_play"
|
|
|
|
- hosts: testhost
|
|
tasks:
|
|
- assert:
|
|
that:
|
|
- hostvars['dynamic_host_a'] is defined
|
|
- hostvars['dynamic_host_a'].connection_out.stdout == "localconn ran echo 'hello world'"
|
|
|
|
- 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"
|