ansible/test/integration/targets/collections/posix.yml
David Shrewsbury ff47d3f766
Make sure collection is a list if a str is given (#69081)
* 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
2020-04-28 10:47:11 -05:00

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"