summaryrefslogtreecommitdiffstats
path: root/test/integration/targets
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:55:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:55:42 +0000
commit62d9962ec7d01c95bf5732169320d3857a41446e (patch)
treef60d8fc63ff738e5f5afec48a84cf41480ee1315 /test/integration/targets
parentReleasing progress-linux version 2.14.13-1~progress7.99u1. (diff)
downloadansible-core-62d9962ec7d01c95bf5732169320d3857a41446e.tar.xz
ansible-core-62d9962ec7d01c95bf5732169320d3857a41446e.zip
Merging upstream version 2.16.5.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/integration/targets')
-rw-r--r--test/integration/targets/ansible-config/aliases2
-rwxr-xr-xtest/integration/targets/ansible-config/files/ini_dupes.py12
-rw-r--r--test/integration/targets/ansible-config/tasks/main.yml14
-rw-r--r--test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/MANIFEST.json2
-rw-r--r--test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py1
-rw-r--r--test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py3
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/MANIFEST.json2
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py1
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py22
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/test/yolo.yml19
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/MANIFEST.json2
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml6
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py27
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml6
-rw-r--r--test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py27
-rw-r--r--test/integration/targets/ansible-doc/randommodule-text.output26
-rw-r--r--test/integration/targets/ansible-doc/randommodule.output19
-rwxr-xr-xtest/integration/targets/ansible-doc/runme.sh134
-rw-r--r--test/integration/targets/ansible-doc/yolo-text.output47
-rw-r--r--test/integration/targets/ansible-doc/yolo.output64
-rw-r--r--test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt6
-rw-r--r--test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml1
-rw-r--r--test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py4
-rw-r--r--test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml2
-rw-r--r--test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_all.yml2
-rw-r--r--test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_recursive_scm_dependency.yml7
-rw-r--r--test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py57
-rw-r--r--test/integration/targets/ansible-galaxy-collection/library/setup_collections.py15
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/build.yml25
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/download.yml4
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/fail_fast_resolvelib.yml2
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/init.yml65
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/install.yml203
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml8
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/list.yml41
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/main.yml43
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/pinned_pre_releases_in_deptree.yml79
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/publish.yml5
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/supported_resolvelib.yml8
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml2
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/verify.yml8
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/virtual_direct_requests.yml77
-rw-r--r--test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j216
-rw-r--r--test/integration/targets/ansible-galaxy-collection/vars/main.yml43
-rwxr-xr-xtest/integration/targets/ansible-galaxy-role/files/create-role-archive.py21
-rw-r--r--test/integration/targets/ansible-galaxy-role/tasks/dir-traversal.yml86
-rw-r--r--test/integration/targets/ansible-galaxy-role/tasks/main.yml15
-rw-r--r--test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml78
-rw-r--r--test/integration/targets/ansible-galaxy/files/testserver.py19
-rwxr-xr-xtest/integration/targets/ansible-galaxy/runme.sh7
-rw-r--r--test/integration/targets/ansible-inventory/files/complex.ini35
-rw-r--r--test/integration/targets/ansible-inventory/files/valid_sample.yml2
-rw-r--r--test/integration/targets/ansible-inventory/filter_plugins/toml.py50
-rw-r--r--test/integration/targets/ansible-inventory/tasks/json_output.yml33
-rw-r--r--test/integration/targets/ansible-inventory/tasks/main.yml7
-rw-r--r--test/integration/targets/ansible-inventory/tasks/toml_output.yml43
-rw-r--r--test/integration/targets/ansible-inventory/tasks/yaml_output.yml34
-rw-r--r--test/integration/targets/ansible-playbook-callbacks/aliases4
-rw-r--r--test/integration/targets/ansible-playbook-callbacks/all-callbacks.yml123
-rw-r--r--test/integration/targets/ansible-playbook-callbacks/callbacks_list.expected24
-rw-r--r--test/integration/targets/ansible-playbook-callbacks/include_me.yml (renamed from test/integration/targets/collections/testcoll2/MANIFEST.json)0
-rwxr-xr-xtest/integration/targets/ansible-playbook-callbacks/runme.sh12
-rw-r--r--test/integration/targets/ansible-pull/pull-integration-test/conn_secret.yml12
-rw-r--r--test/integration/targets/ansible-pull/pull-integration-test/secret_connection_password1
-rwxr-xr-xtest/integration/targets/ansible-pull/runme.sh5
-rw-r--r--test/integration/targets/ansible-runner/aliases1
-rw-r--r--test/integration/targets/ansible-runner/files/adhoc_example1.py1
-rw-r--r--test/integration/targets/ansible-test-cloud-foreman/aliases3
-rw-r--r--test/integration/targets/ansible-test-cloud-foreman/tasks/main.yml6
-rw-r--r--test/integration/targets/ansible-test-cloud-openshift/aliases2
-rw-r--r--test/integration/targets/ansible-test-cloud-openshift/tasks/main.yml11
-rw-r--r--test/integration/targets/ansible-test-cloud-vcenter/aliases3
-rw-r--r--test/integration/targets/ansible-test-cloud-vcenter/tasks/main.yml6
-rwxr-xr-xtest/integration/targets/ansible-test-container/runme.py11
-rw-r--r--test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor1.py4
-rw-r--r--test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor2.py4
-rw-r--r--test/integration/targets/ansible-test-sanity-import/expected.txt2
-rwxr-xr-xtest/integration/targets/ansible-test-sanity-import/runme.sh22
-rw-r--r--test/integration/targets/ansible-test-sanity-no-get-exception/aliases4
-rw-r--r--test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/do-not-check-me.py5
-rw-r--r--test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/plugins/modules/check-me.py5
-rw-r--r--test/integration/targets/ansible-test-sanity-no-get-exception/expected.txt2
-rwxr-xr-xtest/integration/targets/ansible-test-sanity-no-get-exception/runme.sh12
-rw-r--r--test/integration/targets/ansible-test-sanity-pylint/aliases4
-rw-r--r--test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/galaxy.yml6
-rw-r--r--test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/plugins/lookup/deprecated.py22
-rw-r--r--test/integration/targets/ansible-test-sanity-pylint/expected.txt1
-rwxr-xr-xtest/integration/targets/ansible-test-sanity-pylint/runme.sh25
-rw-r--r--test/integration/targets/ansible-test-sanity-replace-urlopen/aliases4
-rw-r--r--test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/do-not-check-me.py5
-rw-r--r--test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/plugins/modules/check-me.py5
-rw-r--r--test/integration/targets/ansible-test-sanity-replace-urlopen/expected.txt1
-rwxr-xr-xtest/integration/targets/ansible-test-sanity-replace-urlopen/runme.sh12
-rw-r--r--test/integration/targets/ansible-test-sanity-use-compat-six/aliases4
-rw-r--r--test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/do-not-check-me.py5
-rw-r--r--test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/plugins/modules/check-me.py5
-rw-r--r--test/integration/targets/ansible-test-sanity-use-compat-six/expected.txt1
-rwxr-xr-xtest/integration/targets/ansible-test-sanity-use-compat-six/runme.sh12
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/meta/runtime.yml4
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/lookup/import_order_lookup.py16
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_1.py33
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_2.py34
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_3.py33
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_4.py33
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_5.py33
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_6.py34
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_7.py33
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/import_order.py24
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/semantic_markup.py127
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/sidecar.yaml3
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md1
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/meta/runtime.yml4
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md1
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/expected.txt21
-rwxr-xr-xtest/integration/targets/ansible-test-sanity-validate-modules/runme.sh12
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md1
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/meta/runtime.yml9
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/bad.py4
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/world.py2
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/modules/bad.py2
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/plugin_utils/check_pylint.py (renamed from test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/filter/check_pylint.py)7
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/random_directory/bad.py2
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py4
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/sanity/ignore.txt1
-rwxr-xr-xtest/integration/targets/ansible-test-sanity/runme.sh6
-rw-r--r--test/integration/targets/ansible-test-units-assertions/aliases4
-rw-r--r--test/integration/targets/ansible-test-units-assertions/ansible_collections/ns/col/tests/unit/plugins/modules/test_assertion.py6
-rwxr-xr-xtest/integration/targets/ansible-test-units-assertions/runme.sh22
-rw-r--r--test/integration/targets/ansible-test-units-forked/aliases5
-rw-r--r--test/integration/targets/ansible-test-units-forked/ansible_collections/ns/col/tests/unit/plugins/modules/test_ansible_forked.py43
-rwxr-xr-xtest/integration/targets/ansible-test-units-forked/runme.sh45
-rwxr-xr-xtest/integration/targets/ansible-test/venv-pythons.py10
-rw-r--r--test/integration/targets/ansible-vault/invalid_format/broken-group-vars-tasks.yml2
-rwxr-xr-xtest/integration/targets/ansible-vault/runme.sh32
-rw-r--r--test/integration/targets/ansible-vault/test_vault.yml2
-rw-r--r--test/integration/targets/ansible-vault/test_vaulted_template.yml2
-rw-r--r--test/integration/targets/ansible/aliases1
-rw-r--r--test/integration/targets/ansible/ansible-testé.cfg2
-rwxr-xr-xtest/integration/targets/ansible/runme.sh6
-rw-r--r--test/integration/targets/apt/aliases1
-rw-r--r--test/integration/targets/apt/tasks/apt.yml51
-rw-r--r--test/integration/targets/apt/tasks/repo.yml2
-rw-r--r--test/integration/targets/apt/tasks/upgrade_scenarios.yml25
-rw-r--r--test/integration/targets/apt_key/aliases1
-rw-r--r--test/integration/targets/apt_key/tasks/main.yml2
-rw-r--r--test/integration/targets/apt_repository/aliases1
-rw-r--r--test/integration/targets/apt_repository/tasks/apt.yml18
-rw-r--r--test/integration/targets/apt_repository/tasks/mode_cleanup.yaml2
-rw-r--r--test/integration/targets/argspec/library/argspec.py6
-rw-r--r--test/integration/targets/become/tasks/main.yml4
-rw-r--r--test/integration/targets/blockinfile/tasks/append_newline.yml119
-rw-r--r--test/integration/targets/blockinfile/tasks/create_dir.yml29
-rw-r--r--test/integration/targets/blockinfile/tasks/main.yml3
-rw-r--r--test/integration/targets/blockinfile/tasks/prepend_newline.yml119
-rw-r--r--test/integration/targets/blocks/unsafe_failed_task.yml2
-rw-r--r--test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout4
-rw-r--r--test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout4
-rw-r--r--test/integration/targets/check_mode/check_mode.yml2
-rw-r--r--test/integration/targets/check_mode/roles/test_check_mode/tasks/main.yml8
-rw-r--r--test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/connection/localconn.py2
-rw-r--r--test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing.py5
-rw-r--r--test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_collection.py5
-rw-r--r--test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_module.py5
-rw-r--r--test/integration/targets/collections/collections/ansible_collections/testns/content_adj/plugins/inventory/statichost.py1
-rw-r--r--test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py1
-rw-r--r--test/integration/targets/command_nonexisting/tasks/main.yml2
-rwxr-xr-xtest/integration/targets/command_shell/scripts/yoink.sh2
-rw-r--r--test/integration/targets/command_shell/tasks/main.yml42
-rw-r--r--test/integration/targets/conditionals/play.yml26
-rw-r--r--test/integration/targets/connection_delegation/aliases1
-rw-r--r--test/integration/targets/connection_paramiko_ssh/test_connection.inventory2
-rw-r--r--test/integration/targets/connection_psrp/tests.yml11
-rw-r--r--test/integration/targets/connection_winrm/tests.yml3
-rw-r--r--test/integration/targets/copy/tasks/main.yml2
-rw-r--r--test/integration/targets/copy/tasks/tests.yml152
-rw-r--r--test/integration/targets/cron/aliases1
-rw-r--r--test/integration/targets/deb822_repository/aliases6
-rw-r--r--test/integration/targets/deb822_repository/meta/main.yml4
-rw-r--r--test/integration/targets/deb822_repository/tasks/install.yml40
-rw-r--r--test/integration/targets/deb822_repository/tasks/main.yml19
-rw-r--r--test/integration/targets/deb822_repository/tasks/test.yml229
-rw-r--r--test/integration/targets/debconf/tasks/main.yml42
-rw-r--r--test/integration/targets/delegate_to/delegate_local_from_root.yml2
-rwxr-xr-xtest/integration/targets/delegate_to/runme.sh4
-rw-r--r--test/integration/targets/delegate_to/test_delegate_to.yml27
-rw-r--r--test/integration/targets/delegate_to/test_random_delegate_to_with_loop.yml26
-rw-r--r--test/integration/targets/delegate_to/test_random_delegate_to_without_loop.yml13
-rw-r--r--test/integration/targets/dnf/aliases2
-rw-r--r--test/integration/targets/dnf/tasks/dnf.yml55
-rw-r--r--test/integration/targets/dnf/tasks/main.yml6
-rw-r--r--test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml3
-rw-r--r--test/integration/targets/dnf/tasks/test_sos_removal.yml4
-rw-r--r--test/integration/targets/dnf5/aliases6
-rw-r--r--test/integration/targets/dnf5/playbook.yml19
-rwxr-xr-xtest/integration/targets/dnf5/runme.sh5
-rw-r--r--test/integration/targets/dpkg_selections/aliases1
-rw-r--r--test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml12
-rw-r--r--test/integration/targets/egg-info/lookup_plugins/import_pkg_resources.py2
-rw-r--r--test/integration/targets/environment/test_environment.yml10
-rw-r--r--test/integration/targets/error_from_connection/connection_plugins/dummy.py1
-rw-r--r--test/integration/targets/expect/tasks/main.yml9
-rw-r--r--test/integration/targets/facts_linux_network/aliases1
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml10
-rw-r--r--test/integration/targets/file/tasks/link_rewrite.yml10
-rw-r--r--test/integration/targets/file/tasks/main.yml2
-rw-r--r--test/integration/targets/filter_core/tasks/main.yml32
-rw-r--r--test/integration/targets/filter_encryption/base.yml12
-rw-r--r--test/integration/targets/filter_mathstuff/tasks/main.yml54
-rw-r--r--test/integration/targets/find/tasks/main.yml3
-rw-r--r--test/integration/targets/find/tasks/mode.yml68
-rw-r--r--test/integration/targets/fork_safe_stdio/aliases2
-rwxr-xr-xtest/integration/targets/fork_safe_stdio/runme.sh2
-rwxr-xr-xtest/integration/targets/gathering_facts/library/dummy119
-rwxr-xr-xtest/integration/targets/gathering_facts/library/dummy219
-rwxr-xr-xtest/integration/targets/gathering_facts/library/dummy319
-rw-r--r--test/integration/targets/gathering_facts/library/file_utils.py3
-rw-r--r--test/integration/targets/gathering_facts/library/slow26
-rwxr-xr-xtest/integration/targets/gathering_facts/runme.sh14
-rw-r--r--test/integration/targets/get_url/tasks/hashlib.yml20
-rw-r--r--test/integration/targets/get_url/tasks/main.yml2
-rw-r--r--test/integration/targets/get_url/tasks/use_netrc.yml6
-rw-r--r--test/integration/targets/git/tasks/depth.yml9
-rw-r--r--test/integration/targets/git/tasks/forcefully-fetch-tag.yml4
-rw-r--r--test/integration/targets/git/tasks/gpg-verification.yml10
-rw-r--r--test/integration/targets/git/tasks/localmods.yml26
-rw-r--r--test/integration/targets/git/tasks/main.yml50
-rw-r--r--test/integration/targets/git/tasks/missing_hostkey.yml3
-rw-r--r--test/integration/targets/git/tasks/missing_hostkey_acceptnew.yml3
-rw-r--r--test/integration/targets/git/tasks/reset-origin.yml9
-rw-r--r--test/integration/targets/git/tasks/setup-local-repos.yml33
-rw-r--r--test/integration/targets/git/tasks/setup.yml38
-rw-r--r--test/integration/targets/git/tasks/single-branch.yml6
-rw-r--r--test/integration/targets/git/tasks/specific-revision.yml18
-rw-r--r--test/integration/targets/git/vars/main.yml1
-rw-r--r--test/integration/targets/group/files/get_free_gid.py23
-rw-r--r--test/integration/targets/group/files/get_gid_for_group.py18
-rw-r--r--test/integration/targets/group/files/gidget.py15
-rw-r--r--test/integration/targets/group/tasks/main.yml23
-rw-r--r--test/integration/targets/group/tasks/tests.yml681
-rw-r--r--test/integration/targets/handlers/80880.yml34
-rw-r--r--test/integration/targets/handlers/82241.yml6
-rw-r--r--test/integration/targets/handlers/nested_flush_handlers_failure_force.yml19
-rw-r--r--test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/include_handlers.yml2
-rw-r--r--test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/main.yml2
-rw-r--r--test/integration/targets/handlers/roles/include_role_include_tasks_handler/tasks/main.yml2
-rw-r--r--test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml1
-rw-r--r--test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml2
-rw-r--r--test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml4
-rw-r--r--test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml2
-rw-r--r--test/integration/targets/handlers/roles/role-82241/handlers/main.yml2
-rw-r--r--test/integration/targets/handlers/roles/role-82241/tasks/entry_point.yml2
-rw-r--r--test/integration/targets/handlers/roles/role-82241/tasks/included_tasks.yml2
-rw-r--r--test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml4
-rw-r--r--test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml2
-rw-r--r--test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml3
-rw-r--r--test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml2
-rw-r--r--test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml3
-rw-r--r--test/integration/targets/handlers/roles/two_tasks_files_role/handlers/main.yml3
-rw-r--r--test/integration/targets/handlers/roles/two_tasks_files_role/tasks/main.yml3
-rw-r--r--test/integration/targets/handlers/roles/two_tasks_files_role/tasks/other.yml3
-rwxr-xr-xtest/integration/targets/handlers/runme.sh27
-rw-r--r--test/integration/targets/handlers/test_include_role_handler_once.yml20
-rw-r--r--test/integration/targets/handlers/test_include_tasks_in_include_role.yml5
-rw-r--r--test/integration/targets/handlers/test_listen_role_dedup.yml5
-rw-r--r--test/integration/targets/handlers/test_multiple_handlers_with_recursive_notification.yml36
-rw-r--r--test/integration/targets/handlers/test_run_once.yml10
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub1/sub11.yml1
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub1/sub11/config11.yml1
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub1/sub11/config112.yml1
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub1/sub12.yml1
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub2/sub21.yml1
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub2/sub21/config211.yml1
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub2/sub21/config212.yml1
-rw-r--r--test/integration/targets/include_vars/files/test_depth/sub3/config3.yml1
-rw-r--r--test/integration/targets/include_vars/tasks/main.yml52
-rw-r--r--test/integration/targets/include_vars/vars/services/service_vars.yml2
-rw-r--r--test/integration/targets/include_vars/vars/services/service_vars_fqcn.yml2
-rw-r--r--test/integration/targets/include_when_parent_is_dynamic/tasks.yml2
-rw-r--r--test/integration/targets/include_when_parent_is_static/tasks.yml2
-rw-r--r--test/integration/targets/includes/include_on_playbook_should_fail.yml2
-rw-r--r--test/integration/targets/includes/roles/test_includes/handlers/main.yml2
-rw-r--r--test/integration/targets/includes/roles/test_includes/tasks/main.yml40
-rw-r--r--test/integration/targets/includes/roles/test_includes_free/tasks/main.yml4
-rw-r--r--test/integration/targets/includes/roles/test_includes_host_pinned/tasks/main.yml2
-rwxr-xr-xtest/integration/targets/includes/runme.sh2
-rw-r--r--test/integration/targets/includes/test_includes2.yml4
-rw-r--r--test/integration/targets/includes/test_includes3.yml2
-rw-r--r--test/integration/targets/inventory/inventory_plugins/contructed_with_hostvars.py2
-rw-r--r--test/integration/targets/inventory_ini/inventory.ini2
-rwxr-xr-xtest/integration/targets/inventory_ini/runme.sh3
-rw-r--r--test/integration/targets/iptables/aliases1
-rw-r--r--test/integration/targets/iptables/tasks/chain_management.yml21
-rw-r--r--test/integration/targets/known_hosts/defaults/main.yml2
-rw-r--r--test/integration/targets/known_hosts/tasks/main.yml2
-rw-r--r--test/integration/targets/lookup-option-name/aliases2
-rw-r--r--test/integration/targets/lookup-option-name/tasks/main.yml6
-rw-r--r--test/integration/targets/lookup_config/tasks/main.yml2
-rw-r--r--test/integration/targets/lookup_fileglob/issue72873/test.yml6
-rw-r--r--test/integration/targets/lookup_first_found/tasks/main.yml53
-rw-r--r--test/integration/targets/lookup_first_found/vars/ishouldnotbefound.yml1
-rw-r--r--test/integration/targets/lookup_first_found/vars/itworks.yml1
-rw-r--r--test/integration/targets/lookup_sequence/tasks/main.yml2
-rw-r--r--test/integration/targets/lookup_together/tasks/main.yml2
-rw-r--r--test/integration/targets/lookup_url/aliases9
-rw-r--r--test/integration/targets/lookup_url/meta/main.yml2
-rw-r--r--test/integration/targets/lookup_url/tasks/main.yml32
-rw-r--r--test/integration/targets/lookup_url/tasks/use_netrc.yml8
-rw-r--r--test/integration/targets/loop-connection/collections/ansible_collections/ns/name/meta/runtime.yml2
-rw-r--r--test/integration/targets/loop-connection/main.yml2
-rw-r--r--test/integration/targets/missing_required_lib/library/missing_required_lib.py2
-rw-r--r--test/integration/targets/module_defaults/action_plugins/debug.py2
-rw-r--r--test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py1
-rw-r--r--test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py1
-rw-r--r--test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py1
-rw-r--r--test/integration/targets/module_no_log/aliases1
-rw-r--r--test/integration/targets/module_no_log/library/module_that_has_secret.py19
-rw-r--r--test/integration/targets/module_no_log/tasks/main.yml38
-rw-r--r--test/integration/targets/module_utils/library/test.py12
-rw-r--r--test/integration/targets/module_utils/library/test_failure.py4
-rw-r--r--test/integration/targets/module_utils/module_utils/bar0/foo3.py (renamed from test/integration/targets/module_utils/module_utils/bar0/foo.py)0
-rw-r--r--test/integration/targets/module_utils/module_utils/foo.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bar/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bar/bam.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bar/bar.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/yak/zebra/foo4.py (renamed from test/integration/targets/module_utils/module_utils/yak/zebra/foo.py)0
-rw-r--r--test/integration/targets/module_utils/module_utils_test.yml2
-rw-r--r--test/integration/targets/module_utils_Ansible.Basic/library/ansible_basic_tests.ps12
-rw-r--r--test/integration/targets/module_utils_Ansible.ModuleUtils.AddType/library/add_type_test.ps168
-rw-r--r--test/integration/targets/no_log/no_log_config.yml13
-rwxr-xr-xtest/integration/targets/no_log/runme.sh7
-rw-r--r--test/integration/targets/old_style_cache_plugins/aliases1
-rw-r--r--test/integration/targets/old_style_cache_plugins/plugins/cache/configurable_redis.py1
-rw-r--r--test/integration/targets/old_style_cache_plugins/setup_redis_cache.yml3
-rw-r--r--test/integration/targets/old_style_vars_plugins/deprecation_warning/v2_vars_plugin.py6
-rw-r--r--test/integration/targets/old_style_vars_plugins/deprecation_warning/vars.py2
-rw-r--r--test/integration/targets/old_style_vars_plugins/roles/a/tasks/main.yml3
-rw-r--r--test/integration/targets/old_style_vars_plugins/roles/a/vars_plugins/auto_role_vars.py11
-rwxr-xr-xtest/integration/targets/old_style_vars_plugins/runme.sh38
-rw-r--r--test/integration/targets/omit/75692.yml2
-rw-r--r--test/integration/targets/package/tasks/main.yml2
-rw-r--r--test/integration/targets/package_facts/aliases1
-rw-r--r--test/integration/targets/parsing/bad_parsing.yml12
-rw-r--r--test/integration/targets/parsing/parsing.yml35
-rw-r--r--test/integration/targets/parsing/roles/test_bad_parsing/tasks/main.yml60
-rw-r--r--test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario1.yml4
-rw-r--r--test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario2.yml4
-rw-r--r--test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario3.yml4
-rw-r--r--test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario4.yml4
-rw-r--r--test/integration/targets/parsing/roles/test_bad_parsing/vars/main.yml2
-rw-r--r--test/integration/targets/parsing/roles/test_good_parsing/tasks/main.yml19
-rw-r--r--test/integration/targets/parsing/roles/test_good_parsing/tasks/test_include_conditional.yml2
-rwxr-xr-xtest/integration/targets/parsing/runme.sh4
-rw-r--r--test/integration/targets/path_lookups/roles/showfile/tasks/notmain.yml (renamed from test/integration/targets/path_lookups/roles/showfile/tasks/main.yml)0
-rw-r--r--test/integration/targets/path_lookups/testplay.yml8
-rw-r--r--test/integration/targets/pause/pause-6.yml25
-rwxr-xr-xtest/integration/targets/pause/test-pause.py23
-rw-r--r--test/integration/targets/pip/tasks/main.yml3
-rw-r--r--test/integration/targets/pip/tasks/no_setuptools.yml48
-rw-r--r--test/integration/targets/pip/tasks/pip.yml22
-rw-r--r--test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py2
-rw-r--r--test/integration/targets/plugin_filtering/filter_lookup.yml2
-rw-r--r--test/integration/targets/plugin_filtering/filter_modules.yml2
-rw-r--r--test/integration/targets/plugin_filtering/filter_ping.yml2
-rw-r--r--test/integration/targets/plugin_filtering/filter_stat.yml2
-rw-r--r--test/integration/targets/plugin_filtering/no_blacklist_module.ini3
-rw-r--r--test/integration/targets/plugin_filtering/no_rejectlist_module.yml (renamed from test/integration/targets/plugin_filtering/no_blacklist_module.yml)2
-rwxr-xr-xtest/integration/targets/plugin_filtering/runme.sh16
-rw-r--r--test/integration/targets/plugin_loader/collections/ansible_collections/n/c/plugins/action/a.py6
-rw-r--r--test/integration/targets/plugin_loader/file_collision/play.yml7
-rw-r--r--test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py15
-rw-r--r--test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml18
-rw-r--r--test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml18
-rw-r--r--test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py14
-rw-r--r--test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml18
-rw-r--r--test/integration/targets/plugin_loader/override/filters.yml2
-rwxr-xr-xtest/integration/targets/plugin_loader/runme.sh5
-rw-r--r--test/integration/targets/plugin_loader/unsafe_plugin_name.yml9
-rw-r--r--test/integration/targets/rel_plugin_loading/subdir/inventory_plugins/notyaml.py2
-rw-r--r--test/integration/targets/remote_tmp/playbook.yml43
-rw-r--r--test/integration/targets/replace/tasks/main.yml19
-rw-r--r--test/integration/targets/result_pickle_error/action_plugins/result_pickle_error.py15
-rw-r--r--test/integration/targets/result_pickle_error/aliases3
-rwxr-xr-xtest/integration/targets/result_pickle_error/runme.sh16
-rw-r--r--test/integration/targets/result_pickle_error/runme.yml7
-rw-r--r--test/integration/targets/result_pickle_error/tasks/main.yml14
-rw-r--r--test/integration/targets/roles/47023.yml5
-rw-r--r--test/integration/targets/roles/dupe_inheritance.yml10
-rw-r--r--test/integration/targets/roles/privacy.yml60
-rw-r--r--test/integration/targets/roles/role_complete.yml47
-rw-r--r--test/integration/targets/roles/role_dep_chain.yml6
-rw-r--r--test/integration/targets/roles/roles/47023_role1/defaults/main.yml1
-rw-r--r--test/integration/targets/roles/roles/47023_role1/tasks/main.yml1
-rw-r--r--test/integration/targets/roles/roles/47023_role1/vars/main.yml1
-rw-r--r--test/integration/targets/roles/roles/47023_role2/tasks/main.yml1
-rw-r--r--test/integration/targets/roles/roles/47023_role3/tasks/main.yml1
-rw-r--r--test/integration/targets/roles/roles/47023_role4/tasks/main.yml5
-rw-r--r--test/integration/targets/roles/roles/a/vars/main.yml1
-rw-r--r--test/integration/targets/roles/roles/bottom/tasks/main.yml3
-rw-r--r--test/integration/targets/roles/roles/failed_when/tasks/main.yml4
-rw-r--r--test/integration/targets/roles/roles/imported_from_include/tasks/main.yml4
-rw-r--r--test/integration/targets/roles/roles/include_import_dep_chain/defaults/main.yml1
-rw-r--r--test/integration/targets/roles/roles/include_import_dep_chain/tasks/main.yml2
-rw-r--r--test/integration/targets/roles/roles/include_import_dep_chain/vars/main.yml1
-rw-r--r--test/integration/targets/roles/roles/middle/tasks/main.yml6
-rw-r--r--test/integration/targets/roles/roles/recover/tasks/main.yml1
-rw-r--r--test/integration/targets/roles/roles/set_var/tasks/main.yml2
-rw-r--r--test/integration/targets/roles/roles/test_connectivity/tasks/main.yml2
-rw-r--r--test/integration/targets/roles/roles/top/tasks/main.yml6
-rw-r--r--test/integration/targets/roles/roles/vars_scope/defaults/main.yml10
-rw-r--r--test/integration/targets/roles/roles/vars_scope/tasks/check_vars.yml7
-rw-r--r--test/integration/targets/roles/roles/vars_scope/tasks/main.yml1
-rw-r--r--test/integration/targets/roles/roles/vars_scope/vars/main.yml9
-rwxr-xr-xtest/integration/targets/roles/runme.sh35
-rw-r--r--test/integration/targets/roles/tasks/check_vars.yml7
-rw-r--r--test/integration/targets/roles/vars/play.yml26
-rw-r--r--test/integration/targets/roles/vars/privacy_vars.yml2
-rw-r--r--test/integration/targets/roles/vars_scope.yml358
-rw-r--r--test/integration/targets/roles_arg_spec/roles/c/meta/main.yml9
-rw-r--r--test/integration/targets/roles_arg_spec/test.yml130
-rw-r--r--test/integration/targets/rpm_key/tasks/rpm_key.yaml26
-rw-r--r--test/integration/targets/script/tasks/main.yml11
-rw-r--r--test/integration/targets/service/aliases1
-rw-r--r--test/integration/targets/service/files/ansible_test_service.py1
-rw-r--r--test/integration/targets/service_facts/aliases1
-rw-r--r--test/integration/targets/setup_deb_repo/tasks/main.yml1
-rw-r--r--test/integration/targets/setup_paramiko/install-Alpine-3-python-3.yml9
-rw-r--r--test/integration/targets/setup_paramiko/install-CentOS-6-python-2.yml3
-rw-r--r--test/integration/targets/setup_paramiko/install-Fedora-35-python-3.yml9
-rw-r--r--test/integration/targets/setup_paramiko/install-Ubuntu-16-python-2.yml3
-rw-r--r--test/integration/targets/setup_paramiko/install-python-2.yml3
-rw-r--r--test/integration/targets/setup_paramiko/uninstall-Alpine-3-python-3.yml4
-rw-r--r--test/integration/targets/setup_paramiko/uninstall-Fedora-35-python-3.yml5
-rw-r--r--test/integration/targets/setup_paramiko/uninstall-apt-python-2.yml5
-rw-r--r--test/integration/targets/setup_paramiko/uninstall-zypper-python-2.yml2
-rw-r--r--test/integration/targets/setup_rpm_repo/tasks/main.yml15
-rwxr-xr-xtest/integration/targets/strategy_linear/runme.sh2
-rw-r--r--test/integration/targets/strategy_linear/task_templated_run_once.yml20
-rw-r--r--test/integration/targets/subversion/aliases2
-rw-r--r--test/integration/targets/support-callback_plugins/aliases1
-rw-r--r--test/integration/targets/support-callback_plugins/callback_plugins/callback_debug.py (renamed from test/integration/targets/ansible/callback_plugins/callback_debug.py)0
-rw-r--r--test/integration/targets/systemd/tasks/test_indirect_service.yml2
-rw-r--r--test/integration/targets/systemd/vars/Debian.yml2
-rwxr-xr-xtest/integration/targets/tags/runme.sh9
-rw-r--r--test/integration/targets/tags/test_template_parent_tags.yml10
-rw-r--r--test/integration/targets/tasks/playbook.yml5
-rwxr-xr-xtest/integration/targets/tasks/runme.sh2
-rw-r--r--test/integration/targets/template/ansible_managed_79129.yml29
-rw-r--r--test/integration/targets/template/arg_template_overrides.j24
-rw-r--r--test/integration/targets/template/in_template_overrides.yml28
-rwxr-xr-xtest/integration/targets/template/runme.sh7
-rw-r--r--test/integration/targets/template/tasks/main.yml4
-rw-r--r--test/integration/targets/template/template_overrides.yml38
-rw-r--r--test/integration/targets/template/templates/%necho Onii-chan help Im stuck;exit 1%n.j23
-rw-r--r--test/integration/targets/template/templates/completely{{ 1 % 0 }} safe template.j23
-rw-r--r--test/integration/targets/template/unsafe.yml5
-rw-r--r--test/integration/targets/template_jinja2_non_native/macro_override.yml2
-rw-r--r--test/integration/targets/templating/tasks/main.yml11
-rw-r--r--test/integration/targets/test_core/tasks/main.yml13
-rw-r--r--test/integration/targets/test_utils/aliases1
-rwxr-xr-xtest/integration/targets/test_utils/scripts/timeout.py21
-rwxr-xr-xtest/integration/targets/unarchive/runme.sh8
-rw-r--r--test/integration/targets/unarchive/runme.yml4
-rw-r--r--test/integration/targets/unarchive/tasks/main.yml1
-rw-r--r--test/integration/targets/unarchive/tasks/test_different_language_var.yml4
-rw-r--r--test/integration/targets/unarchive/tasks/test_mode.yml23
-rw-r--r--test/integration/targets/unarchive/tasks/test_relative_dest.yml26
-rw-r--r--test/integration/targets/unarchive/test_relative_tmp_dir.yml10
-rw-r--r--test/integration/targets/unsafe_writes/aliases1
-rw-r--r--test/integration/targets/until/tasks/main.yml34
-rw-r--r--test/integration/targets/unvault/main.yml1
-rwxr-xr-xtest/integration/targets/unvault/runme.sh2
-rw-r--r--test/integration/targets/uri/tasks/main.yml37
-rw-r--r--test/integration/targets/uri/tasks/redirect-none.yml2
-rw-r--r--test/integration/targets/uri/tasks/redirect-urllib2.yml35
-rw-r--r--test/integration/targets/uri/tasks/return-content.yml2
-rw-r--r--test/integration/targets/uri/tasks/use_netrc.yml2
-rw-r--r--test/integration/targets/user/tasks/main.yml2
-rw-r--r--test/integration/targets/user/tasks/test_create_user.yml12
-rw-r--r--test/integration/targets/user/tasks/test_create_user_home.yml18
-rw-r--r--test/integration/targets/user/tasks/test_expires_no_shadow.yml47
-rw-r--r--test/integration/targets/user/tasks/test_expires_warn.yml36
-rw-r--r--test/integration/targets/user/tasks/test_local.yml40
-rw-r--r--test/integration/targets/user/vars/main.yml2
-rw-r--r--test/integration/targets/var_blending/roles/test_var_blending/tasks/main.yml10
-rwxr-xr-xtest/integration/targets/var_precedence/ansible-var-precedence-check.py5
-rw-r--r--test/integration/targets/var_precedence/test_var_precedence.yml16
-rw-r--r--test/integration/targets/vars_files/aliases2
-rw-r--r--test/integration/targets/vars_files/inventory3
-rwxr-xr-xtest/integration/targets/vars_files/runme.sh5
-rw-r--r--test/integration/targets/vars_files/runme.yml22
-rw-r--r--test/integration/targets/vars_files/validate.yml11
-rw-r--r--test/integration/targets/vars_files/vars/bar.yml1
-rw-r--r--test/integration/targets/vars_files/vars/common.yml1
-rw-r--r--test/integration/targets/vars_files/vars/defaults.yml1
-rw-r--r--test/integration/targets/wait_for/tasks/main.yml11
-rw-r--r--test/integration/targets/win_exec_wrapper/action_plugins/test_rc_1.py35
-rw-r--r--test/integration/targets/win_exec_wrapper/library/test_rc_1.ps117
-rw-r--r--test/integration/targets/win_exec_wrapper/tasks/main.yml9
-rw-r--r--test/integration/targets/win_fetch/tasks/main.yml14
-rw-r--r--test/integration/targets/win_script/files/test_script_with_args.ps12
-rw-r--r--test/integration/targets/win_script/files/test_script_with_errors.ps12
-rw-r--r--test/integration/targets/windows-minimal/library/win_ping_set_attr.ps18
-rw-r--r--test/integration/targets/windows-minimal/library/win_ping_strict_mode_error.ps18
-rw-r--r--test/integration/targets/windows-minimal/library/win_ping_syntax_error.ps18
-rw-r--r--test/integration/targets/windows-minimal/library/win_ping_throw.ps18
-rw-r--r--test/integration/targets/windows-minimal/library/win_ping_throw_string.ps18
-rw-r--r--test/integration/targets/yum/aliases1
-rw-r--r--test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py2
508 files changed, 6934 insertions, 1160 deletions
diff --git a/test/integration/targets/ansible-config/aliases b/test/integration/targets/ansible-config/aliases
new file mode 100644
index 0000000..1d28bdb
--- /dev/null
+++ b/test/integration/targets/ansible-config/aliases
@@ -0,0 +1,2 @@
+shippable/posix/group5
+context/controller
diff --git a/test/integration/targets/ansible-config/files/ini_dupes.py b/test/integration/targets/ansible-config/files/ini_dupes.py
new file mode 100755
index 0000000..ed42e6a
--- /dev/null
+++ b/test/integration/targets/ansible-config/files/ini_dupes.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import annotations
+
+import configparser
+import sys
+
+
+ini_file = sys.argv[1]
+c = configparser.ConfigParser(strict=True, inline_comment_prefixes=(';',))
+c.read_file(open(ini_file))
diff --git a/test/integration/targets/ansible-config/tasks/main.yml b/test/integration/targets/ansible-config/tasks/main.yml
new file mode 100644
index 0000000..a894dd4
--- /dev/null
+++ b/test/integration/targets/ansible-config/tasks/main.yml
@@ -0,0 +1,14 @@
+- name: test ansible-config for valid output and no dupes
+ block:
+ - name: Create temporary file
+ tempfile:
+ path: '{{output_dir}}'
+ state: file
+ suffix: temp.ini
+ register: ini_tempfile
+
+ - name: run config full dump
+ shell: ansible-config init -t all > {{ini_tempfile.path}}
+
+ - name: run ini tester, for correctness and dupes
+ shell: "{{ansible_playbook_python}} '{{role_path}}/files/ini_dupes.py' '{{ini_tempfile.path}}'"
diff --git a/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/MANIFEST.json b/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/MANIFEST.json
index 243a5e4..36f402f 100644
--- a/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/MANIFEST.json
+++ b/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/MANIFEST.json
@@ -17,7 +17,7 @@
"version": "0.1.1231",
"readme": "README.md",
"license_file": "COPYING",
- "homepage": "",
+ "homepage": ""
},
"file_manifest_file": {
"format": 1,
diff --git a/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py b/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py
index caec2ed..dfc1271 100644
--- a/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py
+++ b/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py
@@ -20,7 +20,6 @@ DOCUMENTATION = '''
required: True
'''
-from ansible.errors import AnsibleParserError
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable
diff --git a/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py b/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py
index d456986..639d3c6 100644
--- a/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py
+++ b/test/integration/targets/ansible-doc/broken-docs/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py
@@ -32,7 +32,8 @@ RETURN = """
version_added: 1.0.0
"""
-from ansible.module_utils.common._collections_compat import Sequence
+from collections.abc import Sequence
+
from ansible.plugins.lookup import LookupBase
from ansible.errors import AnsibleError
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/MANIFEST.json b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/MANIFEST.json
index 243a5e4..36f402f 100644
--- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/MANIFEST.json
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/MANIFEST.json
@@ -17,7 +17,7 @@
"version": "0.1.1231",
"readme": "README.md",
"license_file": "COPYING",
- "homepage": "",
+ "homepage": ""
},
"file_manifest_file": {
"format": 1,
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py
index cbb8f0f..1870b8e 100644
--- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/inventory/statichost.py
@@ -19,7 +19,6 @@ DOCUMENTATION = '''
required: True
'''
-from ansible.errors import AnsibleParserError
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py
index 79b7a70..aaaecb8 100644
--- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py
@@ -3,12 +3,17 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-DOCUMENTATION = '''
+DOCUMENTATION = r'''
---
module: randommodule
short_description: A random module
description:
- A random module.
+ - See O(foo.bar.baz#role:main:foo=bar) for how this is used in the P(foo.bar.baz#role)'s C(main) entrypoint.
+ - See L(the docsite,https://docs.ansible.com/ansible-core/devel/) for more information on ansible-core.
+ - This module is not related to the M(ansible.builtin.copy) module. HORIZONTALLINE You might also be interested
+ in R(ansible_python_interpreter, ansible_python_interpreter).
+ - Sometimes you have M(broken markup) that will result in error messages.
author:
- Ansible Core Team
version_added: 1.0.0
@@ -18,22 +23,22 @@ deprecated:
removed_in: '3.0.0'
options:
test:
- description: Some text.
+ description: Some text. Consider not using O(ignore:foo=bar).
type: str
version_added: 1.2.0
sub:
- description: Suboptions.
+ description: Suboptions. Contains O(sub.subtest), which can be set to V(123). You can use E(TEST_ENV) to set this.
type: dict
suboptions:
subtest:
- description: A suboption.
+ description: A suboption. Not compatible to O(ansible.builtin.copy#module:path=c:\\foo\(1\).txt).
type: int
version_added: 1.1.0
# The following is the wrong syntax, and should not get processed
# by add_collection_to_versions_and_dates()
options:
subtest2:
- description: Another suboption.
+ description: Another suboption. Useful when P(ansible.builtin.shuffle#filter) is used with value V([a,b,\),d\\]).
type: float
version_added: 1.1.0
# The following is not supported in modules, and should not get processed
@@ -65,7 +70,7 @@ seealso:
EXAMPLES = '''
'''
-RETURN = '''
+RETURN = r'''
z_last:
description: A last result.
type: str
@@ -75,7 +80,8 @@ z_last:
m_middle:
description:
- This should be in the middle.
- - Has some more data
+ - Has some more data.
+ - Check out RV(m_middle.suboption) and compare it to RV(a_first=foo) and RV(community.general.foo#lookup:value).
type: dict
returned: success and 1st of month
contains:
@@ -86,7 +92,7 @@ m_middle:
version_added: 1.4.0
a_first:
- description: A first result.
+ description: A first result. Use RV(a_first=foo\(bar\\baz\)bam).
type: str
returned: success
'''
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/test/yolo.yml b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/test/yolo.yml
index cc60945..ebfea2a 100644
--- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/test/yolo.yml
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/test/yolo.yml
@@ -8,6 +8,25 @@ DOCUMENTATION:
description: does not matter
type: raw
required: true
+ seealso:
+ - module: ansible.builtin.test
+ - module: testns.testcol.fakemodule
+ description: A fake module
+ - plugin: testns.testcol.noop
+ plugin_type: lookup
+ - plugin: testns.testcol.grouped
+ plugin_type: filter
+ description: A grouped filter.
+ - plugin: ansible.builtin.combine
+ plugin_type: filter
+ - plugin: ansible.builtin.file
+ plugin_type: lookup
+ description: Read a file on the controller.
+ - link: https://docs.ansible.com
+ name: Ansible docsite
+ description: See also the Ansible docsite.
+ - ref: foo_bar
+ description: Some foo bar.
EXAMPLES: |
{{ 'anything' is yolo }}
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/MANIFEST.json b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/MANIFEST.json
index 02ec289..e930d7d 100644
--- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/MANIFEST.json
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/MANIFEST.json
@@ -17,7 +17,7 @@
"version": "1.2.0",
"readme": "README.md",
"license_file": "COPYING",
- "homepage": "",
+ "homepage": ""
},
"file_manifest_file": {
"format": 1,
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml
new file mode 100644
index 0000000..bd6c15a
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/galaxy.yml
@@ -0,0 +1,6 @@
+namespace: testns
+name: testcol3
+version: 0.1.0
+readme: README.md
+authors:
+ - me
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py
new file mode 100644
index 0000000..02dfb89
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol3/plugins/modules/test1.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: test1
+short_description: Foo module in testcol3
+description:
+ - This is a foo module.
+author:
+ - me
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(),
+ )
+
+ module.exit_json()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml
new file mode 100644
index 0000000..7894d39
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/galaxy.yml
@@ -0,0 +1,6 @@
+namespace: testns
+name: testcol4
+version: 1.0.0
+readme: README.md
+authors:
+ - me
diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py
new file mode 100644
index 0000000..ddb0c11
--- /dev/null
+++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol4/plugins/modules/test2.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: test2
+short_description: Foo module in testcol4
+description:
+ - This is a foo module.
+author:
+ - me
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(),
+ )
+
+ module.exit_json()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/ansible-doc/randommodule-text.output b/test/integration/targets/ansible-doc/randommodule-text.output
index 602d66e..ca36134 100644
--- a/test/integration/targets/ansible-doc/randommodule-text.output
+++ b/test/integration/targets/ansible-doc/randommodule-text.output
@@ -1,6 +1,13 @@
> TESTNS.TESTCOL.RANDOMMODULE (./collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py)
- A random module.
+ A random module. See `foo=bar' (of role foo.bar.baz, main
+ entrypoint) for how this is used in the [foo.bar.baz]'s `main'
+ entrypoint. See the docsite <https://docs.ansible.com/ansible-
+ core/devel/> for more information on ansible-core. This module
+ is not related to the [ansible.builtin.copy] module.
+ ------------- You might also be interested in
+ ansible_python_interpreter. Sometimes you have [broken markup]
+ that will result in error messages.
ADDED IN: version 1.0.0 of testns.testcol
@@ -14,7 +21,8 @@ DEPRECATED:
OPTIONS (= is mandatory):
- sub
- Suboptions.
+ Suboptions. Contains `sub.subtest', which can be set to `123'.
+ You can use `TEST_ENV' to set this.
set_via:
env:
- deprecated:
@@ -29,7 +37,8 @@ OPTIONS (= is mandatory):
OPTIONS:
- subtest2
- Another suboption.
+ Another suboption. Useful when [ansible.builtin.shuffle]
+ is used with value `[a,b,),d\]'.
default: null
type: float
added in: version 1.1.0
@@ -39,14 +48,15 @@ OPTIONS (= is mandatory):
SUBOPTIONS:
- subtest
- A suboption.
+ A suboption. Not compatible to `path=c:\foo(1).txt' (of
+ module ansible.builtin.copy).
default: null
type: int
added in: version 1.1.0 of testns.testcol
- test
- Some text.
+ Some text. Consider not using `foo=bar'.
default: null
type: str
added in: version 1.2.0 of testns.testcol
@@ -93,13 +103,15 @@ EXAMPLES:
RETURN VALUES:
- a_first
- A first result.
+ A first result. Use `a_first=foo(bar\baz)bam'.
returned: success
type: str
- m_middle
This should be in the middle.
- Has some more data
+ Has some more data.
+ Check out `m_middle.suboption' and compare it to `a_first=foo'
+ and `value' (of lookup plugin community.general.foo).
returned: success and 1st of month
type: dict
diff --git a/test/integration/targets/ansible-doc/randommodule.output b/test/integration/targets/ansible-doc/randommodule.output
index cf03600..f40202a 100644
--- a/test/integration/targets/ansible-doc/randommodule.output
+++ b/test/integration/targets/ansible-doc/randommodule.output
@@ -12,14 +12,18 @@
"why": "Test deprecation"
},
"description": [
- "A random module."
+ "A random module.",
+ "See O(foo.bar.baz#role:main:foo=bar) for how this is used in the P(foo.bar.baz#role)'s C(main) entrypoint.",
+ "See L(the docsite,https://docs.ansible.com/ansible-core/devel/) for more information on ansible-core.",
+ "This module is not related to the M(ansible.builtin.copy) module. HORIZONTALLINE You might also be interested in R(ansible_python_interpreter, ansible_python_interpreter).",
+ "Sometimes you have M(broken markup) that will result in error messages."
],
"filename": "./collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py",
"has_action": false,
"module": "randommodule",
"options": {
"sub": {
- "description": "Suboptions.",
+ "description": "Suboptions. Contains O(sub.subtest), which can be set to V(123). You can use E(TEST_ENV) to set this.",
"env": [
{
"deprecated": {
@@ -34,14 +38,14 @@
],
"options": {
"subtest2": {
- "description": "Another suboption.",
+ "description": "Another suboption. Useful when P(ansible.builtin.shuffle#filter) is used with value V([a,b,\\),d\\\\]).",
"type": "float",
"version_added": "1.1.0"
}
},
"suboptions": {
"subtest": {
- "description": "A suboption.",
+ "description": "A suboption. Not compatible to O(ansible.builtin.copy#module:path=c:\\\\foo\\(1\\).txt).",
"type": "int",
"version_added": "1.1.0",
"version_added_collection": "testns.testcol"
@@ -50,7 +54,7 @@
"type": "dict"
},
"test": {
- "description": "Some text.",
+ "description": "Some text. Consider not using O(ignore:foo=bar).",
"type": "str",
"version_added": "1.2.0",
"version_added_collection": "testns.testcol"
@@ -103,7 +107,7 @@
"metadata": null,
"return": {
"a_first": {
- "description": "A first result.",
+ "description": "A first result. Use RV(a_first=foo\\(bar\\\\baz\\)bam).",
"returned": "success",
"type": "str"
},
@@ -123,7 +127,8 @@
},
"description": [
"This should be in the middle.",
- "Has some more data"
+ "Has some more data.",
+ "Check out RV(m_middle.suboption) and compare it to RV(a_first=foo) and RV(community.general.foo#lookup:value)."
],
"returned": "success and 1st of month",
"type": "dict"
diff --git a/test/integration/targets/ansible-doc/runme.sh b/test/integration/targets/ansible-doc/runme.sh
index f51fa8a..b525766 100755
--- a/test/integration/targets/ansible-doc/runme.sh
+++ b/test/integration/targets/ansible-doc/runme.sh
@@ -1,36 +1,74 @@
#!/usr/bin/env bash
-set -eux
+# always set sane error behaviors, enable execution tracing later if sufficient verbosity requested
+set -eu
+
+verbosity=0
+
+# default to silent output for naked grep; -vvv+ will adjust this
+export GREP_OPTS=-q
+
+# shell tracing output is very large from this script; only enable if >= -vvv was passed
+while getopts :v opt
+do case "$opt" in
+ v) ((verbosity+=1)) ;;
+ *) ;;
+ esac
+done
+
+if (( verbosity >= 3 ));
+then
+ set -x;
+ export GREP_OPTS= ;
+fi
+
+echo "running playbook-backed docs tests"
ansible-playbook test.yml -i inventory "$@"
# test keyword docs
-ansible-doc -t keyword -l | grep 'vars_prompt: list of variables to prompt for.'
-ansible-doc -t keyword vars_prompt | grep 'description: list of variables to prompt for.'
-ansible-doc -t keyword asldkfjaslidfhals 2>&1 | grep 'Skipping Invalid keyword'
+ansible-doc -t keyword -l | grep $GREP_OPTS 'vars_prompt: list of variables to prompt for.'
+ansible-doc -t keyword vars_prompt | grep $GREP_OPTS 'description: list of variables to prompt for.'
+ansible-doc -t keyword asldkfjaslidfhals 2>&1 | grep $GREP_OPTS 'Skipping Invalid keyword'
# collections testing
(
unset ANSIBLE_PLAYBOOK_DIR
cd "$(dirname "$0")"
-# test module docs from collection
+
+echo "test fakemodule docs from collection"
# we use sed to strip the module path from the first line
current_out="$(ansible-doc --playbook-dir ./ testns.testcol.fakemodule | sed '1 s/\(^> TESTNS\.TESTCOL\.FAKEMODULE\).*(.*)$/\1/')"
expected_out="$(sed '1 s/\(^> TESTNS\.TESTCOL\.FAKEMODULE\).*(.*)$/\1/' fakemodule.output)"
test "$current_out" == "$expected_out"
+echo "test randommodule docs from collection"
# we use sed to strip the plugin path from the first line, and fix-urls.py to unbreak and replace URLs from stable-X branches
current_out="$(ansible-doc --playbook-dir ./ testns.testcol.randommodule | sed '1 s/\(^> TESTNS\.TESTCOL\.RANDOMMODULE\).*(.*)$/\1/' | python fix-urls.py)"
expected_out="$(sed '1 s/\(^> TESTNS\.TESTCOL\.RANDOMMODULE\).*(.*)$/\1/' randommodule-text.output)"
test "$current_out" == "$expected_out"
-# ensure we do work with valid collection name for list
-ansible-doc --list testns.testcol --playbook-dir ./ 2>&1 | grep -v "Invalid collection name"
+echo "test yolo filter docs from collection"
+# we use sed to strip the plugin path from the first line, and fix-urls.py to unbreak and replace URLs from stable-X branches
+current_out="$(ansible-doc --playbook-dir ./ testns.testcol.yolo --type test | sed '1 s/\(^> TESTNS\.TESTCOL\.YOLO\).*(.*)$/\1/' | python fix-urls.py)"
+expected_out="$(sed '1 s/\(^> TESTNS\.TESTCOL\.YOLO\).*(.*)$/\1/' yolo-text.output)"
+test "$current_out" == "$expected_out"
+
+echo "ensure we do work with valid collection name for list"
+ansible-doc --list testns.testcol --playbook-dir ./ 2>&1 | grep $GREP_OPTS -v "Invalid collection name"
-# ensure we dont break on invalid collection name for list
-ansible-doc --list testns.testcol.fakemodule --playbook-dir ./ 2>&1 | grep "Invalid collection name"
+echo "ensure we dont break on invalid collection name for list"
+ansible-doc --list testns.testcol.fakemodule --playbook-dir ./ 2>&1 | grep $GREP_OPTS "Invalid collection name"
-# test listing diff plugin types from collection
+echo "filter list with more than one collection (1/2)"
+output=$(ansible-doc --list testns.testcol3 testns.testcol4 --playbook-dir ./ 2>&1 | wc -l)
+test "$output" -eq 2
+
+echo "filter list with more than one collection (2/2)"
+output=$(ansible-doc --list testns.testcol testns.testcol4 --playbook-dir ./ 2>&1 | wc -l)
+test "$output" -eq 5
+
+echo "testing ansible-doc output for various plugin types"
for ptype in cache inventory lookup vars filter module
do
# each plugin type adds 1 from collection
@@ -50,20 +88,20 @@ do
elif [ "${ptype}" == "lookup" ]; then expected_names=("noop");
elif [ "${ptype}" == "vars" ]; then expected_names=("noop_vars_plugin"); fi
fi
- # ensure we ONLY list from the collection
+ echo "testing collection-filtered list for plugin ${ptype}"
justcol=$(ansible-doc -l -t ${ptype} --playbook-dir ./ testns.testcol|wc -l)
test "$justcol" -eq "$expected"
- # ensure the right names are displayed
+ echo "validate collection plugin name display for plugin ${ptype}"
list_result=$(ansible-doc -l -t ${ptype} --playbook-dir ./ testns.testcol)
metadata_result=$(ansible-doc --metadata-dump --no-fail-on-errors -t ${ptype} --playbook-dir ./ testns.testcol)
for name in "${expected_names[@]}"; do
- echo "${list_result}" | grep "testns.testcol.${name}"
- echo "${metadata_result}" | grep "testns.testcol.${name}"
+ echo "${list_result}" | grep $GREP_OPTS "testns.testcol.${name}"
+ echo "${metadata_result}" | grep $GREP_OPTS "testns.testcol.${name}"
done
- # ensure we get error if passinginvalid collection, much less any plugins
- ansible-doc -l -t ${ptype} testns.testcol 2>&1 | grep "unable to locate collection"
+ # ensure we get error if passing invalid collection, much less any plugins
+ ansible-doc -l -t ${ptype} bogus.boguscoll 2>&1 | grep $GREP_OPTS "unable to locate collection"
# TODO: do we want per namespace?
# ensure we get 1 plugins when restricting namespace
@@ -73,20 +111,28 @@ done
#### test role functionality
-# Test role text output
+echo "testing role text output"
# we use sed to strip the role path from the first line
current_role_out="$(ansible-doc -t role -r ./roles test_role1 | sed '1 s/\(^> TEST_ROLE1\).*(.*)$/\1/')"
expected_role_out="$(sed '1 s/\(^> TEST_ROLE1\).*(.*)$/\1/' fakerole.output)"
test "$current_role_out" == "$expected_role_out"
+echo "testing multiple role entrypoints"
# Two collection roles are defined, but only 1 has a role arg spec with 2 entry points
output=$(ansible-doc -t role -l --playbook-dir . testns.testcol | wc -l)
test "$output" -eq 2
+echo "test listing roles with multiple collection filters"
+# Two collection roles are defined, but only 1 has a role arg spec with 2 entry points
+output=$(ansible-doc -t role -l --playbook-dir . testns.testcol2 testns.testcol | wc -l)
+test "$output" -eq 2
+
+echo "testing standalone roles"
# Include normal roles (no collection filter)
output=$(ansible-doc -t role -l --playbook-dir . | wc -l)
test "$output" -eq 3
+echo "testing role precedence"
# Test that a role in the playbook dir with the same name as a role in the
# 'roles' subdir of the playbook dir does not appear (lower precedence).
output=$(ansible-doc -t role -l --playbook-dir . | grep -c "test_role1 from roles subdir")
@@ -94,7 +140,7 @@ test "$output" -eq 1
output=$(ansible-doc -t role -l --playbook-dir . | grep -c "test_role1 from playbook dir" || true)
test "$output" -eq 0
-# Test entry point filter
+echo "testing role entrypoint filter"
current_role_out="$(ansible-doc -t role --playbook-dir . testns.testcol.testrole -e alternate| sed '1 s/\(^> TESTNS\.TESTCOL\.TESTROLE\).*(.*)$/\1/')"
expected_role_out="$(sed '1 s/\(^> TESTNS\.TESTCOL\.TESTROLE\).*(.*)$/\1/' fakecollrole.output)"
test "$current_role_out" == "$expected_role_out"
@@ -103,10 +149,16 @@ test "$current_role_out" == "$expected_role_out"
#### test add_collection_to_versions_and_dates()
+echo "testing json output"
current_out="$(ansible-doc --json --playbook-dir ./ testns.testcol.randommodule | sed 's/ *$//' | sed 's/ *"filename": "[^"]*",$//')"
expected_out="$(sed 's/ *"filename": "[^"]*",$//' randommodule.output)"
test "$current_out" == "$expected_out"
+echo "testing json output 2"
+current_out="$(ansible-doc --json --playbook-dir ./ testns.testcol.yolo --type test | sed 's/ *$//' | sed 's/ *"filename": "[^"]*",$//')"
+expected_out="$(sed 's/ *"filename": "[^"]*",$//' yolo.output)"
+test "$current_out" == "$expected_out"
+
current_out="$(ansible-doc --json --playbook-dir ./ -t cache testns.testcol.notjsonfile | sed 's/ *$//' | sed 's/ *"filename": "[^"]*",$//')"
expected_out="$(sed 's/ *"filename": "[^"]*",$//' notjsonfile.output)"
test "$current_out" == "$expected_out"
@@ -119,8 +171,9 @@ current_out="$(ansible-doc --json --playbook-dir ./ -t vars testns.testcol.noop_
expected_out="$(sed 's/ *"filename": "[^"]*",$//' noop_vars_plugin.output)"
test "$current_out" == "$expected_out"
+echo "testing metadata dump"
# just ensure it runs
-ANSIBLE_LIBRARY='./nolibrary' ansible-doc --metadata-dump --playbook-dir /dev/null >/dev/null
+ANSIBLE_LIBRARY='./nolibrary' ansible-doc --metadata-dump --playbook-dir /dev/null 1>/dev/null 2>&1
# create broken role argument spec
mkdir -p broken-docs/collections/ansible_collections/testns/testcol/roles/testrole/meta
@@ -144,71 +197,72 @@ argument_specs:
EOF
# ensure that --metadata-dump does not fail when --no-fail-on-errors is supplied
-ANSIBLE_LIBRARY='./nolibrary' ansible-doc --metadata-dump --no-fail-on-errors --playbook-dir broken-docs testns.testcol >/dev/null
+ANSIBLE_LIBRARY='./nolibrary' ansible-doc --metadata-dump --no-fail-on-errors --playbook-dir broken-docs testns.testcol 1>/dev/null 2>&1
# ensure that --metadata-dump does fail when --no-fail-on-errors is not supplied
output=$(ANSIBLE_LIBRARY='./nolibrary' ansible-doc --metadata-dump --playbook-dir broken-docs testns.testcol 2>&1 | grep -c 'ERROR!' || true)
test "${output}" -eq 1
-# ensure we list the 'legacy plugins'
+
+echo "testing legacy plugin listing"
[ "$(ansible-doc -M ./library -l ansible.legacy |wc -l)" -gt "0" ]
-# playbook dir should work the same
+echo "testing legacy plugin list via --playbook-dir"
[ "$(ansible-doc -l ansible.legacy --playbook-dir ./|wc -l)" -gt "0" ]
-# see that we show undocumented when missing docs
+echo "testing undocumented plugin output"
[ "$(ansible-doc -M ./library -l ansible.legacy |grep -c UNDOCUMENTED)" == "6" ]
-# ensure filtering works and does not include any 'test_' modules
+echo "testing filtering does not include any 'test_' modules"
[ "$(ansible-doc -M ./library -l ansible.builtin |grep -c test_)" == 0 ]
[ "$(ansible-doc --playbook-dir ./ -l ansible.builtin |grep -c test_)" == 0 ]
-# ensure filtering still shows modules
+echo "testing filtering still shows modules"
count=$(ANSIBLE_LIBRARY='./nolibrary' ansible-doc -l ansible.builtin |wc -l)
[ "${count}" -gt "0" ]
[ "$(ansible-doc -M ./library -l ansible.builtin |wc -l)" == "${count}" ]
[ "$(ansible-doc --playbook-dir ./ -l ansible.builtin |wc -l)" == "${count}" ]
-# produce 'sidecar' docs for test
+echo "testing sidecar docs for jinja plugins"
[ "$(ansible-doc -t test --playbook-dir ./ testns.testcol.yolo| wc -l)" -gt "0" ]
[ "$(ansible-doc -t filter --playbook-dir ./ donothing| wc -l)" -gt "0" ]
[ "$(ansible-doc -t filter --playbook-dir ./ ansible.legacy.donothing| wc -l)" -gt "0" ]
-# no docs and no sidecar
-ansible-doc -t filter --playbook-dir ./ nodocs 2>&1| grep -c 'missing documentation' || true
+echo "testing no docs and no sidecar"
+ansible-doc -t filter --playbook-dir ./ nodocs 2>&1| grep $GREP_OPTS -c 'missing documentation' || true
-# produce 'sidecar' docs for module
+echo "testing sidecar docs for module"
[ "$(ansible-doc -M ./library test_win_module| wc -l)" -gt "0" ]
[ "$(ansible-doc --playbook-dir ./ test_win_module| wc -l)" -gt "0" ]
-# test 'double DOCUMENTATION' use
+echo "testing duplicate DOCUMENTATION"
[ "$(ansible-doc --playbook-dir ./ double_doc| wc -l)" -gt "0" ]
-# don't break on module dir
+echo "testing don't break on module dir"
ansible-doc --list --module-path ./modules > /dev/null
-# ensure we dedupe by fqcn and not base name
+echo "testing dedupe by fqcn and not base name"
[ "$(ansible-doc -l -t filter --playbook-dir ./ |grep -c 'b64decode')" -eq "3" ]
-# ensure we don't show duplicates for plugins that only exist in ansible.builtin when listing ansible.legacy plugins
+echo "testing no duplicates for plugins that only exist in ansible.builtin when listing ansible.legacy plugins"
[ "$(ansible-doc -l -t filter --playbook-dir ./ |grep -c 'b64encode')" -eq "1" ]
-# with playbook dir, legacy should override
-ansible-doc -t filter split --playbook-dir ./ |grep histerical
+echo "testing with playbook dir, legacy should override"
+ansible-doc -t filter split --playbook-dir ./ |grep $GREP_OPTS histerical
pyc_src="$(pwd)/filter_plugins/other.py"
pyc_1="$(pwd)/filter_plugins/split.pyc"
pyc_2="$(pwd)/library/notaplugin.pyc"
trap 'rm -rf "$pyc_1" "$pyc_2"' EXIT
-# test pyc files are not used as adjacent documentation
+echo "testing pyc files are not used as adjacent documentation"
python -c "import py_compile; py_compile.compile('$pyc_src', cfile='$pyc_1')"
-ansible-doc -t filter split --playbook-dir ./ |grep histerical
+ansible-doc -t filter split --playbook-dir ./ |grep $GREP_OPTS histerical
-# test pyc files are not listed as plugins
+echo "testing pyc files are not listed as plugins"
python -c "import py_compile; py_compile.compile('$pyc_src', cfile='$pyc_2')"
test "$(ansible-doc -l -t module --playbook-dir ./ 2>&1 1>/dev/null |grep -c "notaplugin")" == 0
-# without playbook dir, builtin should return
-ansible-doc -t filter split |grep -v histerical
+echo "testing without playbook dir, builtin should return"
+ansible-doc -t filter split 2>&1 |grep $GREP_OPTS -v histerical
diff --git a/test/integration/targets/ansible-doc/yolo-text.output b/test/integration/targets/ansible-doc/yolo-text.output
new file mode 100644
index 0000000..647a4f6
--- /dev/null
+++ b/test/integration/targets/ansible-doc/yolo-text.output
@@ -0,0 +1,47 @@
+> TESTNS.TESTCOL.YOLO (./collections/ansible_collections/testns/testcol/plugins/test/yolo.yml)
+
+ This is always true
+
+OPTIONS (= is mandatory):
+
+= _input
+ does not matter
+ type: raw
+
+
+SEE ALSO:
+ * Module ansible.builtin.test
+ The official documentation on the
+ ansible.builtin.test module.
+ https://docs.ansible.com/ansible-core/devel/collections/ansible/builtin/test_module.html
+ * Module testns.testcol.fakemodule
+ A fake module
+ * Lookup plugin testns.testcol.noop
+ * Filter plugin testns.testcol.grouped
+ A grouped filter.
+ * Filter plugin ansible.builtin.combine
+ The official documentation on the
+ ansible.builtin.combine filter plugin.
+ https://docs.ansible.com/ansible-core/devel/collections/ansible/builtin/combine_filter.html
+ * Lookup plugin ansible.builtin.file
+ Read a file on the controller.
+ https://docs.ansible.com/ansible-core/devel/collections/ansible/builtin/file_lookup.html
+ * Ansible docsite
+ See also the Ansible docsite.
+ https://docs.ansible.com
+ * Ansible documentation [foo_bar]
+ Some foo bar.
+ https://docs.ansible.com/ansible-core/devel/#stq=foo_bar&stp=1
+
+
+NAME: yolo
+
+EXAMPLES:
+
+{{ 'anything' is yolo }}
+
+
+RETURN VALUES:
+- output
+ always true
+ type: boolean
diff --git a/test/integration/targets/ansible-doc/yolo.output b/test/integration/targets/ansible-doc/yolo.output
new file mode 100644
index 0000000..b54cc2d
--- /dev/null
+++ b/test/integration/targets/ansible-doc/yolo.output
@@ -0,0 +1,64 @@
+{
+ "testns.testcol.yolo": {
+ "doc": {
+ "collection": "testns.testcol",
+ "description": [
+ "This is always true"
+ ],
+ "filename": "./collections/ansible_collections/testns/testcol/plugins/test/yolo.yml",
+ "name": "yolo",
+ "options": {
+ "_input": {
+ "description": "does not matter",
+ "required": true,
+ "type": "raw"
+ }
+ },
+ "seealso": [
+ {
+ "module": "ansible.builtin.test"
+ },
+ {
+ "description": "A fake module",
+ "module": "testns.testcol.fakemodule"
+ },
+ {
+ "plugin": "testns.testcol.noop",
+ "plugin_type": "lookup"
+ },
+ {
+ "description": "A grouped filter.",
+ "plugin": "testns.testcol.grouped",
+ "plugin_type": "filter"
+ },
+ {
+ "plugin": "ansible.builtin.combine",
+ "plugin_type": "filter"
+ },
+ {
+ "description": "Read a file on the controller.",
+ "plugin": "ansible.builtin.file",
+ "plugin_type": "lookup"
+ },
+ {
+ "description": "See also the Ansible docsite.",
+ "link": "https://docs.ansible.com",
+ "name": "Ansible docsite"
+ },
+ {
+ "description": "Some foo bar.",
+ "ref": "foo_bar"
+ }
+ ],
+ "short_description": "you only live once"
+ },
+ "examples": "{{ 'anything' is yolo }}\n",
+ "metadata": null,
+ "return": {
+ "output": {
+ "description": "always true",
+ "type": "boolean"
+ }
+ }
+ }
+}
diff --git a/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt b/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt
index 110009e..6921829 100644
--- a/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt
+++ b/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt
@@ -1,6 +1,11 @@
MANIFEST.json
FILES.json
README.rst
+GPL
+LICENSES/
+LICENSES/MIT.txt
+.reuse/
+.reuse/dep5
changelogs/
docs/
playbooks/
@@ -88,6 +93,7 @@ plugins/test/bar.yml
plugins/test/baz.yaml
plugins/test/test.py
plugins/vars/bar.yml
+plugins/vars/bar.yml.license
plugins/vars/baz.yaml
plugins/vars/test.py
roles/foo/
diff --git a/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml b/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml
index 8f0ada0..140bf2a 100644
--- a/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml
+++ b/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml
@@ -2,6 +2,7 @@ namespace: ns
name: col
version: 1.0.0
readme: README.rst
+license_file: GPL
authors:
- Ansible
manifest:
diff --git a/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py b/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py
index 913a6f7..60c43cc 100644
--- a/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py
+++ b/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py
@@ -5,8 +5,12 @@ paths = [
'ns-col-1.0.0.tar.gz',
'foo.txt',
'README.rst',
+ 'GPL',
+ 'LICENSES/MIT.txt',
+ '.reuse/dep5',
'artifacts/.gitkeep',
'plugins/vars/bar.yml',
+ 'plugins/vars/bar.yml.license',
'plugins/vars/baz.yaml',
'plugins/vars/test.py',
'plugins/vars/docs.md',
diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml
index dab599b..f0e78ca 100644
--- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml
+++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml
@@ -5,7 +5,7 @@
- name: Test installing collections from git repositories
environment:
- ANSIBLE_COLLECTIONS_PATHS: "{{ galaxy_dir }}/collections"
+ ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/collections"
vars:
cleanup: True
galaxy_dir: "{{ galaxy_dir }}"
diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_all.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_all.yml
index f22f984..91ed912 100644
--- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_all.yml
+++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_all.yml
@@ -14,6 +14,8 @@
command: 'ansible-galaxy collection install {{ artifact_path }} -p {{ alt_install_path }} --no-deps'
vars:
artifact_path: "{{ galaxy_dir }}/ansible_test-collection_1-1.0.0.tar.gz"
+ environment:
+ ANSIBLE_COLLECTIONS_PATH: ""
- name: check if the files and folders in build_ignore were respected
stat:
diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_recursive_scm_dependency.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_recursive_scm_dependency.yml
index dd307d7..520dbe5 100644
--- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_recursive_scm_dependency.yml
+++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_recursive_scm_dependency.yml
@@ -22,7 +22,12 @@
lineinfile:
path: '{{ scm_path }}/namespace_2/collection_2/galaxy.yml'
regexp: '^dependencies'
- line: "dependencies: {'git+file://{{ scm_path }}/namespace_1/.git#collection_1/': 'master'}"
+ # NOTE: The committish is set to `HEAD` here because Git's default has
+ # NOTE: changed to `main` and it behaves differently in
+ # NOTE: different envs with different Git versions.
+ line: >-
+ dependencies:
+ {'git+file://{{ scm_path }}/namespace_1/.git#collection_1/': 'HEAD'}
- name: Commit the changes
shell: git add ./; git commit -m 'add collection'
diff --git a/test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py b/test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py
index 53c29f7..c1f5e1d 100644
--- a/test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py
+++ b/test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py
@@ -84,7 +84,8 @@ def invoke_api(module, url, method='GET', data=None, status_codes=None):
resp, info = fetch_url(module, url, method=method, data=data, headers=headers)
if info['status'] not in status_codes:
- module.fail_json(url=url, **info)
+ info['url'] = url
+ module.fail_json(**info)
data = to_text(resp.read())
if data:
@@ -105,7 +106,7 @@ def delete_pulp_distribution(distribution, module):
def delete_pulp_orphans(module):
""" Deletes any orphaned pulp objects. """
- orphan_uri = module.params['pulp_api'] + '/pulp/api/v3/orphans/'
+ orphan_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/orphans/'
task_info = invoke_api(module, orphan_uri, method='DELETE', status_codes=[202])
wait_pulp_task(task_info['task'], module)
@@ -125,25 +126,39 @@ def get_galaxy_namespaces(module):
return [n['name'] for n in ns_info['data']]
-def get_pulp_distributions(module):
+def get_pulp_distributions(module, distribution):
""" Gets a list of all the pulp distributions. """
- distro_uri = module.params['pulp_api'] + '/pulp/api/v3/distributions/ansible/ansible/'
- distro_info = invoke_api(module, distro_uri)
+ distro_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/distributions/ansible/ansible/'
+ distro_info = invoke_api(module, distro_uri + '?name=' + distribution)
return [module.params['pulp_api'] + r['pulp_href'] for r in distro_info['results']]
-def get_pulp_repositories(module):
+def get_pulp_repositories(module, repository):
""" Gets a list of all the pulp repositories. """
- repo_uri = module.params['pulp_api'] + '/pulp/api/v3/repositories/ansible/ansible/'
- repo_info = invoke_api(module, repo_uri)
+ repo_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/repositories/ansible/ansible/'
+ repo_info = invoke_api(module, repo_uri + '?name=' + repository)
return [module.params['pulp_api'] + r['pulp_href'] for r in repo_info['results']]
+def get_repo_collections(repository, module):
+ collections_uri = module.params['galaxy_ng_server'] + 'v3/plugin/ansible/content/' + repository + '/collections/index/'
+ # status code 500 isn't really expected, an unhandled exception is causing this instead of a 404
+ # See https://issues.redhat.com/browse/AAH-2329
+ info = invoke_api(module, collections_uri + '?limit=100&offset=0', status_codes=[200, 500])
+ if not info:
+ return []
+ return [module.params['pulp_api'] + c['href'] for c in info['data']]
+
+
+def delete_repo_collection(collection, module):
+ task_info = invoke_api(module, collection, method='DELETE', status_codes=[202])
+ wait_pulp_task(task_info['task'], module)
+
+
def new_galaxy_namespace(name, module):
""" Creates a new namespace in Galaxy NG. """
- ns_uri = module.params['galaxy_ng_server'] + 'v3/_ui/namespaces/'
- data = {'name': name, 'groups': [{'name': 'system:partner-engineers', 'object_permissions':
- ['add_namespace', 'change_namespace', 'upload_to_namespace']}]}
+ ns_uri = module.params['galaxy_ng_server'] + 'v3/namespaces/ '
+ data = {'name': name, 'groups': []}
ns_info = invoke_api(module, ns_uri, method='POST', data=data, status_codes=[201])
return ns_info['id']
@@ -151,16 +166,17 @@ def new_galaxy_namespace(name, module):
def new_pulp_repository(name, module):
""" Creates a new pulp repository. """
- repo_uri = module.params['pulp_api'] + '/pulp/api/v3/repositories/ansible/ansible/'
- data = {'name': name}
+ repo_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/repositories/ansible/ansible/'
+ # retain_repo_versions to work around https://issues.redhat.com/browse/AAH-2332
+ data = {'name': name, 'retain_repo_versions': '1024'}
repo_info = invoke_api(module, repo_uri, method='POST', data=data, status_codes=[201])
- return module.params['pulp_api'] + repo_info['pulp_href']
+ return repo_info['pulp_href']
def new_pulp_distribution(name, base_path, repository, module):
""" Creates a new pulp distribution for a repository. """
- distro_uri = module.params['pulp_api'] + '/pulp/api/v3/distributions/ansible/ansible/'
+ distro_uri = module.params['galaxy_ng_server'] + 'pulp/api/v3/distributions/ansible/ansible/'
data = {'name': name, 'base_path': base_path, 'repository': repository}
task_info = invoke_api(module, distro_uri, method='POST', data=data, status_codes=[202])
task_info = wait_pulp_task(task_info['task'], module)
@@ -194,8 +210,15 @@ def main():
)
module.params['force_basic_auth'] = True
- [delete_pulp_distribution(d, module) for d in get_pulp_distributions(module)]
- [delete_pulp_repository(r, module) for r in get_pulp_repositories(module)]
+ # It may be due to the process of cleaning up orphans, but we cannot delete the namespace
+ # while a collection still exists, so this is just a new safety to nuke all collections
+ # first
+ for repository in module.params['repositories']:
+ [delete_repo_collection(c, module) for c in get_repo_collections(repository, module)]
+
+ for repository in module.params['repositories']:
+ [delete_pulp_distribution(d, module) for d in get_pulp_distributions(module, repository)]
+ [delete_pulp_repository(r, module) for r in get_pulp_repositories(module, repository)]
delete_pulp_orphans(module)
[delete_galaxy_namespace(n, module) for n in get_galaxy_namespaces(module)]
diff --git a/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py b/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py
index f4a51c4..423edd9 100644
--- a/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py
+++ b/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py
@@ -77,6 +77,7 @@ RETURN = '''
#
'''
+import datetime
import os
import subprocess
import tarfile
@@ -84,13 +85,13 @@ import tempfile
import yaml
from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_bytes
+from ansible.module_utils.common.text.converters import to_bytes
from functools import partial
from multiprocessing import dummy as threading
from multiprocessing import TimeoutError
-COLLECTIONS_BUILD_AND_PUBLISH_TIMEOUT = 120
+COLLECTIONS_BUILD_AND_PUBLISH_TIMEOUT = 180
def publish_collection(module, collection):
@@ -104,6 +105,7 @@ def publish_collection(module, collection):
collection_dir = os.path.join(module.tmpdir, "%s-%s-%s" % (namespace, name, version))
b_collection_dir = to_bytes(collection_dir, errors='surrogate_or_strict')
os.mkdir(b_collection_dir)
+ os.mkdir(os.path.join(b_collection_dir, b'meta'))
with open(os.path.join(b_collection_dir, b'README.md'), mode='wb') as fd:
fd.write(b"Collection readme")
@@ -120,6 +122,8 @@ def publish_collection(module, collection):
}
with open(os.path.join(b_collection_dir, b'galaxy.yml'), mode='wb') as fd:
fd.write(to_bytes(yaml.safe_dump(galaxy_meta), errors='surrogate_or_strict'))
+ with open(os.path.join(b_collection_dir, b'meta/runtime.yml'), mode='wb') as fd:
+ fd.write(b'requires_ansible: ">=1.0.0"')
with tempfile.NamedTemporaryFile(mode='wb') as temp_fd:
temp_fd.write(b"data")
@@ -246,7 +250,8 @@ def run_module():
supports_check_mode=False
)
- result = dict(changed=True, results=[])
+ start = datetime.datetime.now()
+ result = dict(changed=True, results=[], start=str(start))
pool = threading.Pool(4)
publish_func = partial(publish_collection, module)
@@ -263,7 +268,9 @@ def run_module():
r['build']['rc'] + r['publish']['rc'] for r in result['results']
))
- module.exit_json(failed=failed, **result)
+ end = datetime.datetime.now()
+ delta = end - start
+ module.exit_json(failed=failed, end=str(end), delta=str(delta), **result)
def main():
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/build.yml b/test/integration/targets/ansible-galaxy-collection/tasks/build.yml
index 8140d46..83e9acc 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/build.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/build.yml
@@ -1,4 +1,29 @@
---
+- name: create a dangling symbolic link inside collection directory
+ ansible.builtin.file:
+ src: '/non-existent-path/README.md'
+ dest: '{{ galaxy_dir }}/scratch/ansible_test/my_collection/docs/README.md'
+ state: link
+ force: yes
+
+- name: build basic collection based on current directory with dangling symlink
+ command: ansible-galaxy collection build {{ galaxy_verbosity }}
+ args:
+ chdir: '{{ galaxy_dir }}/scratch/ansible_test/my_collection'
+ register: fail_symlink_build
+ ignore_errors: yes
+
+- name: assert that build fails due to dangling symlink
+ assert:
+ that:
+ - fail_symlink_build.failed
+ - '"Failed to find the target path" in fail_symlink_build.stderr'
+
+- name: remove dangling symbolic link
+ ansible.builtin.file:
+ path: '{{ galaxy_dir }}/scratch/ansible_test/my_collection/docs/README.md'
+ state: absent
+
- name: build basic collection based on current directory
command: ansible-galaxy collection build {{ galaxy_verbosity }}
args:
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/download.yml b/test/integration/targets/ansible-galaxy-collection/tasks/download.yml
index b651a73..a554c27 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/download.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/download.yml
@@ -5,7 +5,7 @@
state: directory
- name: download collection with multiple dependencies with --no-deps
- command: ansible-galaxy collection download parent_dep.parent_collection:1.0.0 --no-deps -s pulp_v2 {{ galaxy_verbosity }}
+ command: ansible-galaxy collection download parent_dep.parent_collection:1.0.0 --no-deps -s galaxy_ng {{ galaxy_verbosity }}
register: download_collection
args:
chdir: '{{ galaxy_dir }}/download'
@@ -34,7 +34,7 @@
- (download_collection_actual.files[1].path | basename) in ['requirements.yml', 'parent_dep-parent_collection-1.0.0.tar.gz']
- name: download collection with multiple dependencies
- command: ansible-galaxy collection download parent_dep.parent_collection:1.0.0 -s pulp_v2 {{ galaxy_verbosity }}
+ command: ansible-galaxy collection download parent_dep.parent_collection:1.0.0 -s galaxy_ng {{ galaxy_verbosity }}
register: download_collection
args:
chdir: '{{ galaxy_dir }}/download'
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/fail_fast_resolvelib.yml b/test/integration/targets/ansible-galaxy-collection/tasks/fail_fast_resolvelib.yml
index eb471f8..d861cb4 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/fail_fast_resolvelib.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/fail_fast_resolvelib.yml
@@ -1,5 +1,5 @@
# resolvelib>=0.6.0 added an 'incompatibilities' parameter to find_matches
-# If incompatibilities aren't removed from the viable candidates, this example causes infinite resursion
+# If incompatibilities aren't removed from the viable candidates, this example causes infinite recursion
- name: test resolvelib removes incompatibilites in find_matches and errors quickly (prevent infinite recursion)
block:
- name: create collection dir
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/init.yml b/test/integration/targets/ansible-galaxy-collection/tasks/init.yml
index 17a000d..46198fe 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/init.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/init.yml
@@ -5,6 +5,12 @@
chdir: '{{ galaxy_dir }}/scratch'
register: init_relative
+- name: create required runtime.yml
+ copy:
+ content: |
+ requires_ansible: '>=1.0.0'
+ dest: '{{ galaxy_dir }}/scratch/ansible_test/my_collection/meta/runtime.yml'
+
- name: get result of create default skeleton
find:
path: '{{ galaxy_dir }}/scratch/ansible_test/my_collection'
@@ -92,6 +98,65 @@
- (init_custom_path_actual.files | map(attribute='path') | list)[2] | basename in ['docs', 'plugins', 'roles', 'meta']
- (init_custom_path_actual.files | map(attribute='path') | list)[3] | basename in ['docs', 'plugins', 'roles', 'meta']
+- name: test using a custom skeleton for collection init
+ block:
+ - name: create skeleton directories
+ file:
+ path: "{{ galaxy_dir }}/scratch/skeleton/{{ item }}"
+ state: directory
+ loop:
+ - custom_skeleton
+ - custom_skeleton/plugins
+ - inventory
+
+ - name: create files
+ file:
+ path: "{{ galaxy_dir }}/scratch/skeleton/{{ item }}"
+ state: touch
+ loop:
+ - inventory/foo.py
+ - galaxy.yml
+
+ - name: create symlinks
+ file:
+ path: "{{ galaxy_dir }}/scratch/skeleton/{{ item.link }}"
+ src: "{{ galaxy_dir }}/scratch/skeleton/{{ item.source }}"
+ state: link
+ loop:
+ - link: custom_skeleton/plugins/inventory
+ source: inventory
+ - link: custom_skeleton/galaxy.yml
+ source: galaxy.yml
+
+ - name: initialize a collection using the skeleton
+ command: ansible-galaxy collection init ansible_test.my_collection {{ init_path }} {{ skeleton }}
+ vars:
+ init_path: '--init-path {{ galaxy_dir }}/scratch/skeleton'
+ skeleton: '--collection-skeleton {{ galaxy_dir }}/scratch/skeleton/custom_skeleton'
+
+ - name: stat expected collection contents
+ stat:
+ path: "{{ galaxy_dir }}/scratch/skeleton/ansible_test/my_collection/{{ item }}"
+ register: stat_result
+ loop:
+ - plugins
+ - plugins/inventory
+ - galaxy.yml
+ - plugins/inventory/foo.py
+
+ - assert:
+ that:
+ - stat_result.results[0].stat.isdir
+ - stat_result.results[1].stat.islnk
+ - stat_result.results[2].stat.islnk
+ - stat_result.results[3].stat.isreg
+
+ always:
+ - name: cleanup
+ file:
+ path: "{{ galaxy_dir }}/scratch/skeleton"
+ state: absent
+
- name: create collection for ignored files and folders
command: ansible-galaxy collection init ansible_test.ignore
args:
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/install.yml b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml
index cca83c7..9237826 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/install.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml
@@ -165,10 +165,13 @@
failed_when:
- '"Could not satisfy the following requirements" not in fail_dep_mismatch.stderr'
- '" fail_dep2.name:<0.0.5 (dependency of fail_namespace.fail_collection:2.1.2)" not in fail_dep_mismatch.stderr'
+ - 'pre_release_hint not in fail_dep_mismatch.stderr'
+ vars:
+ pre_release_hint: 'Hint: Pre-releases are not installed by default unless the specific version is given. To enable pre-releases, use --pre.'
- name: Find artifact url for namespace3.name
uri:
- url: '{{ test_server }}{{ vX }}collections/namespace3/name/versions/1.0.0/'
+ url: '{{ test_api_server }}v3/plugin/ansible/content/primary/collections/index/namespace3/name/versions/1.0.0/'
user: '{{ pulp_user }}'
password: '{{ pulp_password }}'
force_basic_auth: true
@@ -218,7 +221,7 @@
state: absent
- assert:
- that: error == expected_error
+ that: expected_error in error
vars:
error: "{{ result.stderr | regex_replace('\\n', ' ') }}"
expected_error: >-
@@ -258,12 +261,14 @@
ignore_errors: yes
register: result
- - debug: msg="Actual - {{ error }}"
+ - debug:
+ msg: "Actual - {{ error }}"
- - debug: msg="Expected - {{ expected_error }}"
+ - debug:
+ msg: "Expected - {{ expected_error }}"
- assert:
- that: error == expected_error
+ that: expected_error in error
always:
- name: clean up collection skeleton and artifact
file:
@@ -295,7 +300,7 @@
- name: Find artifact url for namespace4.name
uri:
- url: '{{ test_server }}{{ vX }}collections/namespace4/name/versions/1.0.0/'
+ url: '{{ test_api_server }}v3/plugin/ansible/content/primary/collections/index/namespace4/name/versions/1.0.0/'
user: '{{ pulp_user }}'
password: '{{ pulp_password }}'
force_basic_auth: true
@@ -325,10 +330,11 @@
environment:
ANSIBLE_GALAXY_SERVER_LIST: undefined
-- when: not requires_auth
+# pulp_v2 doesn't require auth
+- when: v2|default(false)
block:
- name: install a collection with an empty server list - {{ test_id }}
- command: ansible-galaxy collection install namespace5.name -s '{{ test_server }}' {{ galaxy_verbosity }}
+ command: ansible-galaxy collection install namespace5.name -s '{{ test_server }}' --api-version 2 {{ galaxy_verbosity }}
register: install_empty_server_list
environment:
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
@@ -571,7 +577,6 @@
- namespace8
- namespace9
-# SIVEL
- name: assert invalid signature is not fatal with ansible-galaxy install --ignore-errors - {{ test_id }}
assert:
that:
@@ -646,6 +651,7 @@
- namespace8
- namespace9
+# test --ignore-signature-status-code extends ANSIBLE_GALAXY_IGNORE_SIGNATURE_STATUS_CODES env var
- name: install collections with only one valid signature by ignoring the other errors
command: ansible-galaxy install -r {{ req_file }} {{ cli_opts }} {{ galaxy_verbosity }} --ignore-signature-status-code FAILURE
register: install_req
@@ -686,6 +692,60 @@
vars:
install_stderr: "{{ install_req.stderr | regex_replace('\\n', ' ') }}"
+# test --ignore-signature-status-code passed multiple times
+- name: reinstall collections with only one valid signature by ignoring the other errors
+ command: ansible-galaxy install -r {{ req_file }} {{ cli_opts }} {{ galaxy_verbosity }} {{ ignore_errors }}
+ register: install_req
+ vars:
+ req_file: "{{ galaxy_dir }}/ansible_collections/requirements.yaml"
+ cli_opts: "-s {{ test_name }} --keyring {{ keyring }} --force"
+ keyring: "{{ gpg_homedir }}/pubring.kbx"
+ ignore_errors: "--ignore-signature-status-code BADSIG --ignore-signature-status-code FAILURE"
+ environment:
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
+ ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: all
+ ANSIBLE_NOCOLOR: True
+ ANSIBLE_FORCE_COLOR: False
+
+- name: assert invalid signature is not fatal with ansible-galaxy install - {{ test_name }}
+ assert:
+ that:
+ - install_req is success
+ - '"Installing ''namespace7.name:1.0.0'' to" in install_req.stdout'
+ - '"Signature verification failed for ''namespace7.name'' (return code 1)" not in install_req.stdout'
+ - '"Not installing namespace7.name because GnuPG signature verification failed." not in install_stderr'
+ - '"Installing ''namespace8.name:1.0.0'' to" in install_req.stdout'
+ - '"Installing ''namespace9.name:1.0.0'' to" in install_req.stdout'
+ vars:
+ install_stderr: "{{ install_req.stderr | regex_replace('\\n', ' ') }}"
+
+# test --ignore-signature-status-code passed once with a list
+- name: reinstall collections with only one valid signature by ignoring the other errors
+ command: ansible-galaxy install -r {{ req_file }} {{ cli_opts }} {{ galaxy_verbosity }} {{ ignore_errors }}
+ register: install_req
+ vars:
+ req_file: "{{ galaxy_dir }}/ansible_collections/requirements.yaml"
+ cli_opts: "-s {{ test_name }} --keyring {{ keyring }} --force"
+ keyring: "{{ gpg_homedir }}/pubring.kbx"
+ ignore_errors: "--ignore-signature-status-codes BADSIG FAILURE"
+ environment:
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
+ ANSIBLE_GALAXY_REQUIRED_VALID_SIGNATURE_COUNT: all
+ ANSIBLE_NOCOLOR: True
+ ANSIBLE_FORCE_COLOR: False
+
+- name: assert invalid signature is not fatal with ansible-galaxy install - {{ test_name }}
+ assert:
+ that:
+ - install_req is success
+ - '"Installing ''namespace7.name:1.0.0'' to" in install_req.stdout'
+ - '"Signature verification failed for ''namespace7.name'' (return code 1)" not in install_req.stdout'
+ - '"Not installing namespace7.name because GnuPG signature verification failed." not in install_stderr'
+ - '"Installing ''namespace8.name:1.0.0'' to" in install_req.stdout'
+ - '"Installing ''namespace9.name:1.0.0'' to" in install_req.stdout'
+ vars:
+ install_stderr: "{{ install_req.stderr | regex_replace('\\n', ' ') }}"
+
- name: clean up collections from last test
file:
path: '{{ galaxy_dir }}/ansible_collections/{{ collection }}/name'
@@ -697,44 +757,45 @@
- namespace8
- namespace9
-# Uncomment once pulp container is at pulp>=0.5.0
-#- name: install cache.cache at the current latest version
-# command: ansible-galaxy collection install cache.cache -s '{{ test_name }}' -vvv
-# environment:
-# ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
-#
-#- set_fact:
-# cache_version_build: '{{ (cache_version_build | int) + 1 }}'
-#
-#- name: publish update for cache.cache test
-# setup_collections:
-# server: galaxy_ng
-# collections:
-# - namespace: cache
-# name: cache
-# version: 1.0.{{ cache_version_build }}
-#
-#- name: make sure the cache version list is ignored on a collection version change - {{ test_id }}
-# command: ansible-galaxy collection install cache.cache -s '{{ test_name }}' --force -vvv
-# register: install_cached_update
-# environment:
-# ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
-#
-#- name: get result of cache version list is ignored on a collection version change - {{ test_id }}
-# slurp:
-# path: '{{ galaxy_dir }}/ansible_collections/cache/cache/MANIFEST.json'
-# register: install_cached_update_actual
-#
-#- name: assert cache version list is ignored on a collection version change - {{ test_id }}
-# assert:
-# that:
-# - '"Installing ''cache.cache:1.0.{{ cache_version_build }}'' to" in install_cached_update.stdout'
-# - (install_cached_update_actual.content | b64decode | from_json).collection_info.version == '1.0.' ~ cache_version_build
+- when: not v2|default(false)
+ block:
+ - name: install cache.cache at the current latest version
+ command: ansible-galaxy collection install cache.cache -s '{{ test_name }}' -vvv
+ environment:
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
+
+ - set_fact:
+ cache_version_build: '{{ (cache_version_build | int) + 1 }}'
+
+ - name: publish update for cache.cache test
+ setup_collections:
+ server: galaxy_ng
+ collections:
+ - namespace: cache
+ name: cache
+ version: 1.0.{{ cache_version_build }}
+
+ - name: make sure the cache version list is ignored on a collection version change - {{ test_id }}
+ command: ansible-galaxy collection install cache.cache -s '{{ test_name }}' --force -vvv
+ register: install_cached_update
+ environment:
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
+
+ - name: get result of cache version list is ignored on a collection version change - {{ test_id }}
+ slurp:
+ path: '{{ galaxy_dir }}/ansible_collections/cache/cache/MANIFEST.json'
+ register: install_cached_update_actual
+
+ - name: assert cache version list is ignored on a collection version change - {{ test_id }}
+ assert:
+ that:
+ - '"Installing ''cache.cache:1.0.{{ cache_version_build }}'' to" in install_cached_update.stdout'
+ - (install_cached_update_actual.content | b64decode | from_json).collection_info.version == '1.0.' ~ cache_version_build
- name: install collection with symlink - {{ test_id }}
command: ansible-galaxy collection install symlink.symlink -s '{{ test_name }}' {{ galaxy_verbosity }}
environment:
- ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections'
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
register: install_symlink
- find:
@@ -772,6 +833,56 @@
- install_symlink_actual.results[5].stat.islnk
- install_symlink_actual.results[5].stat.lnk_target == '../REÅDMÈ.md'
+
+# Testing an install from source to check that symlinks to directories
+# are preserved (see issue https://github.com/ansible/ansible/issues/78442)
+- name: symlink_dirs collection install from source test
+ block:
+
+ - name: create symlink_dirs collection
+ command: ansible-galaxy collection init symlink_dirs.symlink_dirs --init-path "{{ galaxy_dir }}/scratch"
+
+ - name: create directory in collection
+ file:
+ path: "{{ galaxy_dir }}/scratch/symlink_dirs/symlink_dirs/folderA"
+ state: directory
+
+ - name: create symlink to folderA
+ file:
+ dest: "{{ galaxy_dir }}/scratch/symlink_dirs/symlink_dirs/folderB"
+ src: ./folderA
+ state: link
+ force: yes
+
+ - name: install symlink_dirs collection from source
+ command: ansible-galaxy collection install {{ galaxy_dir }}/scratch/symlink_dirs/symlink_dirs/
+ environment:
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
+ register: install_symlink_dirs
+
+ - name: get result of install collection with symlink_dirs - {{ test_id }}
+ stat:
+ path: '{{ galaxy_dir }}/ansible_collections/symlink_dirs/symlink_dirs/{{ path }}'
+ register: install_symlink_dirs_actual
+ loop_control:
+ loop_var: path
+ loop:
+ - folderA
+ - folderB
+
+ - name: assert install collection with symlink_dirs - {{ test_id }}
+ assert:
+ that:
+ - '"Installing ''symlink_dirs.symlink_dirs:1.0.0'' to" in install_symlink_dirs.stdout'
+ - install_symlink_dirs_actual.results[0].stat.isdir
+ - install_symlink_dirs_actual.results[1].stat.islnk
+ - install_symlink_dirs_actual.results[1].stat.lnk_target == './folderA'
+ always:
+ - name: clean up symlink_dirs collection directory
+ file:
+ path: "{{ galaxy_dir }}/scratch/symlink_dirs"
+ state: absent
+
- name: remove install directory for the next test because parent_dep.parent_collection was installed - {{ test_id }}
file:
path: '{{ galaxy_dir }}/ansible_collections'
@@ -780,7 +891,7 @@
- name: install collection and dep compatible with multiple requirements - {{ test_id }}
command: ansible-galaxy collection install parent_dep.parent_collection parent_dep2.parent_collection
environment:
- ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections'
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
register: install_req
- name: assert install collections with ansible-galaxy install - {{ test_id }}
@@ -802,7 +913,7 @@
- name: install a collection to the same installation directory - {{ test_id }}
command: ansible-galaxy collection install namespace1.name1
environment:
- ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections'
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
register: install_req
- name: assert installed collections with ansible-galaxy install - {{ test_id }}
@@ -1009,7 +1120,7 @@
args:
chdir: '{{ galaxy_dir }}/scratch'
environment:
- ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections'
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections'
register: install_concrete_pre
- name: get result of install collections with concrete pre-release dep - {{ test_id }}
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml b/test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml
index 74c9983..f3b9777 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml
@@ -25,6 +25,14 @@
regexp: "^dependencies:*"
line: "dependencies: {'ns.coll2': '>=1.0.0'}"
+ - name: create required runtime.yml
+ copy:
+ dest: "{{ galaxy_dir }}/offline/setup/ns/{{ item }}/meta/runtime.yml"
+ content: "requires_ansible: '>=1.0.0'"
+ loop:
+ - coll1
+ - coll2
+
- name: build both collections
command: ansible-galaxy collection build {{ init_dir }}/ns/{{ item }}
args:
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/list.yml b/test/integration/targets/ansible-galaxy-collection/tasks/list.yml
index b8d6349..1c93d54 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/list.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/list.yml
@@ -1,4 +1,4 @@
-- name: initialize collection structure
+- name: initialize dev collection structure
command: ansible-galaxy collection init {{ item }} --init-path "{{ galaxy_dir }}/dev/ansible_collections" {{ galaxy_verbosity }}
loop:
- 'dev.collection1'
@@ -8,6 +8,13 @@
- 'dev.collection5'
- 'dev.collection6'
+- name: initialize prod collection structure
+ command: ansible-galaxy collection init {{ item }} --init-path "{{ galaxy_dir }}/prod/ansible_collections" {{ galaxy_verbosity }}
+ loop:
+ - 'prod.collection1'
+ - 'prod.collection2'
+ - 'prod.collection3'
+
- name: replace the default version of the collections
lineinfile:
path: "{{ galaxy_dir }}/dev/ansible_collections/dev/{{ item.name }}/galaxy.yml"
@@ -53,13 +60,13 @@
- assert:
that:
- - "'dev.collection1 *' in list_result.stdout"
+ - "'dev.collection1 *' in list_result.stdout"
# Note the version displayed is the 'placeholder' string rather than "*" since it is not falsey
- - "'dev.collection2 placeholder' in list_result.stdout"
- - "'dev.collection3 *' in list_result.stdout"
- - "'dev.collection4 *' in list_result.stdout"
- - "'dev.collection5 *' in list_result.stdout"
- - "'dev.collection6 *' in list_result.stdout"
+ - "'dev.collection2 placeholder' in list_result.stdout"
+ - "'dev.collection3 *' in list_result.stdout"
+ - "'dev.collection4 *' in list_result.stdout"
+ - "'dev.collection5 *' in list_result.stdout"
+ - "'dev.collection6 *' in list_result.stdout"
- name: list collections in human format
command: ansible-galaxy collection list --format human
@@ -69,12 +76,12 @@
- assert:
that:
- - "'dev.collection1 *' in list_result_human.stdout"
+ - "'dev.collection1 *' in list_result_human.stdout"
# Note the version displayed is the 'placeholder' string rather than "*" since it is not falsey
- - "'dev.collection2 placeholder' in list_result_human.stdout"
- - "'dev.collection3 *' in list_result_human.stdout"
- - "'dev.collection5 *' in list_result.stdout"
- - "'dev.collection6 *' in list_result.stdout"
+ - "'dev.collection2 placeholder' in list_result_human.stdout"
+ - "'dev.collection3 *' in list_result_human.stdout"
+ - "'dev.collection5 *' in list_result.stdout"
+ - "'dev.collection6 *' in list_result.stdout"
- name: list collections in yaml format
command: ansible-galaxy collection list --format yaml
@@ -84,6 +91,12 @@
- assert:
that:
+ - yaml_result[galaxy_dir ~ '/dev/ansible_collections'] != yaml_result[galaxy_dir ~ '/prod/ansible_collections']
+ vars:
+ yaml_result: '{{ list_result_yaml.stdout | from_yaml }}'
+
+- assert:
+ that:
- "item.value | length == 6"
- "item.value['dev.collection1'].version == '*'"
- "item.value['dev.collection2'].version == 'placeholder'"
@@ -91,6 +104,7 @@
- "item.value['dev.collection5'].version == '*'"
- "item.value['dev.collection6'].version == '*'"
with_dict: "{{ list_result_yaml.stdout | from_yaml }}"
+ when: "'dev' in item.key"
- name: list collections in json format
command: ansible-galaxy collection list --format json
@@ -107,6 +121,7 @@
- "item.value['dev.collection5'].version == '*'"
- "item.value['dev.collection6'].version == '*'"
with_dict: "{{ list_result_json.stdout | from_json }}"
+ when: "'dev' in item.key"
- name: list single collection in json format
command: "ansible-galaxy collection list {{ item.key }} --format json"
@@ -137,7 +152,7 @@
register: list_result_error
ignore_errors: True
environment:
- ANSIBLE_COLLECTIONS_PATH: ""
+ ANSIBLE_COLLECTIONS_PATH: "i_dont_exist"
- assert:
that:
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/main.yml b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml
index 724c861..e17d6aa 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/main.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml
@@ -72,13 +72,12 @@
vars:
test_name: '{{ item.name }}'
test_server: '{{ item.server }}'
- vX: '{{ "v3/" if item.v3|default(false) else "v2/" }}'
+ test_api_server: '{{ item.api_server|default(item.server) }}'
loop:
- name: pulp_v2
- server: '{{ pulp_server }}published/api/'
- - name: pulp_v3
- server: '{{ pulp_server }}published/api/'
- v3: true
+ api_server: '{{ galaxy_ng_server }}'
+ server: '{{ pulp_server }}primary/api/'
+ v2: true
- name: galaxy_ng
server: '{{ galaxy_ng_server }}'
v3: true
@@ -108,8 +107,9 @@
test_id: '{{ item.name }}'
test_name: '{{ item.name }}'
test_server: '{{ item.server }}'
- vX: '{{ "v3/" if item.v3|default(false) else "v2/" }}'
+ test_api_server: '{{ item.api_server|default(item.server) }}'
requires_auth: '{{ item.requires_auth|default(false) }}'
+ v2: '{{ item.v2|default(false) }}'
args:
apply:
environment:
@@ -120,10 +120,9 @@
v3: true
requires_auth: true
- name: pulp_v2
- server: '{{ pulp_server }}published/api/'
- - name: pulp_v3
- server: '{{ pulp_server }}published/api/'
- v3: true
+ server: '{{ pulp_server }}primary/api/'
+ api_server: '{{ galaxy_ng_server }}'
+ v2: true
- name: test installing and downloading collections with the range of supported resolvelib versions
include_tasks: supported_resolvelib.yml
@@ -135,6 +134,17 @@
loop_control:
loop_var: resolvelib_version
+- name: test choosing pinned pre-releases anywhere in the dependency tree
+ # This is a regression test for the case when the end-user does not
+ # explicitly allow installing pre-release collection versions, but their
+ # precise pins are still selected if met among the dependencies, either
+ # direct or transitive.
+ include_tasks: pinned_pre_releases_in_deptree.yml
+
+- name: test installing prereleases via scm direct requests
+ # In this test suite because the bug relies on the dep having versions on a Galaxy server
+ include_tasks: virtual_direct_requests.yml
+
- name: publish collection with a dep on another server
setup_collections:
server: secondary
@@ -176,13 +186,13 @@
in install_cross_dep.stdout
# pulp_v2 is highest in the list so it will find it there first
- >-
- "'parent_dep.parent_collection:1.0.0' obtained from server pulp_v2"
+ "'parent_dep.parent_collection:1.0.0' obtained from server galaxy_ng"
in install_cross_dep.stdout
- >-
- "'child_dep.child_collection:0.9.9' obtained from server pulp_v2"
+ "'child_dep.child_collection:0.9.9' obtained from server galaxy_ng"
in install_cross_dep.stdout
- >-
- "'child_dep.child_dep2:1.2.2' obtained from server pulp_v2"
+ "'child_dep.child_dep2:1.2.2' obtained from server galaxy_ng"
in install_cross_dep.stdout
- (install_cross_dep_actual.results[0].content | b64decode | from_json).collection_info.version == '1.0.0'
- (install_cross_dep_actual.results[1].content | b64decode | from_json).collection_info.version == '1.0.0'
@@ -204,10 +214,9 @@
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg'
vars:
- test_api_fallback: 'pulp_v2'
- test_api_fallback_versions: 'v1, v2'
- test_name: 'galaxy_ng'
- test_server: '{{ galaxy_ng_server }}'
+ test_api_fallback: 'galaxy_ng'
+ test_api_fallback_versions: 'v3, pulp-v3, v1'
+ test_name: 'pulp_v2'
- name: run ansible-galaxy collection list tests
include_tasks: list.yml
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/pinned_pre_releases_in_deptree.yml b/test/integration/targets/ansible-galaxy-collection/tasks/pinned_pre_releases_in_deptree.yml
new file mode 100644
index 0000000..3745fa3
--- /dev/null
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/pinned_pre_releases_in_deptree.yml
@@ -0,0 +1,79 @@
+---
+
+- name: >-
+ test that the dependency resolver chooses pre-releases if they are pinned
+ environment:
+ ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
+ ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg'
+ block:
+ - name: reset installation directory
+ file:
+ state: "{{ item }}"
+ path: "{{ galaxy_dir }}/ansible_collections"
+ loop:
+ - absent
+ - directory
+
+ - name: >-
+ install collections with pre-release versions in the dependency tree
+ command: >-
+ ansible-galaxy collection install
+ meta_ns_with_transitive_wildcard_dep.meta_name_with_transitive_wildcard_dep
+ rc_meta_ns_with_transitive_dev_dep.rc_meta_name_with_transitive_dev_dep:=2.4.5-rc5
+ {{ galaxy_verbosity }}
+ register: prioritize_direct_req
+ - assert:
+ that:
+ - >-
+ "rc_meta_ns_with_transitive_dev_dep.rc_meta_name_with_transitive_dev_dep:2.4.5-rc5 was installed successfully"
+ in prioritize_direct_req.stdout
+ - >-
+ "meta_ns_with_transitive_wildcard_dep.meta_name_with_transitive_wildcard_dep:4.5.6 was installed successfully"
+ in prioritize_direct_req.stdout
+ - >-
+ "ns_with_dev_dep.name_with_dev_dep:6.7.8 was installed successfully"
+ in prioritize_direct_req.stdout
+ - >-
+ "ns_with_wildcard_dep.name_with_wildcard_dep:5.6.7-beta.3 was installed successfully"
+ in prioritize_direct_req.stdout
+ - >-
+ "dev_and_stables_ns.dev_and_stables_name:1.2.3-dev0 was installed successfully"
+ in prioritize_direct_req.stdout
+
+ - name: cleanup
+ file:
+ state: "{{ item }}"
+ path: "{{ galaxy_dir }}/ansible_collections"
+ loop:
+ - absent
+ - directory
+
+ - name: >-
+ install collection that only has pre-release versions published
+ to the index
+ command: >-
+ ansible-galaxy collection install
+ rc_meta_ns_with_transitive_dev_dep.rc_meta_name_with_transitive_dev_dep:*
+ {{ galaxy_verbosity }}
+ register: select_pre_release_if_no_stable
+ - assert:
+ that:
+ - >-
+ "rc_meta_ns_with_transitive_dev_dep.rc_meta_name_with_transitive_dev_dep:2.4.5-rc5 was installed successfully"
+ in select_pre_release_if_no_stable.stdout
+ - >-
+ "ns_with_dev_dep.name_with_dev_dep:6.7.8 was installed successfully"
+ in select_pre_release_if_no_stable.stdout
+ - >-
+ "dev_and_stables_ns.dev_and_stables_name:1.2.3-dev0 was installed successfully"
+ in select_pre_release_if_no_stable.stdout
+ always:
+ - name: cleanup
+ file:
+ state: "{{ item }}"
+ path: "{{ galaxy_dir }}/ansible_collections"
+ loop:
+ - absent
+ - directory
+
+...
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml b/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml
index 241eae6..1be16ae 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml
@@ -5,9 +5,12 @@
chdir: '{{ galaxy_dir }}'
register: publish_collection
+- name: ensure we can download the published collection - {{ test_name }}
+ command: ansible-galaxy collection install -s {{ test_name }} -p "{{ remote_tmp_dir }}/publish/{{ test_name }}" ansible_test.my_collection==1.0.0 {{ galaxy_verbosity }}
+
- name: get result of publish collection - {{ test_name }}
uri:
- url: '{{ test_server }}{{ vX }}collections/ansible_test/my_collection/versions/1.0.0/'
+ url: '{{ test_api_server }}v3/plugin/ansible/content/primary/collections/index/ansible_test/my_collection/versions/1.0.0/'
return_content: yes
user: '{{ pulp_user }}'
password: '{{ pulp_password }}'
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/supported_resolvelib.yml b/test/integration/targets/ansible-galaxy-collection/tasks/supported_resolvelib.yml
index 763c5a1..bff3689 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/supported_resolvelib.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/supported_resolvelib.yml
@@ -20,11 +20,11 @@
- include_tasks: install.yml
vars:
- test_name: pulp_v3
+ test_name: galaxy_ng
test_id: '{{ test_name }} (resolvelib {{ resolvelib_version }})'
- test_server: '{{ pulp_server }}published/api/'
- vX: "v3/"
- requires_auth: false
+ test_server: '{{ galaxy_ng_server }}'
+ test_api_server: '{{ galaxy_ng_server }}'
+ requires_auth: true
args:
apply:
environment:
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml b/test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml
index 893ea80..debd70b 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml
@@ -142,7 +142,7 @@
- directory
- name: install a collection
- command: ansible-galaxy collection install namespace1.name1:0.0.1 {{ galaxy_verbosity }}
+ command: ansible-galaxy collection install namespace1.name1==0.0.1 {{ galaxy_verbosity }}
register: result
failed_when:
- '"namespace1.name1:0.0.1 was installed successfully" not in result.stdout_lines'
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/verify.yml b/test/integration/targets/ansible-galaxy-collection/tasks/verify.yml
index dfe3d0f..0fe2f82 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/verify.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/verify.yml
@@ -3,6 +3,11 @@
args:
chdir: '{{ galaxy_dir }}/scratch'
+- name: created required runtime.yml
+ copy:
+ content: 'requires_ansible: ">=1.0.0"'
+ dest: '{{ galaxy_dir }}/scratch/ansible_test/verify/meta/runtime.yml'
+
- name: build the collection
command: ansible-galaxy collection build scratch/ansible_test/verify
args:
@@ -31,6 +36,9 @@
- name: verify the collection against the first valid server
command: ansible-galaxy collection verify ansible_test.verify:1.0.0 -vvvv {{ galaxy_verbosity }}
register: verify
+ vars:
+ # This sets a specific precedence that the tests are expecting
+ ANSIBLE_GALAXY_SERVER_LIST: offline,secondary,pulp_v2,galaxy_ng
- assert:
that:
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/virtual_direct_requests.yml b/test/integration/targets/ansible-galaxy-collection/tasks/virtual_direct_requests.yml
new file mode 100644
index 0000000..7b1931f
--- /dev/null
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/virtual_direct_requests.yml
@@ -0,0 +1,77 @@
+- environment:
+ ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg'
+ vars:
+ scm_path: "{{ galaxy_dir }}/scms"
+ metadata:
+ collection1: |-
+ name: collection1
+ version: "1.0.0"
+ dependencies:
+ test_prereleases.collection2: '*'
+ collection2: |
+ name: collection2
+ version: "1.0.0-dev0"
+ dependencies: {}
+ namespace_boilerplate: |-
+ namespace: test_prereleases
+ readme: README.md
+ authors:
+ - "ansible-core"
+ description: test prerelease priority with virtual collections
+ license:
+ - GPL-2.0-or-later
+ license_file: ''
+ tags: []
+ repository: https://github.com/ansible/ansible
+ documentation: https://github.com/ansible/ansible
+ homepage: https://github.com/ansible/ansible
+ issues: https://github.com/ansible/ansible
+ build_ignore: []
+ block:
+ - name: Initialize git repository
+ command: 'git init {{ scm_path }}/test_prereleases'
+
+ - name: Configure commiter for the repo
+ shell: git config user.email ansible-test@ansible.com && git config user.name ansible-test
+ args:
+ chdir: "{{ scm_path }}/test_prereleases"
+
+ - name: Add collections to the repo
+ file:
+ path: "{{ scm_path }}/test_prereleases/{{ item }}"
+ state: directory
+ loop:
+ - collection1
+ - collection2
+
+ - name: Add collection metadata
+ copy:
+ dest: "{{ scm_path }}/test_prereleases/{{ item }}/galaxy.yml"
+ content: "{{ metadata[item] + '\n' + metadata['namespace_boilerplate'] }}"
+ loop:
+ - collection1
+ - collection2
+
+ - name: Save the changes
+ shell: git add . && git commit -m "Add collections to test installing a git repo directly takes priority over indirect Galaxy dep"
+ args:
+ chdir: '{{ scm_path }}/test_prereleases'
+
+ - name: Validate the dependency also exists on Galaxy before test
+ command: "ansible-galaxy collection install test_prereleases.collection2"
+ register: prereq
+ failed_when: '"test_prereleases.collection2:1.0.0 was installed successfully" not in prereq.stdout'
+
+ - name: Install collections from source
+ command: "ansible-galaxy collection install git+file://{{ scm_path }}/test_prereleases"
+ register: prioritize_direct_req
+
+ - assert:
+ that:
+ - '"test_prereleases.collection2:1.0.0-dev0 was installed successfully" in prioritize_direct_req.stdout'
+
+ always:
+ - name: Clean up test repos
+ file:
+ path: "{{ scm_path }}"
+ state: absent
diff --git a/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2 b/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2
index 9bff527..a242979 100644
--- a/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2
+++ b/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2
@@ -1,28 +1,22 @@
[galaxy]
# Ensures subsequent unstable reruns don't use the cached information causing another failure
cache_dir={{ remote_tmp_dir }}/galaxy_cache
-server_list=offline,pulp_v2,pulp_v3,galaxy_ng,secondary
+server_list=offline,galaxy_ng,secondary,pulp_v2
[galaxy_server.offline]
url={{ offline_server }}
[galaxy_server.pulp_v2]
-url={{ pulp_server }}published/api/
-username={{ pulp_user }}
-password={{ pulp_password }}
-
-[galaxy_server.pulp_v3]
-url={{ pulp_server }}published/api/
-v3=true
+url={{ pulp_server }}primary/api/
username={{ pulp_user }}
password={{ pulp_password }}
+api_version=2
[galaxy_server.galaxy_ng]
-url={{ galaxy_ng_server }}
+url={{ galaxy_ng_server }}content/primary/
token={{ galaxy_ng_token.json.token }}
[galaxy_server.secondary]
-url={{ pulp_server }}secondary/api/
-v3=true
+url={{ galaxy_ng_server }}content/secondary/
username={{ pulp_user }}
password={{ pulp_password }}
diff --git a/test/integration/targets/ansible-galaxy-collection/vars/main.yml b/test/integration/targets/ansible-galaxy-collection/vars/main.yml
index 175d669..066d267 100644
--- a/test/integration/targets/ansible-galaxy-collection/vars/main.yml
+++ b/test/integration/targets/ansible-galaxy-collection/vars/main.yml
@@ -9,17 +9,20 @@ supported_resolvelib_versions:
- "0.6.0"
- "0.7.0"
- "0.8.0"
+ - "0.9.0"
+ - "1.0.1"
unsupported_resolvelib_versions:
- "0.2.0" # Fails on import
- "0.5.1"
pulp_repositories:
- - published
+ - primary
- secondary
publish_namespaces:
- ansible_test
+ - secondary
collection_list:
# Scenario to test out pre-release being ignored unless explicitly set and version pagination.
@@ -162,3 +165,41 @@ collection_list:
name: parent
dependencies:
namespace1.name1: '*'
+
+ # non-prerelease is published to test that installing
+ # the pre-release from SCM doesn't accidentally prefer indirect
+ # dependencies from Galaxy
+ - namespace: test_prereleases
+ name: collection2
+ version: 1.0.0
+
+ - namespace: dev_and_stables_ns
+ name: dev_and_stables_name
+ version: 1.2.3-dev0
+ - namespace: dev_and_stables_ns
+ name: dev_and_stables_name
+ version: 1.2.4
+
+ - namespace: ns_with_wildcard_dep
+ name: name_with_wildcard_dep
+ version: 5.6.7-beta.3
+ dependencies:
+ dev_and_stables_ns.dev_and_stables_name: >-
+ *
+ - namespace: ns_with_dev_dep
+ name: name_with_dev_dep
+ version: 6.7.8
+ dependencies:
+ dev_and_stables_ns.dev_and_stables_name: 1.2.3-dev0
+
+ - namespace: rc_meta_ns_with_transitive_dev_dep
+ name: rc_meta_name_with_transitive_dev_dep
+ version: 2.4.5-rc5
+ dependencies:
+ ns_with_dev_dep.name_with_dev_dep: >-
+ *
+ - namespace: meta_ns_with_transitive_wildcard_dep
+ name: meta_name_with_transitive_wildcard_dep
+ version: 4.5.6
+ dependencies:
+ ns_with_wildcard_dep.name_with_wildcard_dep: 5.6.7-beta.3
diff --git a/test/integration/targets/ansible-galaxy-role/files/create-role-archive.py b/test/integration/targets/ansible-galaxy-role/files/create-role-archive.py
index cfd908c..4876663 100755
--- a/test/integration/targets/ansible-galaxy-role/files/create-role-archive.py
+++ b/test/integration/targets/ansible-galaxy-role/files/create-role-archive.py
@@ -2,6 +2,7 @@
"""Create a role archive which overwrites an arbitrary file."""
import argparse
+import os
import pathlib
import tarfile
import tempfile
@@ -18,6 +19,15 @@ def main() -> None:
create_archive(args.archive, args.content, args.target)
+def generate_files_from_path(path):
+ if os.path.isdir(path):
+ for subpath in os.listdir(path):
+ _path = os.path.join(path, subpath)
+ yield from generate_files_from_path(_path)
+ elif os.path.isfile(path):
+ yield pathlib.Path(path)
+
+
def create_archive(archive_path: pathlib.Path, content_path: pathlib.Path, target_path: pathlib.Path) -> None:
with (
tarfile.open(name=archive_path, mode='w') as role_archive,
@@ -35,10 +45,15 @@ def create_archive(archive_path: pathlib.Path, content_path: pathlib.Path, targe
role_archive.add(meta_main_path)
role_archive.add(symlink_path)
- content_tarinfo = role_archive.gettarinfo(content_path, str(symlink_path))
+ for path in generate_files_from_path(content_path):
+ if path == content_path:
+ arcname = str(symlink_path)
+ else:
+ arcname = os.path.join(temp_dir_path, path)
- with content_path.open('rb') as content_file:
- role_archive.addfile(content_tarinfo, content_file)
+ content_tarinfo = role_archive.gettarinfo(path, arcname)
+ with path.open('rb') as file_content:
+ role_archive.addfile(content_tarinfo, file_content)
if __name__ == '__main__':
diff --git a/test/integration/targets/ansible-galaxy-role/tasks/dir-traversal.yml b/test/integration/targets/ansible-galaxy-role/tasks/dir-traversal.yml
index c70e899..1c17daf 100644
--- a/test/integration/targets/ansible-galaxy-role/tasks/dir-traversal.yml
+++ b/test/integration/targets/ansible-galaxy-role/tasks/dir-traversal.yml
@@ -23,6 +23,9 @@
command:
cmd: ansible-galaxy role install --roles-path '{{ remote_tmp_dir }}/dir-traversal/roles' dangerous.tar
chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ environment:
+ ANSIBLE_NOCOLOR: True
+ ANSIBLE_FORCE_COLOR: False
ignore_errors: true
register: galaxy_install_dangerous
@@ -42,3 +45,86 @@
- dangerous_overwrite_content.content|default('')|b64decode == ''
- not dangerous_overwrite_stat.stat.exists
- galaxy_install_dangerous is failed
+ - "'is not a subpath of the role' in (galaxy_install_dangerous.stderr | regex_replace('\n', ' '))"
+
+- name: remove tarfile for next test
+ file:
+ path: '{{ item }}'
+ state: absent
+ loop:
+ - '{{ remote_tmp_dir }}/dir-traversal/source/dangerous.tar'
+ - '{{ remote_tmp_dir }}/dir-traversal/roles/dangerous.tar'
+
+- name: build dangerous dir traversal role that includes .. in the symlink path
+ script:
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ cmd: create-role-archive.py dangerous.tar content.txt {{ remote_tmp_dir }}/dir-traversal/source/../target/target-file-to-overwrite.txt
+ executable: '{{ ansible_playbook_python }}'
+
+- name: install dangerous role
+ command:
+ cmd: 'ansible-galaxy role install --roles-path {{ remote_tmp_dir }}/dir-traversal/roles dangerous.tar'
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ environment:
+ ANSIBLE_NOCOLOR: True
+ ANSIBLE_FORCE_COLOR: False
+ ignore_errors: true
+ register: galaxy_install_dangerous
+
+- name: check for overwritten file
+ stat:
+ path: '{{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt'
+ register: dangerous_overwrite_stat
+
+- name: get overwritten content
+ slurp:
+ path: '{{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt'
+ register: dangerous_overwrite_content
+ when: dangerous_overwrite_stat.stat.exists
+
+- assert:
+ that:
+ - dangerous_overwrite_content.content|default('')|b64decode == ''
+ - not dangerous_overwrite_stat.stat.exists
+ - galaxy_install_dangerous is failed
+ - "'is not a subpath of the role' in (galaxy_install_dangerous.stderr | regex_replace('\n', ' '))"
+
+- name: remove tarfile for next test
+ file:
+ path: '{{ remote_tmp_dir }}/dir-traversal/source/dangerous.tar'
+ state: absent
+
+- name: build dangerous dir traversal role that includes .. in the relative symlink path
+ script:
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ cmd: create-role-archive.py dangerous_rel.tar content.txt ../context.txt
+
+- name: install dangerous role with relative symlink
+ command:
+ cmd: 'ansible-galaxy role install --roles-path {{ remote_tmp_dir }}/dir-traversal/roles dangerous_rel.tar'
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ environment:
+ ANSIBLE_NOCOLOR: True
+ ANSIBLE_FORCE_COLOR: False
+ ignore_errors: true
+ register: galaxy_install_dangerous
+
+- name: check for symlink outside role
+ stat:
+ path: "{{ remote_tmp_dir | realpath }}/dir-traversal/roles/symlink"
+ register: symlink_outside_role
+
+- assert:
+ that:
+ - not symlink_outside_role.stat.exists
+ - galaxy_install_dangerous is failed
+ - "'is not a subpath of the role' in (galaxy_install_dangerous.stderr | regex_replace('\n', ' '))"
+
+- name: remove test directories
+ file:
+ path: '{{ remote_tmp_dir }}/dir-traversal/{{ item }}'
+ state: absent
+ loop:
+ - source
+ - target
+ - roles
diff --git a/test/integration/targets/ansible-galaxy-role/tasks/main.yml b/test/integration/targets/ansible-galaxy-role/tasks/main.yml
index b39df11..5f88a55 100644
--- a/test/integration/targets/ansible-galaxy-role/tasks/main.yml
+++ b/test/integration/targets/ansible-galaxy-role/tasks/main.yml
@@ -25,10 +25,18 @@
- name: Valid role archive
command: "tar cf {{ remote_tmp_dir }}/valid-role.tar {{ remote_tmp_dir }}/role.d"
-- name: Invalid file
- copy:
- content: ""
+- name: Add invalid symlink
+ file:
+ state: link
+ src: "~/invalid"
dest: "{{ remote_tmp_dir }}/role.d/tasks/~invalid.yml"
+ force: yes
+
+- name: Add another invalid symlink
+ file:
+ state: link
+ src: "/"
+ dest: "{{ remote_tmp_dir }}/role.d/tasks/invalid$name.yml"
- name: Valid requirements file
copy:
@@ -61,3 +69,4 @@
command: ansible-galaxy role remove invalid-testrole
- import_tasks: dir-traversal.yml
+- import_tasks: valid-role-symlinks.yml
diff --git a/test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml b/test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml
new file mode 100644
index 0000000..8a60b2e
--- /dev/null
+++ b/test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml
@@ -0,0 +1,78 @@
+- name: create test directories
+ file:
+ path: '{{ remote_tmp_dir }}/dir-traversal/{{ item }}'
+ state: directory
+ loop:
+ - source
+ - target
+ - roles
+
+- name: create subdir in the role content to test relative symlinks
+ file:
+ dest: '{{ remote_tmp_dir }}/dir-traversal/source/role_subdir'
+ state: directory
+
+- copy:
+ dest: '{{ remote_tmp_dir }}/dir-traversal/source/role_subdir/.keep'
+ content: ''
+
+- set_fact:
+ installed_roles: "{{ remote_tmp_dir | realpath }}/dir-traversal/roles"
+
+- name: build role with symlink to a directory in the role
+ script:
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ cmd: create-role-archive.py safe-link-dir.tar ./ role_subdir/..
+ executable: '{{ ansible_playbook_python }}'
+
+- name: install role successfully
+ command:
+ cmd: 'ansible-galaxy role install --roles-path {{ remote_tmp_dir }}/dir-traversal/roles safe-link-dir.tar'
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ register: galaxy_install_ok
+
+- name: check for the directory symlink in the role
+ stat:
+ path: "{{ installed_roles }}/safe-link-dir.tar/symlink"
+ register: symlink_in_role
+
+- assert:
+ that:
+ - symlink_in_role.stat.exists
+ - symlink_in_role.stat.lnk_source == installed_roles + '/safe-link-dir.tar'
+
+- name: remove tarfile for next test
+ file:
+ path: '{{ remote_tmp_dir }}/dir-traversal/source/safe-link-dir.tar'
+ state: absent
+
+- name: build role with safe relative symlink
+ script:
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ cmd: create-role-archive.py safe.tar ./ role_subdir/../context.txt
+ executable: '{{ ansible_playbook_python }}'
+
+- name: install role successfully
+ command:
+ cmd: 'ansible-galaxy role install --roles-path {{ remote_tmp_dir }}/dir-traversal/roles safe.tar'
+ chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
+ register: galaxy_install_ok
+
+- name: check for symlink in role
+ stat:
+ path: "{{ installed_roles }}/safe.tar/symlink"
+ register: symlink_in_role
+
+- assert:
+ that:
+ - symlink_in_role.stat.exists
+ - symlink_in_role.stat.lnk_source == installed_roles + '/safe.tar/context.txt'
+
+- name: remove test directories
+ file:
+ path: '{{ remote_tmp_dir }}/dir-traversal/{{ item }}'
+ state: absent
+ loop:
+ - source
+ - target
+ - roles
diff --git a/test/integration/targets/ansible-galaxy/files/testserver.py b/test/integration/targets/ansible-galaxy/files/testserver.py
index 1359850..8cca6a8 100644
--- a/test/integration/targets/ansible-galaxy/files/testserver.py
+++ b/test/integration/targets/ansible-galaxy/files/testserver.py
@@ -1,20 +1,15 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import sys
+import http.server
+import socketserver
import ssl
if __name__ == '__main__':
- if sys.version_info[0] >= 3:
- import http.server
- import socketserver
- Handler = http.server.SimpleHTTPRequestHandler
- httpd = socketserver.TCPServer(("", 4443), Handler)
- else:
- import BaseHTTPServer
- import SimpleHTTPServer
- Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
- httpd = BaseHTTPServer.HTTPServer(("", 4443), Handler)
+ Handler = http.server.SimpleHTTPRequestHandler
+ context = ssl.SSLContext()
+ context.load_cert_chain(certfile='./cert.pem', keyfile='./key.pem')
+ httpd = socketserver.TCPServer(("", 4443), Handler)
+ httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
- httpd.socket = ssl.wrap_socket(httpd.socket, certfile='./cert.pem', keyfile='./key.pem', server_side=True)
httpd.serve_forever()
diff --git a/test/integration/targets/ansible-galaxy/runme.sh b/test/integration/targets/ansible-galaxy/runme.sh
index 7d966e2..fcd826c 100755
--- a/test/integration/targets/ansible-galaxy/runme.sh
+++ b/test/integration/targets/ansible-galaxy/runme.sh
@@ -61,10 +61,13 @@ f_ansible_galaxy_create_role_repo_post()
git add .
git commit -m "local testing ansible galaxy role"
+ # NOTE: `HEAD` is used because the newer Git versions create
+ # NOTE: `main` by default and the older ones differ. We
+ # NOTE: want to avoid hardcoding them.
git archive \
--format=tar \
--prefix="${repo_name}/" \
- master > "${repo_tar}"
+ HEAD > "${repo_tar}"
# Configure basic (insecure) HTTPS-accessible repository
galaxy_local_test_role_http_repo="${galaxy_webserver_root}/${galaxy_local_test_role}.git"
if [[ ! -d "${galaxy_local_test_role_http_repo}" ]]; then
@@ -354,7 +357,7 @@ pushd "${galaxy_testdir}"
popd # ${galaxy_testdir}
f_ansible_galaxy_status \
- "role info non-existant role"
+ "role info non-existent role"
mkdir -p "${role_testdir}"
pushd "${role_testdir}"
diff --git a/test/integration/targets/ansible-inventory/files/complex.ini b/test/integration/targets/ansible-inventory/files/complex.ini
new file mode 100644
index 0000000..227d9ea
--- /dev/null
+++ b/test/integration/targets/ansible-inventory/files/complex.ini
@@ -0,0 +1,35 @@
+ihavenogroup
+
+[all]
+hostinall
+
+[all:vars]
+ansible_connection=local
+
+[test_group1]
+test1 myvar=something
+test2 myvar=something2
+test3
+
+[test_group2]
+test1
+test4
+test5
+
+[test_group3]
+test2 othervar=stuff
+test3
+test6
+
+[parent_1:children]
+test_group1
+
+[parent_2:children]
+test_group1
+
+[parent_3:children]
+test_group2
+test_group3
+
+[parent_3]
+test2
diff --git a/test/integration/targets/ansible-inventory/files/valid_sample.yml b/test/integration/targets/ansible-inventory/files/valid_sample.yml
index 477f82f..b8e7b88 100644
--- a/test/integration/targets/ansible-inventory/files/valid_sample.yml
+++ b/test/integration/targets/ansible-inventory/files/valid_sample.yml
@@ -4,4 +4,4 @@ all:
hosts:
something:
foo: bar
- ungrouped: {} \ No newline at end of file
+ ungrouped: {}
diff --git a/test/integration/targets/ansible-inventory/filter_plugins/toml.py b/test/integration/targets/ansible-inventory/filter_plugins/toml.py
new file mode 100644
index 0000000..997173c
--- /dev/null
+++ b/test/integration/targets/ansible-inventory/filter_plugins/toml.py
@@ -0,0 +1,50 @@
+# (c) 2017, Matt Martz <matt@sivel.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import functools
+
+from ansible.plugins.inventory.toml import HAS_TOML, toml_dumps
+try:
+ from ansible.plugins.inventory.toml import toml
+except ImportError:
+ pass
+
+from ansible.errors import AnsibleFilterError
+from ansible.module_utils.common.text.converters import to_text
+from ansible.module_utils.common._collections_compat import MutableMapping
+from ansible.module_utils.six import string_types
+
+
+def _check_toml(func):
+ @functools.wraps(func)
+ def inner(o):
+ if not HAS_TOML:
+ raise AnsibleFilterError('The %s filter plugin requires the python "toml" library' % func.__name__)
+ return func(o)
+ return inner
+
+
+@_check_toml
+def from_toml(o):
+ if not isinstance(o, string_types):
+ raise AnsibleFilterError('from_toml requires a string, got %s' % type(o))
+ return toml.loads(to_text(o, errors='surrogate_or_strict'))
+
+
+@_check_toml
+def to_toml(o):
+ if not isinstance(o, MutableMapping):
+ raise AnsibleFilterError('to_toml requires a dict, got %s' % type(o))
+ return to_text(toml_dumps(o), errors='surrogate_or_strict')
+
+
+class FilterModule(object):
+ def filters(self):
+ return {
+ 'to_toml': to_toml,
+ 'from_toml': from_toml
+ }
diff --git a/test/integration/targets/ansible-inventory/tasks/json_output.yml b/test/integration/targets/ansible-inventory/tasks/json_output.yml
new file mode 100644
index 0000000..2652061
--- /dev/null
+++ b/test/integration/targets/ansible-inventory/tasks/json_output.yml
@@ -0,0 +1,33 @@
+- block:
+ - name: check baseline
+ command: ansible-inventory -i '{{ role_path }}/files/valid_sample.yml' --list
+ register: limited
+
+ - name: ensure non empty host list
+ assert:
+ that:
+ - "'something' in inv['_meta']['hostvars']"
+
+ - name: check that limit removes host
+ command: ansible-inventory -i '{{ role_path }}/files/valid_sample.yml' --limit '!something' --list
+ register: limited
+
+ - name: ensure empty host list
+ assert:
+ that:
+ - "'something' not in inv['_meta']['hostvars']"
+
+ - name: check dupes
+ command: ansible-inventory -i '{{ role_path }}/files/complex.ini' --list
+ register: limited
+
+ - name: ensure host only appears on directly assigned
+ assert:
+ that:
+ - "'hosts' not in inv['parent_1']"
+ - "'hosts' not in inv['parent_2']"
+ - "'hosts' in inv['parent_3']"
+ - "'test1' in inv['test_group1']['hosts']"
+ vars:
+ inv: '{{limited.stdout|from_json }}'
+ delegate_to: localhost
diff --git a/test/integration/targets/ansible-inventory/tasks/main.yml b/test/integration/targets/ansible-inventory/tasks/main.yml
index 84ac2c3..c3459c1 100644
--- a/test/integration/targets/ansible-inventory/tasks/main.yml
+++ b/test/integration/targets/ansible-inventory/tasks/main.yml
@@ -145,3 +145,10 @@
loop_control:
loop_var: toml_package
when: toml_package is not contains 'tomllib' or (toml_package is contains 'tomllib' and ansible_facts.python.version_info >= [3, 11])
+
+
+- include_tasks: "{{item}}_output.yml"
+ loop:
+ - json
+ - yaml
+ - toml
diff --git a/test/integration/targets/ansible-inventory/tasks/toml_output.yml b/test/integration/targets/ansible-inventory/tasks/toml_output.yml
new file mode 100644
index 0000000..1e5df9a
--- /dev/null
+++ b/test/integration/targets/ansible-inventory/tasks/toml_output.yml
@@ -0,0 +1,43 @@
+- name: only test if have toml in python
+ command: "{{ansible_playbook_python}} -c 'import toml'"
+ ignore_errors: true
+ delegate_to: localhost
+ register: has_toml
+
+- block:
+ - name: check baseline
+ command: ansible-inventory -i '{{ role_path }}/files/valid_sample.yml' --list --toml
+ register: limited
+
+ - name: ensure non empty host list
+ assert:
+ that:
+ - "'something' in inv['somegroup']['hosts']"
+
+ - name: check that limit removes host
+ command: ansible-inventory -i '{{ role_path }}/files/valid_sample.yml' --limit '!something' --list --toml
+ register: limited
+ ignore_errors: true
+
+ - name: ensure empty host list
+ assert:
+ that:
+ - limited is failed
+
+ - name: check dupes
+ command: ansible-inventory -i '{{ role_path }}/files/complex.ini' --list --toml
+ register: limited
+
+ - debug: var=inv
+
+ - name: ensure host only appears on directly assigned
+ assert:
+ that:
+ - "'hosts' not in inv['parent_1']"
+ - "'hosts' not in inv['parent_2']"
+ - "'hosts' in inv['parent_3']"
+ - "'test1' in inv['test_group1']['hosts']"
+ vars:
+ inv: '{{limited.stdout|from_toml}}'
+ when: has_toml is success
+ delegate_to: localhost
diff --git a/test/integration/targets/ansible-inventory/tasks/yaml_output.yml b/test/integration/targets/ansible-inventory/tasks/yaml_output.yml
new file mode 100644
index 0000000..d41a8d0
--- /dev/null
+++ b/test/integration/targets/ansible-inventory/tasks/yaml_output.yml
@@ -0,0 +1,34 @@
+- block:
+ - name: check baseline
+ command: ansible-inventory -i '{{ role_path }}/files/valid_sample.yml' --list --yaml
+ register: limited
+
+ - name: ensure something in host list
+ assert:
+ that:
+ - "'something' in inv['all']['children']['somegroup']['hosts']"
+
+ - name: check that limit removes host
+ command: ansible-inventory -i '{{ role_path }}/files/valid_sample.yml' --limit '!something' --list --yaml
+ register: limited
+
+ - name: ensure empty host list
+ assert:
+ that:
+ - not inv
+
+ - name: check dupes
+ command: ansible-inventory -i '{{ role_path }}/files/complex.ini' --list --yaml
+ register: limited
+
+ - name: ensure host only appears on directly assigned
+ assert:
+ that:
+ - "'hosts' not in inv['all']['children']['parent_1']"
+ - "'hosts' not in inv['all']['children']['parent_2']"
+ - "'hosts' in inv['all']['children']['parent_3']"
+ - "'test1' in inv['all']['children']['parent_1']['children']['test_group1']['hosts']"
+ - "'hosts' not in inv['all']['children']['parent_2']['children']['test_group1']"
+ vars:
+ inv: '{{limited.stdout|from_yaml}}'
+ delegate_to: localhost
diff --git a/test/integration/targets/ansible-playbook-callbacks/aliases b/test/integration/targets/ansible-playbook-callbacks/aliases
new file mode 100644
index 0000000..d88a7fa
--- /dev/null
+++ b/test/integration/targets/ansible-playbook-callbacks/aliases
@@ -0,0 +1,4 @@
+shippable/posix/group3
+context/controller
+needs/target/setup_remote_tmp_dir
+needs/target/support-callback_plugins
diff --git a/test/integration/targets/ansible-playbook-callbacks/all-callbacks.yml b/test/integration/targets/ansible-playbook-callbacks/all-callbacks.yml
new file mode 100644
index 0000000..85a53c7
--- /dev/null
+++ b/test/integration/targets/ansible-playbook-callbacks/all-callbacks.yml
@@ -0,0 +1,123 @@
+- hosts: localhost
+ gather_facts: false
+ vars_prompt:
+ name: vars_prompt_var
+ default: hamsandwich
+ handlers:
+ - name: handler1
+ debug:
+ msg: handler1
+
+ - debug:
+ msg: listen1
+ listen:
+ - listen1
+ roles:
+ - setup_remote_tmp_dir
+ tasks:
+ - name: ok
+ debug:
+ msg: ok
+
+ - name: changed
+ debug:
+ msg: changed
+ changed_when: true
+
+ - name: skipped
+ debug:
+ msg: skipped
+ when: false
+
+ - name: failed
+ debug:
+ msg: failed
+ failed_when: true
+ ignore_errors: true
+
+ - name: unreachable
+ ping:
+ delegate_to: example.org
+ ignore_unreachable: true
+ vars:
+ ansible_timeout: 1
+
+ - name: loop
+ debug:
+ ignore_errors: true
+ changed_when: '{{ item.changed }}'
+ failed_when: '{{ item.failed }}'
+ when: '{{ item.when }}'
+ loop:
+ # ok
+ - changed: false
+ failed: false
+ when: true
+ # changed
+ - changed: true
+ failed: false
+ when: true
+ # failed
+ - changed: false
+ failed: true
+ when: true
+ # skipped
+ - changed: false
+ failed: false
+ when: false
+
+ - name: notify handler1
+ debug:
+ msg: notify handler1
+ changed_when: true
+ notify:
+ - handler1
+
+ - name: notify listen1
+ debug:
+ msg: notify listen1
+ changed_when: true
+ notify:
+ - listen1
+
+ - name: retry ok
+ debug:
+ register: result
+ until: result.attempts == 2
+ retries: 1
+ delay: 0
+
+ - name: retry failed
+ debug:
+ register: result
+ until: result.attempts == 3
+ retries: 1
+ delay: 0
+ ignore_errors: true
+
+ - name: async poll ok
+ command: sleep 3
+ async: 5
+ poll: 2
+
+ - name: async poll failed
+ shell: sleep 3; false
+ async: 5
+ poll: 2
+ ignore_errors: true
+
+ - include_tasks: include_me.yml
+
+ - name: diff
+ copy:
+ content: diff
+ dest: '{{ remote_tmp_dir }}/diff.txt'
+ diff: true
+
+- hosts: i_dont_exist
+
+- hosts: localhost
+ gather_facts: false
+ max_fail_percentage: 0
+ tasks:
+ - fail:
diff --git a/test/integration/targets/ansible-playbook-callbacks/callbacks_list.expected b/test/integration/targets/ansible-playbook-callbacks/callbacks_list.expected
new file mode 100644
index 0000000..1d064a2
--- /dev/null
+++ b/test/integration/targets/ansible-playbook-callbacks/callbacks_list.expected
@@ -0,0 +1,24 @@
+ 1 __init__
+92 v2_on_any
+ 1 v2_on_file_diff
+ 4 v2_playbook_on_handler_task_start
+ 2 v2_playbook_on_include
+ 1 v2_playbook_on_no_hosts_matched
+ 3 v2_playbook_on_notify
+ 3 v2_playbook_on_play_start
+ 1 v2_playbook_on_start
+ 1 v2_playbook_on_stats
+19 v2_playbook_on_task_start
+ 1 v2_playbook_on_vars_prompt
+ 1 v2_runner_item_on_failed
+ 2 v2_runner_item_on_ok
+ 1 v2_runner_item_on_skipped
+ 1 v2_runner_on_async_failed
+ 1 v2_runner_on_async_ok
+ 2 v2_runner_on_async_poll
+ 5 v2_runner_on_failed
+16 v2_runner_on_ok
+ 1 v2_runner_on_skipped
+23 v2_runner_on_start
+ 1 v2_runner_on_unreachable
+ 2 v2_runner_retry
diff --git a/test/integration/targets/collections/testcoll2/MANIFEST.json b/test/integration/targets/ansible-playbook-callbacks/include_me.yml
index e69de29..e69de29 100644
--- a/test/integration/targets/collections/testcoll2/MANIFEST.json
+++ b/test/integration/targets/ansible-playbook-callbacks/include_me.yml
diff --git a/test/integration/targets/ansible-playbook-callbacks/runme.sh b/test/integration/targets/ansible-playbook-callbacks/runme.sh
new file mode 100755
index 0000000..933863e
--- /dev/null
+++ b/test/integration/targets/ansible-playbook-callbacks/runme.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -eux
+
+export ANSIBLE_CALLBACK_PLUGINS=../support-callback_plugins/callback_plugins
+export ANSIBLE_ROLES_PATH=../
+export ANSIBLE_STDOUT_CALLBACK=callback_debug
+export ANSIBLE_HOST_PATTERN_MISMATCH=warning
+
+ansible-playbook all-callbacks.yml 2>/dev/null | sort | uniq -c | tee callbacks_list.out
+
+diff -w callbacks_list.out callbacks_list.expected
diff --git a/test/integration/targets/ansible-pull/pull-integration-test/conn_secret.yml b/test/integration/targets/ansible-pull/pull-integration-test/conn_secret.yml
new file mode 100644
index 0000000..f884973
--- /dev/null
+++ b/test/integration/targets/ansible-pull/pull-integration-test/conn_secret.yml
@@ -0,0 +1,12 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - ping: data='{{ansible_password}}'
+ register: dumb
+ vars:
+ ansible_python_interpreter: '{{ansible_playbook_python}}'
+
+ - name: If we got here, password was passed!
+ assert:
+ that:
+ - "dumb.ping == 'Testing123'"
diff --git a/test/integration/targets/ansible-pull/pull-integration-test/secret_connection_password b/test/integration/targets/ansible-pull/pull-integration-test/secret_connection_password
new file mode 100644
index 0000000..44e6a2c
--- /dev/null
+++ b/test/integration/targets/ansible-pull/pull-integration-test/secret_connection_password
@@ -0,0 +1 @@
+Testing123
diff --git a/test/integration/targets/ansible-pull/runme.sh b/test/integration/targets/ansible-pull/runme.sh
index 582e809..b591b28 100755
--- a/test/integration/targets/ansible-pull/runme.sh
+++ b/test/integration/targets/ansible-pull/runme.sh
@@ -36,7 +36,8 @@ function pass_tests {
fi
# test for https://github.com/ansible/ansible/issues/13681
- if grep -E '127\.0\.0\.1.*ok' "${temp_log}"; then
+ # match play default output stats, was matching limit + docker
+ if grep -E '127\.0\.0\.1\s*: ok=' "${temp_log}"; then
cat "${temp_log}"
echo "Found host 127.0.0.1 in output. Only localhost should be present."
exit 1
@@ -86,5 +87,7 @@ ANSIBLE_CONFIG='' ansible-pull -d "${pull_dir}" -U "${repo_dir}" "$@" multi_play
pass_tests_multi
+ANSIBLE_CONFIG='' ansible-pull -d "${pull_dir}" -U "${repo_dir}" conn_secret.yml --connection-password-file "${repo_dir}/secret_connection_password" "$@"
+
# fail if we try do delete /var/tmp
ANSIBLE_CONFIG='' ansible-pull -d var/tmp -U "${repo_dir}" --purge "$@"
diff --git a/test/integration/targets/ansible-runner/aliases b/test/integration/targets/ansible-runner/aliases
index 13e7d78..f4caffd 100644
--- a/test/integration/targets/ansible-runner/aliases
+++ b/test/integration/targets/ansible-runner/aliases
@@ -1,5 +1,4 @@
shippable/posix/group5
context/controller
-skip/osx
skip/macos
skip/freebsd
diff --git a/test/integration/targets/ansible-runner/files/adhoc_example1.py b/test/integration/targets/ansible-runner/files/adhoc_example1.py
index ab24bca..fe7f944 100644
--- a/test/integration/targets/ansible-runner/files/adhoc_example1.py
+++ b/test/integration/targets/ansible-runner/files/adhoc_example1.py
@@ -2,7 +2,6 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
-import os
import sys
import ansible_runner
diff --git a/test/integration/targets/ansible-test-cloud-foreman/aliases b/test/integration/targets/ansible-test-cloud-foreman/aliases
deleted file mode 100644
index a4bdcea..0000000
--- a/test/integration/targets/ansible-test-cloud-foreman/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-cloud/foreman
-shippable/generic/group1
-context/controller
diff --git a/test/integration/targets/ansible-test-cloud-foreman/tasks/main.yml b/test/integration/targets/ansible-test-cloud-foreman/tasks/main.yml
deleted file mode 100644
index 4170d83..0000000
--- a/test/integration/targets/ansible-test-cloud-foreman/tasks/main.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-- name: Verify endpoints respond
- uri:
- url: "{{ item }}"
- validate_certs: no
- with_items:
- - http://{{ ansible_env.FOREMAN_HOST }}:{{ ansible_env.FOREMAN_PORT }}/ping
diff --git a/test/integration/targets/ansible-test-cloud-openshift/aliases b/test/integration/targets/ansible-test-cloud-openshift/aliases
index 6e32db7..b714e82 100644
--- a/test/integration/targets/ansible-test-cloud-openshift/aliases
+++ b/test/integration/targets/ansible-test-cloud-openshift/aliases
@@ -1,4 +1,4 @@
cloud/openshift
shippable/generic/group1
-disabled # disabled due to requirements conflict: botocore 1.20.6 has requirement urllib3<1.27,>=1.25.4, but you have urllib3 1.24.3.
+disabled # the container crashes when using a non-default network on some docker hosts (such as Ubuntu 20.04)
context/controller
diff --git a/test/integration/targets/ansible-test-cloud-openshift/tasks/main.yml b/test/integration/targets/ansible-test-cloud-openshift/tasks/main.yml
index c3b5190..6acb67d 100644
--- a/test/integration/targets/ansible-test-cloud-openshift/tasks/main.yml
+++ b/test/integration/targets/ansible-test-cloud-openshift/tasks/main.yml
@@ -1,6 +1,13 @@
+- name: Load kubeconfig
+ include_vars: "{{ lookup('env', 'K8S_AUTH_KUBECONFIG') }}"
+
+- name: Verify endpoints exist
+ assert:
+ that: clusters
+
- name: Verify endpoints respond
uri:
- url: "{{ item }}"
+ url: "{{ item.cluster.server }}"
validate_certs: no
with_items:
- - https://openshift-origin:8443/
+ - "{{ clusters }}"
diff --git a/test/integration/targets/ansible-test-cloud-vcenter/aliases b/test/integration/targets/ansible-test-cloud-vcenter/aliases
deleted file mode 100644
index 0cd8ad2..0000000
--- a/test/integration/targets/ansible-test-cloud-vcenter/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-cloud/vcenter
-shippable/generic/group1
-context/controller
diff --git a/test/integration/targets/ansible-test-cloud-vcenter/tasks/main.yml b/test/integration/targets/ansible-test-cloud-vcenter/tasks/main.yml
deleted file mode 100644
index 49e5c16..0000000
--- a/test/integration/targets/ansible-test-cloud-vcenter/tasks/main.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-- name: Verify endpoints respond
- uri:
- url: "{{ item }}"
- validate_certs: no
- with_items:
- - http://{{ vcenter_hostname }}:5000/ # control endpoint for the simulator
diff --git a/test/integration/targets/ansible-test-container/runme.py b/test/integration/targets/ansible-test-container/runme.py
index 8ff48e0..3c86b6d 100755
--- a/test/integration/targets/ansible-test-container/runme.py
+++ b/test/integration/targets/ansible-test-container/runme.py
@@ -996,7 +996,7 @@ class AptBootstrapper(Bootstrapper):
@classmethod
def install_podman(cls) -> bool:
"""Return True if podman will be installed."""
- return not (os_release.id == 'ubuntu' and os_release.version_id == '20.04')
+ return not (os_release.id == 'ubuntu' and os_release.version_id in {'20.04', '22.04'})
@classmethod
def install_docker(cls) -> bool:
@@ -1053,13 +1053,14 @@ class ApkBootstrapper(Bootstrapper):
# crun added as podman won't install it as dep if runc is present
# but we don't want runc as it fails
# The edge `crun` package installed below requires ip6tables, and in
- # edge, the `iptables` package includes `ip6tables`, but in 3.16 they
- # are separate.
+ # edge, the `iptables` package includes `ip6tables`, but in 3.18 they
+ # are separate. Remove `ip6tables` once we update to 3.19.
packages = ['docker', 'podman', 'openssl', 'crun', 'ip6tables']
run_command('apk', 'add', *packages)
- # 3.16 only contains crun 1.4.5, to get 1.9.2 to resolve the run/shm issue, install crun from edge
- run_command('apk', 'upgrade', '-U', '--repository=http://dl-cdn.alpinelinux.org/alpine/edge/community', 'crun')
+ # 3.18 only contains crun 1.8.4, to get 1.9.2 to resolve the run/shm issue, install crun from 3.19
+ # Remove once we update to 3.19
+ run_command('apk', 'upgrade', '-U', '--repository=http://dl-cdn.alpinelinux.org/alpine/v3.19/community', 'crun')
run_command('service', 'docker', 'start')
run_command('modprobe', 'tun')
diff --git a/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor1.py b/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor1.py
index f59b909..f662b97 100644
--- a/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor1.py
+++ b/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor1.py
@@ -16,10 +16,10 @@ RETURN = '''#'''
from ansible.plugins.lookup import LookupBase
# noinspection PyUnresolvedReferences
-from ansible.plugins import loader # import the loader to verify it works when the collection loader has already been loaded
+from ansible.plugins import loader # import the loader to verify it works when the collection loader has already been loaded # pylint: disable=unused-import
try:
- import demo
+ import demo # pylint: disable=unused-import
except ImportError:
pass
else:
diff --git a/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor2.py b/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor2.py
index 22b4236..38860b0 100644
--- a/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor2.py
+++ b/test/integration/targets/ansible-test-sanity-import/ansible_collections/ns/col/plugins/lookup/vendor2.py
@@ -16,10 +16,10 @@ RETURN = '''#'''
from ansible.plugins.lookup import LookupBase
# noinspection PyUnresolvedReferences
-from ansible.plugins import loader # import the loader to verify it works when the collection loader has already been loaded
+from ansible.plugins import loader # import the loader to verify it works when the collection loader has already been loaded # pylint: disable=unused-import
try:
- import demo
+ import demo # pylint: disable=unused-import
except ImportError:
pass
else:
diff --git a/test/integration/targets/ansible-test-sanity-import/expected.txt b/test/integration/targets/ansible-test-sanity-import/expected.txt
new file mode 100644
index 0000000..ab41fd7
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-import/expected.txt
@@ -0,0 +1,2 @@
+plugins/lookup/stderr.py:0:0: stderr: unwanted stderr
+plugins/lookup/stdout.py:0:0: stdout: unwanted stdout
diff --git a/test/integration/targets/ansible-test-sanity-import/runme.sh b/test/integration/targets/ansible-test-sanity-import/runme.sh
index a12e3e3..a49a71a 100755
--- a/test/integration/targets/ansible-test-sanity-import/runme.sh
+++ b/test/integration/targets/ansible-test-sanity-import/runme.sh
@@ -1,7 +1,29 @@
#!/usr/bin/env bash
+set -eu
+
+# Create test scenarios at runtime that do not pass sanity tests.
+# This avoids the need to create ignore entries for the tests.
+
+mkdir -p ansible_collections/ns/col/plugins/lookup
+
+(
+ cd ansible_collections/ns/col/plugins/lookup
+
+ echo "import sys; sys.stdout.write('unwanted stdout')" > stdout.py # stdout: unwanted stdout
+ echo "import sys; sys.stderr.write('unwanted stderr')" > stderr.py # stderr: unwanted stderr
+)
+
source ../collection/setup.sh
+# Run regular import sanity tests.
+
+ansible-test sanity --test import --color --failure-ok --lint --python "${ANSIBLE_TEST_PYTHON_VERSION}" "${@}" 1> actual-stdout.txt 2> actual-stderr.txt
+diff -u "${TEST_DIR}/expected.txt" actual-stdout.txt
+grep -f "${TEST_DIR}/expected.txt" actual-stderr.txt
+
+# Run import sanity tests which require modifications to the source directory.
+
vendor_dir="$(python -c 'import pathlib, ansible._vendor; print(pathlib.Path(ansible._vendor.__file__).parent)')"
cleanup() {
diff --git a/test/integration/targets/ansible-test-sanity-no-get-exception/aliases b/test/integration/targets/ansible-test-sanity-no-get-exception/aliases
new file mode 100644
index 0000000..7741d44
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-no-get-exception/aliases
@@ -0,0 +1,4 @@
+shippable/posix/group3 # runs in the distro test containers
+shippable/generic/group1 # runs in the default test container
+context/controller
+needs/target/collection
diff --git a/test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/do-not-check-me.py b/test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/do-not-check-me.py
new file mode 100644
index 0000000..ca25269
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/do-not-check-me.py
@@ -0,0 +1,5 @@
+from ansible.module_utils.pycompat24 import get_exception
+
+
+def do_stuff():
+ get_exception()
diff --git a/test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/plugins/modules/check-me.py b/test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/plugins/modules/check-me.py
new file mode 100644
index 0000000..ca25269
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-no-get-exception/ansible_collections/ns/col/plugins/modules/check-me.py
@@ -0,0 +1,5 @@
+from ansible.module_utils.pycompat24 import get_exception
+
+
+def do_stuff():
+ get_exception()
diff --git a/test/integration/targets/ansible-test-sanity-no-get-exception/expected.txt b/test/integration/targets/ansible-test-sanity-no-get-exception/expected.txt
new file mode 100644
index 0000000..4c432cb
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-no-get-exception/expected.txt
@@ -0,0 +1,2 @@
+plugins/modules/check-me.py:1:44: do not use `get_exception`
+plugins/modules/check-me.py:5:4: do not use `get_exception`
diff --git a/test/integration/targets/ansible-test-sanity-no-get-exception/runme.sh b/test/integration/targets/ansible-test-sanity-no-get-exception/runme.sh
new file mode 100755
index 0000000..b8ec2d0
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-no-get-exception/runme.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -eu
+
+source ../collection/setup.sh
+
+set -x
+
+ansible-test sanity --test no-get-exception --color --lint --failure-ok "${@}" > actual.txt
+
+diff -u "${TEST_DIR}/expected.txt" actual.txt
+diff -u do-not-check-me.py plugins/modules/check-me.py
diff --git a/test/integration/targets/ansible-test-sanity-pylint/aliases b/test/integration/targets/ansible-test-sanity-pylint/aliases
new file mode 100644
index 0000000..7741d44
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-pylint/aliases
@@ -0,0 +1,4 @@
+shippable/posix/group3 # runs in the distro test containers
+shippable/generic/group1 # runs in the default test container
+context/controller
+needs/target/collection
diff --git a/test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/galaxy.yml b/test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/galaxy.yml
new file mode 100644
index 0000000..53a7727
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/galaxy.yml
@@ -0,0 +1,6 @@
+namespace: ns
+name: col
+version:
+readme: README.rst
+authors:
+ - Ansible
diff --git a/test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/plugins/lookup/deprecated.py b/test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/plugins/lookup/deprecated.py
new file mode 100644
index 0000000..b7908b6
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-pylint/ansible_collections/ns/col/plugins/lookup/deprecated.py
@@ -0,0 +1,22 @@
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+name: deprecated
+short_description: lookup
+description: Lookup.
+author:
+ - Ansible Core Team
+'''
+
+EXAMPLES = '''#'''
+RETURN = '''#'''
+
+from ansible.plugins.lookup import LookupBase
+
+
+class LookupModule(LookupBase):
+ def run(self, **kwargs):
+ return []
diff --git a/test/integration/targets/ansible-test-sanity-pylint/expected.txt b/test/integration/targets/ansible-test-sanity-pylint/expected.txt
new file mode 100644
index 0000000..df7bbc2
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-pylint/expected.txt
@@ -0,0 +1 @@
+plugins/lookup/deprecated.py:27:0: collection-deprecated-version: Deprecated version ('2.0.0') found in call to Display.deprecated or AnsibleModule.deprecate
diff --git a/test/integration/targets/ansible-test-sanity-pylint/runme.sh b/test/integration/targets/ansible-test-sanity-pylint/runme.sh
new file mode 100755
index 0000000..72190bf
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-pylint/runme.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -eu
+
+source ../collection/setup.sh
+
+# Create test scenarios at runtime that do not pass sanity tests.
+# This avoids the need to create ignore entries for the tests.
+
+echo "
+from ansible.utils.display import Display
+
+display = Display()
+display.deprecated('', version='2.0.0', collection_name='ns.col')" >> plugins/lookup/deprecated.py
+
+# Verify deprecation checking works for normal releases and pre-releases.
+
+for version in 2.0.0 2.0.0-dev0; do
+ echo "Checking version: ${version}"
+ sed "s/^version:.*\$/version: ${version}/" < galaxy.yml > galaxy.yml.tmp
+ mv galaxy.yml.tmp galaxy.yml
+ ansible-test sanity --test pylint --color --failure-ok --lint "${@}" 1> actual-stdout.txt 2> actual-stderr.txt
+ diff -u "${TEST_DIR}/expected.txt" actual-stdout.txt
+ grep -f "${TEST_DIR}/expected.txt" actual-stderr.txt
+done
diff --git a/test/integration/targets/ansible-test-sanity-replace-urlopen/aliases b/test/integration/targets/ansible-test-sanity-replace-urlopen/aliases
new file mode 100644
index 0000000..7741d44
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-replace-urlopen/aliases
@@ -0,0 +1,4 @@
+shippable/posix/group3 # runs in the distro test containers
+shippable/generic/group1 # runs in the default test container
+context/controller
+needs/target/collection
diff --git a/test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/do-not-check-me.py b/test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/do-not-check-me.py
new file mode 100644
index 0000000..9b9c7e6
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/do-not-check-me.py
@@ -0,0 +1,5 @@
+import urllib.request
+
+
+def do_stuff():
+ urllib.request.urlopen('https://www.ansible.com/')
diff --git a/test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/plugins/modules/check-me.py b/test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/plugins/modules/check-me.py
new file mode 100644
index 0000000..9b9c7e6
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-replace-urlopen/ansible_collections/ns/col/plugins/modules/check-me.py
@@ -0,0 +1,5 @@
+import urllib.request
+
+
+def do_stuff():
+ urllib.request.urlopen('https://www.ansible.com/')
diff --git a/test/integration/targets/ansible-test-sanity-replace-urlopen/expected.txt b/test/integration/targets/ansible-test-sanity-replace-urlopen/expected.txt
new file mode 100644
index 0000000..4dd1bfb
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-replace-urlopen/expected.txt
@@ -0,0 +1 @@
+plugins/modules/check-me.py:5:20: use `ansible.module_utils.urls.open_url` instead of `urlopen`
diff --git a/test/integration/targets/ansible-test-sanity-replace-urlopen/runme.sh b/test/integration/targets/ansible-test-sanity-replace-urlopen/runme.sh
new file mode 100755
index 0000000..e6637c5
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-replace-urlopen/runme.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -eu
+
+source ../collection/setup.sh
+
+set -x
+
+ansible-test sanity --test replace-urlopen --color --lint --failure-ok "${@}" > actual.txt
+
+diff -u "${TEST_DIR}/expected.txt" actual.txt
+diff -u do-not-check-me.py plugins/modules/check-me.py
diff --git a/test/integration/targets/ansible-test-sanity-use-compat-six/aliases b/test/integration/targets/ansible-test-sanity-use-compat-six/aliases
new file mode 100644
index 0000000..7741d44
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-use-compat-six/aliases
@@ -0,0 +1,4 @@
+shippable/posix/group3 # runs in the distro test containers
+shippable/generic/group1 # runs in the default test container
+context/controller
+needs/target/collection
diff --git a/test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/do-not-check-me.py b/test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/do-not-check-me.py
new file mode 100644
index 0000000..7f7f9f5
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/do-not-check-me.py
@@ -0,0 +1,5 @@
+import six
+
+
+def do_stuff():
+ assert six.text_type
diff --git a/test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/plugins/modules/check-me.py b/test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/plugins/modules/check-me.py
new file mode 100644
index 0000000..7f7f9f5
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-use-compat-six/ansible_collections/ns/col/plugins/modules/check-me.py
@@ -0,0 +1,5 @@
+import six
+
+
+def do_stuff():
+ assert six.text_type
diff --git a/test/integration/targets/ansible-test-sanity-use-compat-six/expected.txt b/test/integration/targets/ansible-test-sanity-use-compat-six/expected.txt
new file mode 100644
index 0000000..42ba83b
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-use-compat-six/expected.txt
@@ -0,0 +1 @@
+plugins/modules/check-me.py:1:1: use `ansible.module_utils.six` instead of `six`
diff --git a/test/integration/targets/ansible-test-sanity-use-compat-six/runme.sh b/test/integration/targets/ansible-test-sanity-use-compat-six/runme.sh
new file mode 100755
index 0000000..dbd38f9
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-use-compat-six/runme.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -eu
+
+source ../collection/setup.sh
+
+set -x
+
+ansible-test sanity --test use-compat-six --color --lint --failure-ok "${@}" > actual.txt
+
+diff -u "${TEST_DIR}/expected.txt" actual.txt
+diff -u do-not-check-me.py plugins/modules/check-me.py
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/meta/runtime.yml b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/meta/runtime.yml
new file mode 100644
index 0000000..7c4b25d
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/meta/runtime.yml
@@ -0,0 +1,4 @@
+plugin_routing:
+ modules:
+ module:
+ action_plugin: ns.col.action
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/lookup/import_order_lookup.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/lookup/import_order_lookup.py
new file mode 100644
index 0000000..5a1f0ec
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/lookup/import_order_lookup.py
@@ -0,0 +1,16 @@
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import annotations
+
+from ansible.plugins.lookup import LookupBase
+
+DOCUMENTATION = """
+name: import_order_lookup
+short_description: Import order lookup
+description: Import order lookup.
+"""
+
+
+class LookupModule(LookupBase):
+ def run(self, terms, variables=None, **kwargs):
+ return []
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_1.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_1.py
new file mode 100644
index 0000000..1b23b49
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_1.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: check_mode_attribute_1
+short_description: Test for check mode attribute 1
+description: Test for check mode attribute 1.
+author:
+ - Ansible Core Team
+extends_documentation_fragment:
+ - ansible.builtin.action_common_attributes
+attributes:
+ check_mode:
+ # doc says full support, code says none
+ support: full
+ diff_mode:
+ support: none
+ platform:
+ platforms: all
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(), supports_check_mode=False)
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_2.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_2.py
new file mode 100644
index 0000000..0687e9f
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_2.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: check_mode_attribute_2
+short_description: Test for check mode attribute 2
+description: Test for check mode attribute 2.
+author:
+ - Ansible Core Team
+extends_documentation_fragment:
+ - ansible.builtin.action_common_attributes
+attributes:
+ check_mode:
+ # doc says partial support, code says none
+ support: partial
+ details: Whatever this means.
+ diff_mode:
+ support: none
+ platform:
+ platforms: all
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(), supports_check_mode=False)
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_3.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_3.py
new file mode 100644
index 0000000..61226e6
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_3.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: check_mode_attribute_3
+short_description: Test for check mode attribute 3
+description: Test for check mode attribute 3.
+author:
+ - Ansible Core Team
+extends_documentation_fragment:
+ - ansible.builtin.action_common_attributes
+attributes:
+ check_mode:
+ # doc says no support, code says some
+ support: none
+ diff_mode:
+ support: none
+ platform:
+ platforms: all
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(), supports_check_mode=True)
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_4.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_4.py
new file mode 100644
index 0000000..1cb7813
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_4.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: check_mode_attribute_4
+short_description: Test for check mode attribute 4
+description: Test for check mode attribute 4.
+author:
+ - Ansible Core Team
+extends_documentation_fragment:
+ - ansible.builtin.action_common_attributes
+attributes:
+ check_mode:
+ # documentation says some support, but no details
+ support: partial
+ diff_mode:
+ support: none
+ platform:
+ platforms: all
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(), supports_check_mode=True)
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_5.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_5.py
new file mode 100644
index 0000000..a8d8556
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_5.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: check_mode_attribute_5
+short_description: Test for check mode attribute 5
+description: Test for check mode attribute 5.
+author:
+ - Ansible Core Team
+extends_documentation_fragment:
+ - ansible.builtin.action_common_attributes
+attributes:
+ check_mode:
+ # Everything is correct: both docs and code claim no support
+ support: none
+ diff_mode:
+ support: none
+ platform:
+ platforms: all
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(), supports_check_mode=False)
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_6.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_6.py
new file mode 100644
index 0000000..cd5a4fb
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_6.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: check_mode_attribute_6
+short_description: Test for check mode attribute 6
+description: Test for check mode attribute 6.
+author:
+ - Ansible Core Team
+extends_documentation_fragment:
+ - ansible.builtin.action_common_attributes
+attributes:
+ check_mode:
+ # Everything is correct: docs says partial support *with details*, code claims (at least some) support
+ support: partial
+ details: Some details.
+ diff_mode:
+ support: none
+ platform:
+ platforms: all
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(), supports_check_mode=True)
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_7.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_7.py
new file mode 100644
index 0000000..73d976c
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/check_mode_attribute_7.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+module: check_mode_attribute_7
+short_description: Test for check mode attribute 7
+description: Test for check mode attribute 7.
+author:
+ - Ansible Core Team
+extends_documentation_fragment:
+ - ansible.builtin.action_common_attributes
+attributes:
+ check_mode:
+ # Everything is correct: docs says full support, code claims (at least some) support
+ support: full
+ diff_mode:
+ support: none
+ platform:
+ platforms: all
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(), supports_check_mode=True)
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/import_order.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/import_order.py
new file mode 100644
index 0000000..f4f3c9b
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/import_order.py
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+
+DOCUMENTATION = '''
+module: import_order
+short_description: Import order test module
+description: Import order test module.
+author:
+ - Ansible Core Team
+'''
+
+EXAMPLES = '''#'''
+RETURN = ''''''
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict())
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/semantic_markup.py b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/semantic_markup.py
new file mode 100644
index 0000000..587731d
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/semantic_markup.py
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: semantic_markup
+short_description: Test semantic markup
+description:
+ - Test semantic markup.
+ - RV(does.not.exist=true).
+
+author:
+ - Ansible Core Team
+
+options:
+ foo:
+ description:
+ - Test.
+ type: str
+
+ a1:
+ description:
+ - O(foo)
+ - O(foo=bar)
+ - O(foo[1]=bar)
+ - O(ignore:bar=baz)
+ - O(ansible.builtin.copy#module:path=/)
+ - V(foo)
+ - V(bar(1\\2\)3)
+ - V(C(foo\)).
+ - E(env(var\))
+ - RV(ansible.builtin.copy#module:backup)
+ - RV(bar=baz)
+ - RV(ignore:bam)
+ - RV(ignore:bam.bar=baz)
+ - RV(bar).
+ - P(ansible.builtin.file#lookup)
+ type: str
+
+ a2:
+ description: V(C\(foo\)).
+ type: str
+
+ a3:
+ description: RV(bam).
+ type: str
+
+ a4:
+ description: P(foo.bar#baz).
+ type: str
+
+ a5:
+ description: P(foo.bar.baz).
+ type: str
+
+ a6:
+ description: P(foo.bar.baz#woof).
+ type: str
+
+ a7:
+ description: E(foo\(bar).
+ type: str
+
+ a8:
+ description: O(bar).
+ type: str
+
+ a9:
+ description: O(bar=bam).
+ type: str
+
+ a10:
+ description: O(foo.bar=1).
+ type: str
+
+ a11:
+ description: Something with suboptions.
+ type: dict
+ suboptions:
+ b1:
+ description:
+ - V(C\(foo\)).
+ - RV(bam).
+ - P(foo.bar#baz).
+ - P(foo.bar.baz).
+ - P(foo.bar.baz#woof).
+ - E(foo\(bar).
+ - O(bar).
+ - O(bar=bam).
+ - O(foo.bar=1).
+ type: str
+'''
+
+EXAMPLES = '''#'''
+
+RETURN = r'''
+bar:
+ description: Bar.
+ type: int
+ returned: success
+ sample: 5
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+if __name__ == '__main__':
+ module = AnsibleModule(argument_spec=dict(
+ foo=dict(),
+ a1=dict(),
+ a2=dict(),
+ a3=dict(),
+ a4=dict(),
+ a5=dict(),
+ a6=dict(),
+ a7=dict(),
+ a8=dict(),
+ a9=dict(),
+ a10=dict(),
+ a11=dict(type='dict', options=dict(
+ b1=dict(),
+ ))
+ ))
+ module.exit_json()
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/sidecar.yaml b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/sidecar.yaml
index c257542..4ca20ef 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/sidecar.yaml
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/sidecar.yaml
@@ -17,6 +17,9 @@ DOCUMENTATION:
default: foo
author:
- Ansible Core Team
+ seealso:
+ - plugin: ns.col.import_order_lookup
+ plugin_type: lookup
EXAMPLES: |
- name: example for sidecar
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md
index bf1003f..d158a98 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md
@@ -1,3 +1,4 @@
README
------
+
This is a simple collection used to test failures with ``ansible-test sanity --test validate-modules``.
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/meta/runtime.yml b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/meta/runtime.yml
new file mode 100644
index 0000000..7c163fe
--- /dev/null
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/meta/runtime.yml
@@ -0,0 +1,4 @@
+plugin_routing:
+ lookup:
+ lookup:
+ action_plugin: invalid
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md
index bbdd513..9c1c1c3 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md
@@ -1,3 +1,4 @@
README
------
+
This is a simple PowerShell-only collection used to verify that ``ansible-test`` works on a collection.
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/expected.txt b/test/integration/targets/ansible-test-sanity-validate-modules/expected.txt
index 95f12f3..ca6e52a 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/expected.txt
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/expected.txt
@@ -1,5 +1,26 @@
+plugins/lookup/import_order_lookup.py:5:0: import-before-documentation: Import found before documentation variables. All imports must appear below DOCUMENTATION/EXAMPLES/RETURN.
+plugins/modules/check_mode_attribute_1.py:0:0: attributes-check-mode: The module does not declare support for check mode, but the check_mode attribute's support value is 'full' and not 'none'
+plugins/modules/check_mode_attribute_2.py:0:0: attributes-check-mode: The module does not declare support for check mode, but the check_mode attribute's support value is 'partial' and not 'none'
+plugins/modules/check_mode_attribute_3.py:0:0: attributes-check-mode: The module does declare support for check mode, but the check_mode attribute's support value is 'none'
+plugins/modules/check_mode_attribute_4.py:0:0: attributes-check-mode-details: The module declares it does not fully support check mode, but has no details on what exactly that means
+plugins/modules/import_order.py:8:0: import-before-documentation: Import found before documentation variables. All imports must appear below DOCUMENTATION/EXAMPLES/RETURN.
plugins/modules/invalid_yaml_syntax.py:0:0: deprecation-mismatch: "meta/runtime.yml" and DOCUMENTATION.deprecation do not agree.
plugins/modules/invalid_yaml_syntax.py:0:0: missing-documentation: No DOCUMENTATION provided
plugins/modules/invalid_yaml_syntax.py:8:15: documentation-syntax-error: DOCUMENTATION is not valid YAML
plugins/modules/invalid_yaml_syntax.py:12:15: invalid-examples: EXAMPLES is not valid YAML
plugins/modules/invalid_yaml_syntax.py:16:15: return-syntax-error: RETURN is not valid YAML
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.0: While parsing "V(C\(" at index 1: Unnecessarily escaped "(" @ data['options']['a11']['suboptions']['b1']['description'][0]. Got 'V(C\\(foo\\)).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.2: While parsing "P(foo.bar#baz)" at index 1: Plugin name "foo.bar" is not a FQCN @ data['options']['a11']['suboptions']['b1']['description'][2]. Got 'P(foo.bar#baz).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.3: While parsing "P(foo.bar.baz)" at index 1: Parameter "foo.bar.baz" is not of the form FQCN#type @ data['options']['a11']['suboptions']['b1']['description'][3]. Got 'P(foo.bar.baz).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.4: Directive "P(foo.bar.baz#woof)" must contain a valid plugin type; found "woof" @ data['options']['a11']['suboptions']['b1']['description'][4]. Got 'P(foo.bar.baz#woof).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a11.suboptions.b1.description.5: While parsing "E(foo\(" at index 1: Unnecessarily escaped "(" @ data['options']['a11']['suboptions']['b1']['description'][5]. Got 'E(foo\\(bar).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a2.description: While parsing "V(C\(" at index 1: Unnecessarily escaped "(" for dictionary value @ data['options']['a2']['description']. Got 'V(C\\(foo\\)).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a4.description: While parsing "P(foo.bar#baz)" at index 1: Plugin name "foo.bar" is not a FQCN for dictionary value @ data['options']['a4']['description']. Got 'P(foo.bar#baz).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a5.description: While parsing "P(foo.bar.baz)" at index 1: Parameter "foo.bar.baz" is not of the form FQCN#type for dictionary value @ data['options']['a5']['description']. Got 'P(foo.bar.baz).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a6.description: Directive "P(foo.bar.baz#woof)" must contain a valid plugin type; found "woof" for dictionary value @ data['options']['a6']['description']. Got 'P(foo.bar.baz#woof).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: DOCUMENTATION.options.a7.description: While parsing "E(foo\(" at index 1: Unnecessarily escaped "(" for dictionary value @ data['options']['a7']['description']. Got 'E(foo\\(bar).'
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: Directive "O(bar)" contains a non-existing option "bar"
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: Directive "O(bar=bam)" contains a non-existing option "bar"
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: Directive "O(foo.bar=1)" contains a non-existing option "foo.bar"
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: Directive "RV(bam)" contains a non-existing return value "bam"
+plugins/modules/semantic_markup.py:0:0: invalid-documentation-markup: Directive "RV(does.not.exist=true)" contains a non-existing return value "does.not.exist"
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/runme.sh b/test/integration/targets/ansible-test-sanity-validate-modules/runme.sh
index e029996..5e2365a 100755
--- a/test/integration/targets/ansible-test-sanity-validate-modules/runme.sh
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/runme.sh
@@ -6,7 +6,17 @@ set -eux
ansible-test sanity --test validate-modules --color --truncate 0 --failure-ok --lint "${@}" 1> actual-stdout.txt 2> actual-stderr.txt
diff -u "${TEST_DIR}/expected.txt" actual-stdout.txt
-grep -f "${TEST_DIR}/expected.txt" actual-stderr.txt
+grep -F -f "${TEST_DIR}/expected.txt" actual-stderr.txt
+
+cd ../col
+ansible-test sanity --test runtime-metadata
+
+cd ../failure
+if ansible-test sanity --test runtime-metadata 2>&1 | tee out.txt; then
+ echo "runtime-metadata in failure should be invalid"
+ exit 1
+fi
+grep out.txt -e 'extra keys not allowed'
cd ../ps_only
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md
index d8138d3..67b8a83 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md
@@ -1,3 +1,4 @@
README
------
+
This is a simple collection used to verify that ``ansible-test`` works on a collection.
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/meta/runtime.yml b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/meta/runtime.yml
index fee22ad..76ead13 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/meta/runtime.yml
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/meta/runtime.yml
@@ -2,4 +2,11 @@ requires_ansible: '>=2.11' # force ansible-doc to check the Ansible version (re
plugin_routing:
modules:
hi:
- redirect: hello
+ redirect: ns.col2.hello
+ hiya:
+ redirect: ns.col2.package.subdir.hiya
+ module_utils:
+ hi:
+ redirect: ansible_collections.ns.col2.plugins.module_utils
+ hello:
+ redirect: ns.col2.hiya
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/bad.py b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/bad.py
index 580f9d8..16e0bc8 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/bad.py
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/bad.py
@@ -19,9 +19,9 @@ EXAMPLES = '''
RETURN = ''' # '''
from ansible.plugins.lookup import LookupBase
-from ansible import constants
+from ansible import constants # pylint: disable=unused-import
-import lxml
+import lxml # pylint: disable=unused-import
class LookupModule(LookupBase):
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/world.py b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/world.py
index dbb479a..5cdd096 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/world.py
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/lookup/world.py
@@ -19,7 +19,7 @@ EXAMPLES = '''
RETURN = ''' # '''
from ansible.plugins.lookup import LookupBase
-from ansible import constants
+from ansible import constants # pylint: disable=unused-import
class LookupModule(LookupBase):
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/modules/bad.py b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/modules/bad.py
index e79613b..8780e35 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/modules/bad.py
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/modules/bad.py
@@ -19,7 +19,7 @@ EXAMPLES = '''
RETURN = ''''''
from ansible.module_utils.basic import AnsibleModule
-from ansible import constants # intentionally trigger pylint ansible-bad-module-import error
+from ansible import constants # intentionally trigger pylint ansible-bad-module-import error # pylint: disable=unused-import
def main():
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/filter/check_pylint.py b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/plugin_utils/check_pylint.py
index f1be4f3..1fe4dfa 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/filter/check_pylint.py
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/plugin_utils/check_pylint.py
@@ -9,15 +9,10 @@ __metaclass__ = type
# syntax-error: Cannot import 'string' due to syntax error 'invalid syntax (&lt;unknown&gt;, line 109)'
# Python 3.9 fails with astroid 2.2.5 but works on 2.3.3
# syntax-error: Cannot import 'string' due to syntax error 'invalid syntax (&lt;unknown&gt;, line 104)'
-import string
+import string # pylint: disable=unused-import
# Python 3.9 fails with pylint 2.3.1 or 2.4.4 with astroid 2.3.3 but works with pylint 2.5.0 and astroid 2.4.0
# 'Call' object has no attribute 'value'
result = {None: None}[{}.get('something')]
-# pylint 2.3.1 and 2.4.4 report the following error but 2.5.0 and 2.6.0 do not
-# blacklisted-name: Black listed name "foo"
-# see: https://github.com/PyCQA/pylint/issues/3701
-# regression: documented as a known issue and removed from ignore.txt so pylint can be upgraded to 2.6.0
-# if future versions of pylint fix this issue then the ignore should be restored
foo = {}.keys()
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/random_directory/bad.py b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/random_directory/bad.py
index 2e35cf8..e34d1c3 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/random_directory/bad.py
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/random_directory/bad.py
@@ -5,4 +5,4 @@ __metaclass__ = type
# This is not an allowed import, but since this file is in a plugins/ subdirectory that is not checked,
# the import sanity test will not complain.
-import lxml
+import lxml # pylint: disable=unused-import
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py
index 8221543..a5d896f 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py
@@ -4,12 +4,12 @@ __metaclass__ = type
import tempfile
try:
- import urllib2 # intentionally trigger pylint ansible-bad-import error
+ import urllib2 # intentionally trigger pylint ansible-bad-import error # pylint: disable=unused-import
except ImportError:
urllib2 = None
try:
- from urllib2 import Request # intentionally trigger pylint ansible-bad-import-from error
+ from urllib2 import Request # intentionally trigger pylint ansible-bad-import-from error # pylint: disable=unused-import
except ImportError:
Request = None
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/sanity/ignore.txt b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/sanity/ignore.txt
index e1b3f4c..dcbe827 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/sanity/ignore.txt
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/sanity/ignore.txt
@@ -1,6 +1,7 @@
plugins/modules/bad.py import
plugins/modules/bad.py pylint:ansible-bad-module-import
plugins/lookup/bad.py import
+plugins/plugin_utils/check_pylint.py pylint:disallowed-name
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-function
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import-from
diff --git a/test/integration/targets/ansible-test-sanity/runme.sh b/test/integration/targets/ansible-test-sanity/runme.sh
index 233db74..9258495 100755
--- a/test/integration/targets/ansible-test-sanity/runme.sh
+++ b/test/integration/targets/ansible-test-sanity/runme.sh
@@ -1,5 +1,11 @@
#!/usr/bin/env bash
+set -eux
+
+ansible-test sanity --color --allow-disabled -e "${@}"
+
+set +x
+
source ../collection/setup.sh
set -x
diff --git a/test/integration/targets/ansible-test-units-assertions/aliases b/test/integration/targets/ansible-test-units-assertions/aliases
new file mode 100644
index 0000000..f25bc67
--- /dev/null
+++ b/test/integration/targets/ansible-test-units-assertions/aliases
@@ -0,0 +1,4 @@
+shippable/generic/group1 # runs in the default test container
+context/controller
+needs/target/collection
+needs/target/ansible-test
diff --git a/test/integration/targets/ansible-test-units-assertions/ansible_collections/ns/col/tests/unit/plugins/modules/test_assertion.py b/test/integration/targets/ansible-test-units-assertions/ansible_collections/ns/col/tests/unit/plugins/modules/test_assertion.py
new file mode 100644
index 0000000..e172200
--- /dev/null
+++ b/test/integration/targets/ansible-test-units-assertions/ansible_collections/ns/col/tests/unit/plugins/modules/test_assertion.py
@@ -0,0 +1,6 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+def test_assertion():
+ assert dict(yes=True) == dict(no=False)
diff --git a/test/integration/targets/ansible-test-units-assertions/runme.sh b/test/integration/targets/ansible-test-units-assertions/runme.sh
new file mode 100755
index 0000000..86fe5c8
--- /dev/null
+++ b/test/integration/targets/ansible-test-units-assertions/runme.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+source ../collection/setup.sh
+
+set -x
+
+options=$("${TEST_DIR}"/../ansible-test/venv-pythons.py --only-versions)
+IFS=', ' read -r -a pythons <<< "${options}"
+
+for python in "${pythons[@]}"; do
+ if ansible-test units --truncate 0 --python "${python}" --requirements "${@}" 2>&1 | tee pytest.log; then
+ echo "Test did not fail as expected."
+ exit 1
+ fi
+
+ if [ "${python}" = "2.7" ]; then
+ grep "^E *AssertionError$" pytest.log
+ else
+
+ grep "^E *AssertionError: assert {'yes': True} == {'no': False}$" pytest.log
+ fi
+done
diff --git a/test/integration/targets/ansible-test-units-forked/aliases b/test/integration/targets/ansible-test-units-forked/aliases
new file mode 100644
index 0000000..79d7dbd
--- /dev/null
+++ b/test/integration/targets/ansible-test-units-forked/aliases
@@ -0,0 +1,5 @@
+shippable/posix/group3 # runs in the distro test containers
+shippable/generic/group1 # runs in the default test container
+context/controller
+needs/target/collection
+needs/target/ansible-test
diff --git a/test/integration/targets/ansible-test-units-forked/ansible_collections/ns/col/tests/unit/plugins/modules/test_ansible_forked.py b/test/integration/targets/ansible-test-units-forked/ansible_collections/ns/col/tests/unit/plugins/modules/test_ansible_forked.py
new file mode 100644
index 0000000..828099c
--- /dev/null
+++ b/test/integration/targets/ansible-test-units-forked/ansible_collections/ns/col/tests/unit/plugins/modules/test_ansible_forked.py
@@ -0,0 +1,43 @@
+"""Unit tests to verify the functionality of the ansible-forked pytest plugin."""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import os
+import pytest
+import signal
+import sys
+import warnings
+
+
+warnings.warn("This verifies that warnings generated during test collection are reported.")
+
+
+@pytest.mark.xfail
+def test_kill_xfail():
+ os.kill(os.getpid(), signal.SIGKILL) # causes pytest to report stdout and stderr
+
+
+def test_kill():
+ os.kill(os.getpid(), signal.SIGKILL) # causes pytest to report stdout and stderr
+
+
+@pytest.mark.xfail
+def test_exception_xfail():
+ sys.stdout.write("This stdout message should be hidden due to xfail.")
+ sys.stderr.write("This stderr message should be hidden due to xfail.")
+ raise Exception("This error is expected, but should be hidden due to xfail.")
+
+
+def test_exception():
+ sys.stdout.write("This stdout message should be reported since we're throwing an exception.")
+ sys.stderr.write("This stderr message should be reported since we're throwing an exception.")
+ raise Exception("This error is expected and should be visible.")
+
+
+def test_warning():
+ warnings.warn("This verifies that warnings generated at test time are reported.")
+
+
+def test_passed():
+ pass
diff --git a/test/integration/targets/ansible-test-units-forked/runme.sh b/test/integration/targets/ansible-test-units-forked/runme.sh
new file mode 100755
index 0000000..c39f3c4
--- /dev/null
+++ b/test/integration/targets/ansible-test-units-forked/runme.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+source ../collection/setup.sh
+
+set -x
+
+options=$("${TEST_DIR}"/../ansible-test/venv-pythons.py --only-versions)
+IFS=', ' read -r -a pythons <<< "${options}"
+
+for python in "${pythons[@]}"; do
+ echo "*** Checking Python ${python} ***"
+
+ if ansible-test units --truncate 0 --target-python "venv/${python}" "${@}" > output.log 2>&1 ; then
+ cat output.log
+ echo "Unit tests on Python ${python} did not fail as expected. See output above."
+ exit 1
+ fi
+
+ cat output.log
+ echo "Unit tests on Python ${python} failed as expected. See output above. Checking for expected output ..."
+
+ # Verify that the appropriate tests pased, failed or xfailed.
+ grep 'PASSED tests/unit/plugins/modules/test_ansible_forked.py::test_passed' output.log
+ grep 'PASSED tests/unit/plugins/modules/test_ansible_forked.py::test_warning' output.log
+ grep 'XFAIL tests/unit/plugins/modules/test_ansible_forked.py::test_kill_xfail' output.log
+ grep 'FAILED tests/unit/plugins/modules/test_ansible_forked.py::test_kill' output.log
+ grep 'FAILED tests/unit/plugins/modules/test_ansible_forked.py::test_exception' output.log
+ grep 'XFAIL tests/unit/plugins/modules/test_ansible_forked.py::test_exception_xfail' output.log
+
+ # Verify that warnings are properly surfaced.
+ grep 'UserWarning: This verifies that warnings generated at test time are reported.' output.log
+ grep 'UserWarning: This verifies that warnings generated during test collection are reported.' output.log
+
+ # Verify there are no unexpected warnings.
+ grep 'Warning' output.log | grep -v 'UserWarning: This verifies that warnings generated ' && exit 1
+
+ # Verify that details from failed tests are properly surfaced.
+ grep "^Test CRASHED with exit code -9.$" output.log
+ grep "^This stdout message should be reported since we're throwing an exception.$" output.log
+ grep "^This stderr message should be reported since we're throwing an exception.$" output.log
+ grep '^> *raise Exception("This error is expected and should be visible.")$' output.log
+ grep "^E *Exception: This error is expected and should be visible.$" output.log
+
+ echo "*** Done Checking Python ${python} ***"
+done
diff --git a/test/integration/targets/ansible-test/venv-pythons.py b/test/integration/targets/ansible-test/venv-pythons.py
index b380f14..97998bc 100755
--- a/test/integration/targets/ansible-test/venv-pythons.py
+++ b/test/integration/targets/ansible-test/venv-pythons.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
"""Return target Python options for use with ansible-test."""
+import argparse
import os
import shutil
import subprocess
@@ -10,6 +11,11 @@ from ansible import release
def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--only-versions', action='store_true')
+
+ options = parser.parse_args()
+
ansible_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(release.__file__))))
source_root = os.path.join(ansible_root, 'test', 'lib')
@@ -33,6 +39,10 @@ def main():
print(f'{executable} - {"fail" if process.returncode else "pass"}', file=sys.stderr)
if not process.returncode:
+ if options.only_versions:
+ args.append(python_version)
+ continue
+
args.extend(['--target-python', f'venv/{python_version}'])
print(' '.join(args))
diff --git a/test/integration/targets/ansible-vault/invalid_format/broken-group-vars-tasks.yml b/test/integration/targets/ansible-vault/invalid_format/broken-group-vars-tasks.yml
index 71dbacc..2365d47 100644
--- a/test/integration/targets/ansible-vault/invalid_format/broken-group-vars-tasks.yml
+++ b/test/integration/targets/ansible-vault/invalid_format/broken-group-vars-tasks.yml
@@ -20,4 +20,4 @@
# 3366323866663763660a323766383531396433663861656532373663373134376263383263316261
# 3137
-# $ ansible-playbook -i inventory --vault-password-file=vault-secret tasks.yml
+# $ ansible-playbook -i inventory --vault-password-file=vault-secret tasks.yml
diff --git a/test/integration/targets/ansible-vault/runme.sh b/test/integration/targets/ansible-vault/runme.sh
index 50720ea..98399ec 100755
--- a/test/integration/targets/ansible-vault/runme.sh
+++ b/test/integration/targets/ansible-vault/runme.sh
@@ -47,6 +47,18 @@ echo $?
# view the vault encrypted password file
ansible-vault view "$@" --vault-id vault-password encrypted-vault-password
+# check if ansible-vault fails when destination is not writable
+NOT_WRITABLE_DIR="${MYTMPDIR}/not_writable"
+TEST_FILE_EDIT4="${NOT_WRITABLE_DIR}/testfile"
+mkdir "${NOT_WRITABLE_DIR}"
+touch "${TEST_FILE_EDIT4}"
+chmod ugo-w "${NOT_WRITABLE_DIR}"
+ansible-vault encrypt "$@" --vault-password-file vault-password "${TEST_FILE_EDIT4}" < /dev/null > log 2>&1 && :
+grep "not writable" log && :
+WRONG_RC=$?
+echo "rc was $WRONG_RC (1 is expected)"
+[ $WRONG_RC -eq 1 ]
+
# encrypt with a password from a vault encrypted password file and multiple vault-ids
# should fail because we dont know which vault id to use to encrypt with
ansible-vault encrypt "$@" --vault-id vault-password --vault-id encrypted-vault-password "${TEST_FILE_ENC_PASSWORD}" && :
@@ -574,3 +586,23 @@ ansible-playbook realpath.yml "$@" --vault-password-file symlink/get-password-sy
# using symlink
ansible-playbook symlink.yml "$@" --vault-password-file script/vault-secret.sh 2>&1 |grep "${ER}"
+
+### SALT TESTING ###
+# prep files for encryption
+for salted in test1 test2 test3
+do
+ echo 'this is salty' > "salted_${salted}"
+done
+
+# encrypt files
+ANSIBLE_VAULT_ENCRYPT_SALT=salty ansible-vault encrypt salted_test1 --vault-password-file example1_password "$@"
+ANSIBLE_VAULT_ENCRYPT_SALT=salty ansible-vault encrypt salted_test2 --vault-password-file example1_password "$@"
+ansible-vault encrypt salted_test3 --vault-password-file example1_password "$@"
+
+# should be the same
+out=$(diff salted_test1 salted_test2)
+[ "${out}" == "" ]
+
+# shoudl be diff
+out=$(diff salted_test1 salted_test3 || true)
+[ "${out}" != "" ]
diff --git a/test/integration/targets/ansible-vault/test_vault.yml b/test/integration/targets/ansible-vault/test_vault.yml
index 7f8ed11..c21d49a 100644
--- a/test/integration/targets/ansible-vault/test_vault.yml
+++ b/test/integration/targets/ansible-vault/test_vault.yml
@@ -1,6 +1,6 @@
- hosts: testhost
gather_facts: False
vars:
- - output_dir: .
+ output_dir: .
roles:
- { role: test_vault, tags: test_vault}
diff --git a/test/integration/targets/ansible-vault/test_vaulted_template.yml b/test/integration/targets/ansible-vault/test_vaulted_template.yml
index b495211..6a16ec8 100644
--- a/test/integration/targets/ansible-vault/test_vaulted_template.yml
+++ b/test/integration/targets/ansible-vault/test_vaulted_template.yml
@@ -1,6 +1,6 @@
- hosts: testhost
gather_facts: False
vars:
- - output_dir: .
+ output_dir: .
roles:
- { role: test_vaulted_template, tags: test_vaulted_template}
diff --git a/test/integration/targets/ansible/aliases b/test/integration/targets/ansible/aliases
index 8278ec8..c7f2050 100644
--- a/test/integration/targets/ansible/aliases
+++ b/test/integration/targets/ansible/aliases
@@ -1,2 +1,3 @@
shippable/posix/group3
context/controller
+needs/target/support-callback_plugins
diff --git a/test/integration/targets/ansible/ansible-testé.cfg b/test/integration/targets/ansible/ansible-testé.cfg
index 09af947..a0e4e8d 100644
--- a/test/integration/targets/ansible/ansible-testé.cfg
+++ b/test/integration/targets/ansible/ansible-testé.cfg
@@ -1,3 +1,3 @@
[defaults]
remote_user = admin
-collections_paths = /tmp/collections
+collections_path = /tmp/collections
diff --git a/test/integration/targets/ansible/runme.sh b/test/integration/targets/ansible/runme.sh
index e9e72a9..d678021 100755
--- a/test/integration/targets/ansible/runme.sh
+++ b/test/integration/targets/ansible/runme.sh
@@ -14,9 +14,9 @@ ANSIBLE_REMOTE_USER=administrator ansible-config dump| grep 'DEFAULT_REMOTE_USER
ansible-config list | grep 'DEFAULT_REMOTE_USER'
# Collection
-ansible-config view -c ./ansible-testé.cfg | grep 'collections_paths = /tmp/collections'
+ansible-config view -c ./ansible-testé.cfg | grep 'collections_path = /tmp/collections'
ansible-config dump -c ./ansible-testé.cfg | grep 'COLLECTIONS_PATHS([^)]*) ='
-ANSIBLE_COLLECTIONS_PATHS=/tmp/collections ansible-config dump| grep 'COLLECTIONS_PATHS([^)]*) ='
+ANSIBLE_COLLECTIONS_PATH=/tmp/collections ansible-config dump| grep 'COLLECTIONS_PATHS([^)]*) ='
ansible-config list | grep 'COLLECTIONS_PATHS'
# 'view' command must fail when config file is missing or has an invalid file extension
@@ -34,7 +34,7 @@ ansible localhost -m debug -a var=playbook_dir --playbook-dir=/doesnotexist/tmp
env -u ANSIBLE_PLAYBOOK_DIR ANSIBLE_CONFIG=./playbookdir_cfg.ini ansible localhost -m debug -a var=playbook_dir | grep '"playbook_dir": "/doesnotexist/tmp"'
# test adhoc callback triggers
-ANSIBLE_STDOUT_CALLBACK=callback_debug ANSIBLE_LOAD_CALLBACK_PLUGINS=1 ansible --playbook-dir . testhost -i ../../inventory -m ping | grep -E '^v2_' | diff -u adhoc-callback.stdout -
+ANSIBLE_CALLBACK_PLUGINS=../support-callback_plugins/callback_plugins ANSIBLE_STDOUT_CALLBACK=callback_debug ANSIBLE_LOAD_CALLBACK_PLUGINS=1 ansible --playbook-dir . testhost -i ../../inventory -m ping | grep -E '^v2_' | diff -u adhoc-callback.stdout -
# CB_WANTS_IMPLICIT isn't anything in Ansible itself.
# Our test cb plugin just accepts it. It lets us avoid copypasting the whole
diff --git a/test/integration/targets/apt/aliases b/test/integration/targets/apt/aliases
index 5f892f9..20c8709 100644
--- a/test/integration/targets/apt/aliases
+++ b/test/integration/targets/apt/aliases
@@ -1,6 +1,5 @@
shippable/posix/group2
destructive
skip/freebsd
-skip/osx
skip/macos
skip/rhel
diff --git a/test/integration/targets/apt/tasks/apt.yml b/test/integration/targets/apt/tasks/apt.yml
index d273eda..a0bc199 100644
--- a/test/integration/targets/apt/tasks/apt.yml
+++ b/test/integration/targets/apt/tasks/apt.yml
@@ -372,7 +372,7 @@
- libcaca-dev
- libslang2-dev
-# https://github.com/ansible/ansible/issues/38995
+# # https://github.com/ansible/ansible/issues/38995
- name: build-dep for a package
apt:
name: tree
@@ -524,6 +524,55 @@
- "allow_change_held_packages_no_update is not changed"
- "allow_change_held_packages_hello_version.stdout == allow_change_held_packages_hello_version_again.stdout"
+# Remove pkg on hold
+- name: Put hello on hold
+ shell: apt-mark hold hello
+
+- name: Get hold list
+ shell: apt-mark showhold
+ register: hello_hold
+
+- name: Check that the package hello is on the hold list
+ assert:
+ that:
+ - "'hello' in hello_hold.stdout"
+
+- name: Try removing package hello
+ apt:
+ name: hello
+ state: absent
+ register: package_removed
+ ignore_errors: true
+
+- name: verify the package is not removed with dpkg
+ shell: dpkg -l hello
+ register: dpkg_result
+
+- name: Verify that package was not removed
+ assert:
+ that:
+ - package_removed is failed
+ - dpkg_result is success
+
+- name: Try removing package (allow_change_held_packages=yes)
+ apt:
+ name: hello
+ state: absent
+ allow_change_held_packages: yes
+ register: package_removed
+
+- name: verify the package is removed with dpkg
+ shell: dpkg -l hello
+ register: dpkg_result
+ ignore_errors: true
+
+- name: Verify that package removal was succesfull
+ assert:
+ that:
+ - package_removed is success
+ - dpkg_result is failed
+ - package_removed.changed
+
# Virtual package
- name: Install a virtual package
apt:
diff --git a/test/integration/targets/apt/tasks/repo.yml b/test/integration/targets/apt/tasks/repo.yml
index d4cce78..b1d08af 100644
--- a/test/integration/targets/apt/tasks/repo.yml
+++ b/test/integration/targets/apt/tasks/repo.yml
@@ -400,6 +400,8 @@
- { upgrade_type: safe, force_apt_get: True }
- { upgrade_type: full, force_apt_get: True }
+ - include_tasks: "upgrade_scenarios.yml"
+
- name: Remove aptitude if not originally present
apt:
pkg: aptitude
diff --git a/test/integration/targets/apt/tasks/upgrade_scenarios.yml b/test/integration/targets/apt/tasks/upgrade_scenarios.yml
new file mode 100644
index 0000000..a8bf76b
--- /dev/null
+++ b/test/integration/targets/apt/tasks/upgrade_scenarios.yml
@@ -0,0 +1,25 @@
+# See https://github.com/ansible/ansible/issues/77868
+# fail_on_autoremove is not valid parameter for aptitude
+- name: Use fail_on_autoremove using aptitude
+ apt:
+ upgrade: yes
+ fail_on_autoremove: yes
+ register: fail_on_autoremove_result
+
+- name: Check if fail_on_autoremove does not fail with aptitude
+ assert:
+ that:
+ - not fail_on_autoremove_result.failed
+
+# See https://github.com/ansible/ansible/issues/77868
+# allow_downgrade is not valid parameter for aptitude
+- name: Use allow_downgrade using aptitude
+ apt:
+ upgrade: yes
+ allow_downgrade: yes
+ register: allow_downgrade_result
+
+- name: Check if allow_downgrade does not fail with aptitude
+ assert:
+ that:
+ - not allow_downgrade_result.failed
diff --git a/test/integration/targets/apt_key/aliases b/test/integration/targets/apt_key/aliases
index a820ec9..97f534a 100644
--- a/test/integration/targets/apt_key/aliases
+++ b/test/integration/targets/apt_key/aliases
@@ -1,5 +1,4 @@
shippable/posix/group1
skip/freebsd
-skip/osx
skip/macos
skip/rhel
diff --git a/test/integration/targets/apt_key/tasks/main.yml b/test/integration/targets/apt_key/tasks/main.yml
index ffb89b2..7aee56a 100644
--- a/test/integration/targets/apt_key/tasks/main.yml
+++ b/test/integration/targets/apt_key/tasks/main.yml
@@ -21,7 +21,7 @@
- import_tasks: 'apt_key_inline_data.yml'
when: ansible_distribution in ('Ubuntu', 'Debian')
-
+
- import_tasks: 'file.yml'
when: ansible_distribution in ('Ubuntu', 'Debian')
diff --git a/test/integration/targets/apt_repository/aliases b/test/integration/targets/apt_repository/aliases
index 34e2b54..b4fe8db 100644
--- a/test/integration/targets/apt_repository/aliases
+++ b/test/integration/targets/apt_repository/aliases
@@ -1,6 +1,5 @@
destructive
shippable/posix/group1
skip/freebsd
-skip/osx
skip/macos
skip/rhel
diff --git a/test/integration/targets/apt_repository/tasks/apt.yml b/test/integration/targets/apt_repository/tasks/apt.yml
index 9c15e64..2ddf414 100644
--- a/test/integration/targets/apt_repository/tasks/apt.yml
+++ b/test/integration/targets/apt_repository/tasks/apt.yml
@@ -152,6 +152,11 @@
- 'result.changed'
- 'result.state == "present"'
- 'result.repo == test_ppa_spec'
+ - '"sources_added" in result'
+ - 'result.sources_added | length == 1'
+ - '"git" in result.sources_added[0]'
+ - '"sources_removed" in result'
+ - 'result.sources_removed | length == 0'
- result_cache is not changed
- name: 'examine apt cache mtime'
@@ -167,6 +172,17 @@
apt_repository: repo='{{test_ppa_spec}}' state=absent
register: result
+- assert:
+ that:
+ - 'result.changed'
+ - 'result.state == "absent"'
+ - 'result.repo == test_ppa_spec'
+ - '"sources_added" in result'
+ - 'result.sources_added | length == 0'
+ - '"sources_removed" in result'
+ - 'result.sources_removed | length == 1'
+ - '"git" in result.sources_removed[0]'
+
# When installing a repo with the spec, the key is *NOT* added
- name: 'ensure ppa key is absent (expect: pass)'
apt_key: id='{{test_ppa_key}}' state=absent
@@ -224,7 +240,7 @@
- assert:
that:
- result is failed
- - result.msg == 'Please set argument \'repo\' to a non-empty value'
+ - result.msg.startswith("argument 'repo' is of type <class 'NoneType'> and we were unable to convert to str")
- name: Test apt_repository with an empty value for repo
apt_repository:
diff --git a/test/integration/targets/apt_repository/tasks/mode_cleanup.yaml b/test/integration/targets/apt_repository/tasks/mode_cleanup.yaml
index 726de11..62960cc 100644
--- a/test/integration/targets/apt_repository/tasks/mode_cleanup.yaml
+++ b/test/integration/targets/apt_repository/tasks/mode_cleanup.yaml
@@ -4,4 +4,4 @@
- name: Delete existing repo
file:
path: "{{ test_repo_path }}"
- state: absent \ No newline at end of file
+ state: absent
diff --git a/test/integration/targets/argspec/library/argspec.py b/test/integration/targets/argspec/library/argspec.py
index b6d6d11..2d86d77 100644
--- a/test/integration/targets/argspec/library/argspec.py
+++ b/test/integration/targets/argspec/library/argspec.py
@@ -23,6 +23,10 @@ def main():
'type': 'str',
'choices': ['absent', 'present'],
},
+ 'default_value': {
+ 'type': 'bool',
+ 'default': True,
+ },
'path': {},
'content': {},
'mapping': {
@@ -246,7 +250,7 @@ def main():
('state', 'present', ('path', 'content'), True),
),
mutually_exclusive=(
- ('path', 'content'),
+ ('path', 'content', 'default_value',),
),
required_one_of=(
('required_one_of_one', 'required_one_of_two'),
diff --git a/test/integration/targets/become/tasks/main.yml b/test/integration/targets/become/tasks/main.yml
index 4a2ce64..c05824d 100644
--- a/test/integration/targets/become/tasks/main.yml
+++ b/test/integration/targets/become/tasks/main.yml
@@ -11,8 +11,8 @@
ansible_become_user: "{{ become_test_config.user }}"
ansible_become_method: "{{ become_test_config.method }}"
ansible_become_password: "{{ become_test_config.password | default(None) }}"
- loop: "{{
- (become_methods | selectattr('skip', 'undefined') | list)+
+ loop: "{{
+ (become_methods | selectattr('skip', 'undefined') | list)+
(become_methods | selectattr('skip', 'defined') | rejectattr('skip') | list)
}}"
loop_control:
diff --git a/test/integration/targets/blockinfile/tasks/append_newline.yml b/test/integration/targets/blockinfile/tasks/append_newline.yml
new file mode 100644
index 0000000..ae3aef8
--- /dev/null
+++ b/test/integration/targets/blockinfile/tasks/append_newline.yml
@@ -0,0 +1,119 @@
+- name: Create append_newline test file
+ copy:
+ dest: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ content: |
+ line1
+ line2
+ line3
+
+- name: add content to file appending a new line
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ append_newline: true
+ insertafter: "line1"
+ block: |
+ line1.5
+ register: insert_appending_a_new_line
+
+- name: add content to file appending a new line (again)
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ append_newline: true
+ insertafter: "line1"
+ block: |
+ line1.5
+ register: insert_appending_a_new_line_again
+
+- name: get file content after adding content appending a new line
+ stat:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ register: appended_a_new_line
+
+- name: check content is the expected one after inserting content appending a new line
+ assert:
+ that:
+ - insert_appending_a_new_line is changed
+ - insert_appending_a_new_line_again is not changed
+ - appended_a_new_line.stat.checksum == "525ffd613a0b0eb6675e506226dc2adedf621f34"
+
+- name: add content to file without appending a new line
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ marker: "#{mark} UNWRAPPED TEXT"
+ insertafter: "line2"
+ block: |
+ line2.5
+ register: insert_without_appending_new_line
+
+- name: get file content after adding content without appending a new line
+ stat:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ register: without_appending_new_line
+
+- name: check content is the expected one after inserting without appending a new line
+ assert:
+ that:
+ - insert_without_appending_new_line is changed
+ - without_appending_new_line.stat.checksum == "d5f5ed1428af50b5484a5184dc7e1afda1736646"
+
+- name: append a new line to existing block
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ append_newline: true
+ marker: "#{mark} UNWRAPPED TEXT"
+ insertafter: "line2"
+ block: |
+ line2.5
+ register: append_new_line_to_existing_block
+
+- name: get file content after appending a line to existing block
+ stat:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ register: new_line_appended
+
+- name: check content is the expected one after appending a new line to an existing block
+ assert:
+ that:
+ - append_new_line_to_existing_block is changed
+ - new_line_appended.stat.checksum == "b09dd16be73a0077027d5a324294db8a75a7b0f9"
+
+- name: add a block appending a new line at the end of the file
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ append_newline: true
+ marker: "#{mark} END OF FILE TEXT"
+ insertafter: "line3"
+ block: |
+ line3.5
+ register: insert_appending_new_line_at_the_end_of_file
+
+- name: get file content after appending new line at the end of the file
+ stat:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ register: inserted_block_appending_new_line_at_the_end_of_the_file
+
+- name: check content is the expected one after adding a block appending a new line at the end of the file
+ assert:
+ that:
+ - insert_appending_new_line_at_the_end_of_file is changed
+ - inserted_block_appending_new_line_at_the_end_of_the_file.stat.checksum == "9b90722b84d9bdda1be781cc4bd44d8979887691"
+
+
+- name: Removing a block with append_newline set to true does not append another line
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ append_newline: true
+ marker: "#{mark} UNWRAPPED TEXT"
+ state: absent
+ register: remove_block_appending_new_line
+
+- name: get file content after removing existing block appending new line
+ stat:
+ path: "{{ remote_tmp_dir_test }}/append_newline.txt"
+ register: removed_block_appending_new_line
+
+- name: check content is the expected one after removing a block appending a new line
+ assert:
+ that:
+ - remove_block_appending_new_line is changed
+ - removed_block_appending_new_line.stat.checksum == "9a40d4c0969255cd6147537b38309d69a9b10049"
diff --git a/test/integration/targets/blockinfile/tasks/create_dir.yml b/test/integration/targets/blockinfile/tasks/create_dir.yml
new file mode 100644
index 0000000..a16ada5
--- /dev/null
+++ b/test/integration/targets/blockinfile/tasks/create_dir.yml
@@ -0,0 +1,29 @@
+- name: Set up a directory to test module error handling
+ file:
+ path: "{{ remote_tmp_dir_test }}/unreadable"
+ state: directory
+ mode: "000"
+
+- name: Create a directory and file with blockinfile
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/unreadable/createme/file.txt"
+ block: |
+ line 1
+ line 2
+ state: present
+ create: yes
+ register: permissions_error
+ ignore_errors: yes
+
+- name: assert the error looks right
+ assert:
+ that:
+ - permissions_error.msg.startswith('Error creating')
+ when: "ansible_user_id != 'root'"
+
+- name: otherwise (root) assert the directory and file exists
+ stat:
+ path: "{{ remote_tmp_dir_test }}/unreadable/createme/file.txt"
+ register: path_created
+ failed_when: path_created.exists is false
+ when: "ansible_user_id == 'root'"
diff --git a/test/integration/targets/blockinfile/tasks/main.yml b/test/integration/targets/blockinfile/tasks/main.yml
index 054e554..f26cb16 100644
--- a/test/integration/targets/blockinfile/tasks/main.yml
+++ b/test/integration/targets/blockinfile/tasks/main.yml
@@ -31,6 +31,7 @@
- import_tasks: add_block_to_existing_file.yml
- import_tasks: create_file.yml
+- import_tasks: create_dir.yml
- import_tasks: preserve_line_endings.yml
- import_tasks: block_without_trailing_newline.yml
- import_tasks: file_without_trailing_newline.yml
@@ -39,3 +40,5 @@
- import_tasks: insertafter.yml
- import_tasks: insertbefore.yml
- import_tasks: multiline_search.yml
+- import_tasks: append_newline.yml
+- import_tasks: prepend_newline.yml
diff --git a/test/integration/targets/blockinfile/tasks/prepend_newline.yml b/test/integration/targets/blockinfile/tasks/prepend_newline.yml
new file mode 100644
index 0000000..535db01
--- /dev/null
+++ b/test/integration/targets/blockinfile/tasks/prepend_newline.yml
@@ -0,0 +1,119 @@
+- name: Create prepend_newline test file
+ copy:
+ dest: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ content: |
+ line1
+ line2
+ line3
+
+- name: add content to file prepending a new line at the beginning of the file
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ prepend_newline: true
+ insertbefore: "line1"
+ block: |
+ line0.5
+ register: insert_prepending_a_new_line_at_the_beginning_of_the_file
+
+- name: get file content after adding content prepending a new line at the beginning of the file
+ stat:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ register: prepended_a_new_line_at_the_beginning_of_the_file
+
+- name: check content is the expected one after prepending a new line at the beginning of the file
+ assert:
+ that:
+ - insert_prepending_a_new_line_at_the_beginning_of_the_file is changed
+ - prepended_a_new_line_at_the_beginning_of_the_file.stat.checksum == "bfd32c880bbfadd1983c67836c46bf8ed9d50343"
+
+- name: add content to file prepending a new line
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ prepend_newline: true
+ marker: "#{mark} WRAPPED TEXT"
+ insertafter: "line1"
+ block: |
+ line1.5
+ register: insert_prepending_a_new_line
+
+- name: add content to file prepending a new line (again)
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ prepend_newline: true
+ marker: "#{mark} WRAPPED TEXT"
+ insertafter: "line1"
+ block: |
+ line1.5
+ register: insert_prepending_a_new_line_again
+
+- name: get file content after adding content prepending a new line
+ stat:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ register: prepended_a_new_line
+
+- name: check content is the expected one after inserting content prepending a new line
+ assert:
+ that:
+ - insert_prepending_a_new_line is changed
+ - insert_prepending_a_new_line_again is not changed
+ - prepended_a_new_line.stat.checksum == "d5b8b42690f4a38b9a040adc3240a6f81ad5f8ee"
+
+- name: add content to file without prepending a new line
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ marker: "#{mark} UNWRAPPED TEXT"
+ insertafter: "line3"
+ block: |
+ line3.5
+ register: insert_without_prepending_new_line
+
+- name: get file content after adding content without prepending a new line
+ stat:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ register: without_prepending_new_line
+
+- name: check content is the expected one after inserting without prepending a new line
+ assert:
+ that:
+ - insert_without_prepending_new_line is changed
+ - without_prepending_new_line.stat.checksum == "ad06200e7ee5b22b7eff4c57075b42d038eaffb6"
+
+- name: prepend a new line to existing block
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ prepend_newline: true
+ marker: "#{mark} UNWRAPPED TEXT"
+ insertafter: "line3"
+ block: |
+ line3.5
+ register: prepend_new_line_to_existing_block
+
+- name: get file content after prepending a new line to an existing block
+ stat:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ register: new_line_prepended
+
+- name: check content is the expected one after prepending a new line to an existing block
+ assert:
+ that:
+ - prepend_new_line_to_existing_block is changed
+ - new_line_prepended.stat.checksum == "f2dd48160fb3c7c8e02d292666a1a3f08503f6bf"
+
+- name: Removing a block with prepend_newline set to true does not prepend another line
+ blockinfile:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ prepend_newline: true
+ marker: "#{mark} UNWRAPPED TEXT"
+ state: absent
+ register: remove_block_prepending_new_line
+
+- name: get file content after removing existing block prepending new line
+ stat:
+ path: "{{ remote_tmp_dir_test }}/prepend_newline.txt"
+ register: removed_block_prepending_new_line
+
+- name: check content is the expected one after removing a block prepending a new line
+ assert:
+ that:
+ - remove_block_prepending_new_line is changed
+ - removed_block_prepending_new_line.stat.checksum == "c97c3da7d607acfd5d786fbb81f3d93d867c914a" \ No newline at end of file
diff --git a/test/integration/targets/blocks/unsafe_failed_task.yml b/test/integration/targets/blocks/unsafe_failed_task.yml
index adfa492..e74327b 100644
--- a/test/integration/targets/blocks/unsafe_failed_task.yml
+++ b/test/integration/targets/blocks/unsafe_failed_task.yml
@@ -1,7 +1,7 @@
- hosts: localhost
gather_facts: false
vars:
- - data: {}
+ data: {}
tasks:
- block:
- name: template error
diff --git a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout
index 71a4ef9..ed45575 100644
--- a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout
+++ b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_lossy_verbose.stdout
@@ -43,6 +43,7 @@ fatal: [testhost]: FAILED! =>
TASK [Skipped task] ************************************************************
skipping: [testhost] =>
changed: false
+ false_condition: false
skip_reason: Conditional result was False
TASK [Task with var in name (foo bar)] *****************************************
@@ -120,6 +121,7 @@ ok: [testhost] => (item=debug-3) =>
msg: debug-3
skipping: [testhost] => (item=debug-4) =>
ansible_loop_var: item
+ false_condition: item != 4
item: 4
fatal: [testhost]: FAILED! =>
msg: One or more items failed
@@ -199,9 +201,11 @@ skipping: [testhost] =>
TASK [debug] *******************************************************************
skipping: [testhost] => (item=1) =>
ansible_loop_var: item
+ false_condition: false
item: 1
skipping: [testhost] => (item=2) =>
ansible_loop_var: item
+ false_condition: false
item: 2
skipping: [testhost] =>
msg: All items skipped
diff --git a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout
index 7a99cc7..3a121a5 100644
--- a/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout
+++ b/test/integration/targets/callback_default/callback_default.out.result_format_yaml_verbose.stdout
@@ -45,6 +45,7 @@ fatal: [testhost]: FAILED! =>
TASK [Skipped task] ************************************************************
skipping: [testhost] =>
changed: false
+ false_condition: false
skip_reason: Conditional result was False
TASK [Task with var in name (foo bar)] *****************************************
@@ -126,6 +127,7 @@ ok: [testhost] => (item=debug-3) =>
msg: debug-3
skipping: [testhost] => (item=debug-4) =>
ansible_loop_var: item
+ false_condition: item != 4
item: 4
fatal: [testhost]: FAILED! =>
msg: One or more items failed
@@ -206,9 +208,11 @@ skipping: [testhost] =>
TASK [debug] *******************************************************************
skipping: [testhost] => (item=1) =>
ansible_loop_var: item
+ false_condition: false
item: 1
skipping: [testhost] => (item=2) =>
ansible_loop_var: item
+ false_condition: false
item: 2
skipping: [testhost] =>
msg: All items skipped
diff --git a/test/integration/targets/check_mode/check_mode.yml b/test/integration/targets/check_mode/check_mode.yml
index a577750..ebf1c5b 100644
--- a/test/integration/targets/check_mode/check_mode.yml
+++ b/test/integration/targets/check_mode/check_mode.yml
@@ -1,7 +1,7 @@
- name: Test that check works with check_mode specified in roles
hosts: testhost
vars:
- - output_dir: .
+ output_dir: .
roles:
- { role: test_always_run, tags: test_always_run }
- { role: test_check_mode, tags: test_check_mode }
diff --git a/test/integration/targets/check_mode/roles/test_check_mode/tasks/main.yml b/test/integration/targets/check_mode/roles/test_check_mode/tasks/main.yml
index f926d14..ce9ecbf 100644
--- a/test/integration/targets/check_mode/roles/test_check_mode/tasks/main.yml
+++ b/test/integration/targets/check_mode/roles/test_check_mode/tasks/main.yml
@@ -25,8 +25,8 @@
register: foo
- name: verify that the file was marked as changed in check mode
- assert:
- that:
+ assert:
+ that:
- "template_result is changed"
- "not foo.stat.exists"
@@ -44,7 +44,7 @@
check_mode: no
- name: verify that the file was not changed
- assert:
- that:
+ assert:
+ that:
- "checkmode_disabled is changed"
- "template_result2 is not changed"
diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/connection/localconn.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/connection/localconn.py
index fc19a99..77f8050 100644
--- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/connection/localconn.py
+++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/connection/localconn.py
@@ -1,7 +1,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-from ansible.module_utils._text import to_native
+from ansible.module_utils.common.text.converters import to_native
from ansible.plugins.connection import ConnectionBase
DOCUMENTATION = """
diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing.py
index b945eb6..6f3a19d 100644
--- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing.py
+++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing.py
@@ -2,10 +2,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
-import sys
-
-from ..module_utils import bogusmu # pylint: disable=relative-beyond-top-level
+from ..module_utils import bogusmu # pylint: disable=relative-beyond-top-level,unused-import
def main():
diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_collection.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_collection.py
index 59cb3c5..6f2320d 100644
--- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_collection.py
+++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_collection.py
@@ -2,10 +2,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
-import sys
-
-from ..module_utils import missing_redirect_target_collection # pylint: disable=relative-beyond-top-level
+from ..module_utils import missing_redirect_target_collection # pylint: disable=relative-beyond-top-level,unused-import
def main():
diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_module.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_module.py
index 31ffd17..de5c2e5 100644
--- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_module.py
+++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/uses_mu_missing_redirect_module.py
@@ -2,10 +2,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
-import sys
-
-from ..module_utils import missing_redirect_target_module # pylint: disable=relative-beyond-top-level
+from ..module_utils import missing_redirect_target_module # pylint: disable=relative-beyond-top-level,unused-import
def main():
diff --git a/test/integration/targets/collections/collections/ansible_collections/testns/content_adj/plugins/inventory/statichost.py b/test/integration/targets/collections/collections/ansible_collections/testns/content_adj/plugins/inventory/statichost.py
index ae6941f..9269648 100644
--- a/test/integration/targets/collections/collections/ansible_collections/testns/content_adj/plugins/inventory/statichost.py
+++ b/test/integration/targets/collections/collections/ansible_collections/testns/content_adj/plugins/inventory/statichost.py
@@ -19,7 +19,6 @@ DOCUMENTATION = '''
required: True
'''
-from ansible.errors import AnsibleParserError
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable
diff --git a/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py b/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py
index 23cce10..f1242e1 100644
--- a/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py
+++ b/test/integration/targets/collections/test_task_resolved_plugin/callback_plugins/display_resolved_action.py
@@ -14,7 +14,6 @@ DOCUMENTATION = '''
- Enable in configuration.
'''
-from ansible import constants as C
from ansible.plugins.callback import CallbackBase
diff --git a/test/integration/targets/command_nonexisting/tasks/main.yml b/test/integration/targets/command_nonexisting/tasks/main.yml
index d21856e..e54ecb3 100644
--- a/test/integration/targets/command_nonexisting/tasks/main.yml
+++ b/test/integration/targets/command_nonexisting/tasks/main.yml
@@ -1,4 +1,4 @@
- command: commandthatdoesnotexist --would-be-awkward
register: res
changed_when: "'changed' in res.stdout"
- failed_when: "res.stdout != '' or res.stderr != ''" \ No newline at end of file
+ failed_when: "res.stdout != '' or res.stderr != ''"
diff --git a/test/integration/targets/command_shell/scripts/yoink.sh b/test/integration/targets/command_shell/scripts/yoink.sh
new file mode 100755
index 0000000..ca955da
--- /dev/null
+++ b/test/integration/targets/command_shell/scripts/yoink.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+sleep 10
diff --git a/test/integration/targets/command_shell/tasks/main.yml b/test/integration/targets/command_shell/tasks/main.yml
index 1f4aa5d..2cc365d 100644
--- a/test/integration/targets/command_shell/tasks/main.yml
+++ b/test/integration/targets/command_shell/tasks/main.yml
@@ -284,6 +284,30 @@
that:
- "command_result6.stdout == '9cd0697c6a9ff6689f0afb9136fa62e0b3fee903'"
+- name: check default var expansion
+ command: /bin/sh -c 'echo "\$TEST"'
+ environment:
+ TEST: z
+ register: command_result7
+
+- name: assert vars were expanded
+ assert:
+ that:
+ - command_result7.stdout == '\\z'
+
+- name: check disabled var expansion
+ command: /bin/sh -c 'echo "\$TEST"'
+ args:
+ expand_argument_vars: false
+ environment:
+ TEST: z
+ register: command_result8
+
+- name: assert vars were not expanded
+ assert:
+ that:
+ - command_result8.stdout == '$TEST'
+
##
## shell
##
@@ -546,3 +570,21 @@
- command_strip.stderr == 'hello \n '
- command_no_strip.stdout== 'hello \n \r\n'
- command_no_strip.stderr == 'hello \n \r\n'
+
+- name: run shell with expand_argument_vars
+ shell: echo 'hi'
+ args:
+ expand_argument_vars: false
+ register: shell_expand_failure
+ ignore_errors: true
+
+- name: assert shell with expand_arguments_vars failed
+ assert:
+ that:
+ - shell_expand_failure is failed
+ - "shell_expand_failure.msg == 'Unsupported parameters for (shell) module: expand_argument_vars'"
+
+- name: Run command that backgrounds, to ensure no hang
+ shell: '{{ role_path }}/scripts/yoink.sh &'
+ delegate_to: localhost
+ timeout: 5
diff --git a/test/integration/targets/conditionals/play.yml b/test/integration/targets/conditionals/play.yml
index 455818c..56ec843 100644
--- a/test/integration/targets/conditionals/play.yml
+++ b/test/integration/targets/conditionals/play.yml
@@ -665,3 +665,29 @@
- item
loop:
- 1 == 1
+
+ - set_fact:
+ sentinel_file: '{{ lookup("env", "OUTPUT_DIR")}}/LOOKUP_SIDE_EFFECT.txt'
+
+ - name: ensure sentinel file is absent
+ file:
+ path: '{{ sentinel_file }}'
+ state: absent
+ - name: get an untrusted var that's a valid Jinja expression with a side-effect
+ shell: |
+ echo "lookup('pipe', 'echo bang > \"$SENTINEL_FILE\" && cat \"$SENTINEL_FILE\"')"
+ environment:
+ SENTINEL_FILE: '{{ sentinel_file }}'
+ register: untrusted_expr
+ - name: use a conditional with an inline template that refers to the untrusted expression
+ debug:
+ msg: look at some seemingly innocuous stuff
+ when: '"foo" in {{ untrusted_expr.stdout }}'
+ ignore_errors: true
+ - name: ensure the untrusted expression side-effect has not executed
+ stat:
+ path: '{{ sentinel_file }}'
+ register: sentinel_stat
+ - assert:
+ that:
+ - not sentinel_stat.stat.exists
diff --git a/test/integration/targets/connection_delegation/aliases b/test/integration/targets/connection_delegation/aliases
index 6c96566..0ce7601 100644
--- a/test/integration/targets/connection_delegation/aliases
+++ b/test/integration/targets/connection_delegation/aliases
@@ -1,6 +1,5 @@
shippable/posix/group3
context/controller
skip/freebsd # No sshpass
-skip/osx # No sshpass
skip/macos # No sshpass
skip/rhel # No sshpass
diff --git a/test/integration/targets/connection_paramiko_ssh/test_connection.inventory b/test/integration/targets/connection_paramiko_ssh/test_connection.inventory
index a3f34ab..cd17c09 100644
--- a/test/integration/targets/connection_paramiko_ssh/test_connection.inventory
+++ b/test/integration/targets/connection_paramiko_ssh/test_connection.inventory
@@ -2,6 +2,6 @@
paramiko_ssh-pipelining ansible_ssh_pipelining=true
paramiko_ssh-no-pipelining ansible_ssh_pipelining=false
[paramiko_ssh:vars]
-ansible_host=localhost
+ansible_host={{ 'localhost'|string }}
ansible_connection=paramiko_ssh
ansible_python_interpreter="{{ ansible_playbook_python }}"
diff --git a/test/integration/targets/connection_psrp/tests.yml b/test/integration/targets/connection_psrp/tests.yml
index dabbf40..08832b1 100644
--- a/test/integration/targets/connection_psrp/tests.yml
+++ b/test/integration/targets/connection_psrp/tests.yml
@@ -6,6 +6,9 @@
gather_facts: no
tasks:
+ - name: reboot the host
+ ansible.windows.win_reboot:
+
- name: test complex objects in raw output
# until PyYAML is upgraded to 4.x we need to use the \U escape for a unicode codepoint
# and enclose in a quote to it translates the \U
@@ -29,15 +32,8 @@
- raw_out.stdout_lines[4] == "winrm"
- raw_out.stdout_lines[5] == "string - \U0001F4A9"
- # Become only works on Server 2008 when running with basic auth, skip this host for now as it is too complicated to
- # override the auth protocol in the tests.
- - name: check if we running on Server 2008
- win_shell: '[System.Environment]::OSVersion.Version -ge [Version]"6.1"'
- register: os_version
-
- name: test out become with psrp
win_whoami:
- when: os_version|bool
register: whoami_out
become: yes
become_method: runas
@@ -47,7 +43,6 @@
assert:
that:
- whoami_out.account.sid == "S-1-5-18"
- when: os_version|bool
- name: test out async with psrp
win_shell: Start-Sleep -Seconds 2; Write-Output abc
diff --git a/test/integration/targets/connection_winrm/tests.yml b/test/integration/targets/connection_winrm/tests.yml
index 78f92a4..b086a3a 100644
--- a/test/integration/targets/connection_winrm/tests.yml
+++ b/test/integration/targets/connection_winrm/tests.yml
@@ -6,6 +6,9 @@
gather_facts: no
tasks:
+ - name: reboot the host
+ ansible.windows.win_reboot:
+
- name: setup remote tmp dir
import_role:
name: ../../setup_remote_tmp_dir
diff --git a/test/integration/targets/copy/tasks/main.yml b/test/integration/targets/copy/tasks/main.yml
index b86c56a..601312f 100644
--- a/test/integration/targets/copy/tasks/main.yml
+++ b/test/integration/targets/copy/tasks/main.yml
@@ -84,6 +84,7 @@
- import_tasks: check_mode.yml
# https://github.com/ansible/ansible/issues/57618
+ # https://github.com/ansible/ansible/issues/79749
- name: Test diff contents
copy:
content: 'Ansible managed\n'
@@ -95,6 +96,7 @@
that:
- 'diff_output.diff[0].before == ""'
- '"Ansible managed" in diff_output.diff[0].after'
+ - '"file.txt" in diff_output.diff[0].after_header'
- name: tests with remote_src and non files
import_tasks: src_remote_file_is_not_file.yml
diff --git a/test/integration/targets/copy/tasks/tests.yml b/test/integration/targets/copy/tasks/tests.yml
index d6c8e63..40ea9de 100644
--- a/test/integration/targets/copy/tasks/tests.yml
+++ b/test/integration/targets/copy/tasks/tests.yml
@@ -420,6 +420,80 @@
- "stat_results2.stat.mode == '0547'"
#
+# test copying an empty dir to a dest dir with remote_src=True
+#
+
+- name: create empty test dir
+ file:
+ path: '{{ remote_dir }}/testcase_empty_dir'
+ state: directory
+
+- name: test copying an empty dir to a dir that does not exist (dest ends with slash)
+ copy:
+ src: '{{ remote_dir }}/testcase_empty_dir/'
+ remote_src: yes
+ dest: '{{ remote_dir }}/testcase_empty_dir_dest/'
+ register: copy_result
+
+- name: get stat of newly created dir
+ stat:
+ path: '{{ remote_dir }}/testcase_empty_dir_dest'
+ register: stat_result
+
+- assert:
+ that:
+ - copy_result.changed
+ - stat_result.stat.exists
+ - stat_result.stat.isdir
+
+- name: test no change is made running the task twice
+ copy:
+ src: '{{ remote_dir }}/testcase_empty_dir/'
+ remote_src: yes
+ dest: '{{ remote_dir }}/testcase_empty_dir_dest/'
+ register: copy_result
+ failed_when: copy_result is changed
+
+- name: remove to test dest with no trailing slash
+ file:
+ path: '{{ remote_dir }}/testcase_empty_dir_dest/'
+ state: absent
+
+- name: test copying an empty dir to a dir that does not exist (both src/dest have no trailing slash)
+ copy:
+ src: '{{ remote_dir }}/testcase_empty_dir'
+ remote_src: yes
+ dest: '{{ remote_dir }}/testcase_empty_dir_dest'
+ register: copy_result
+
+- name: get stat of newly created dir
+ stat:
+ path: '{{ remote_dir }}/testcase_empty_dir_dest'
+ register: stat_result
+
+- assert:
+ that:
+ - copy_result.changed
+ - stat_result.stat.exists
+ - stat_result.stat.isdir
+
+- name: test no change is made running the task twice
+ copy:
+ src: '{{ remote_dir }}/testcase_empty_dir/'
+ remote_src: yes
+ dest: '{{ remote_dir }}/testcase_empty_dir_dest/'
+ register: copy_result
+ failed_when: copy_result is changed
+
+- name: clean up src and dest
+ file:
+ path: "{{ item }}"
+ state: absent
+ loop:
+ - '{{ remote_dir }}/testcase_empty_dir'
+ - '{{ remote_dir }}/testcase_empty_dir_dest'
+
+#
# test recursive copy local_follow=False, no trailing slash
#
@@ -2284,3 +2358,81 @@
that:
- fail_copy_directory_with_enc_file is failed
- fail_copy_directory_with_enc_file.msg == 'A vault password or secret must be specified to decrypt {{role_path}}/files-different/vault/vault-file'
+
+#
+# Test for issue 74536: recursively copy all nested directories with remote_src=yes and src='dir/' when dest exists
+#
+- vars:
+ src: '{{ remote_dir }}/testcase_74536'
+ block:
+ - name: create source dir with 3 nested subdirs
+ file:
+ path: '{{ src }}/a/b1/c1'
+ state: directory
+
+ - name: copy the source dir with a trailing slash
+ copy:
+ src: '{{ src }}/'
+ remote_src: yes
+ dest: '{{ src }}_dest/'
+ register: copy_result
+ failed_when: copy_result is not changed
+
+ - name: remove the source dir to recreate with different subdirs
+ file:
+ path: '{{ src }}'
+ state: absent
+
+ - name: recreate source dir
+ file:
+ path: "{{ item }}"
+ state: directory
+ loop:
+ - '{{ src }}/a/b1/c2'
+ - '{{ src }}/a/b2/c3'
+
+ - name: copy the source dir containing new subdirs into the existing dest dir
+ copy:
+ src: '{{ src }}/'
+ remote_src: yes
+ dest: '{{ src }}_dest/'
+ register: copy_result
+
+ - name: stat each directory that should exist
+ stat:
+ path: '{{ item }}'
+ register: stat_result
+ loop:
+ - '{{ src }}_dest'
+ - '{{ src }}_dest/a'
+ - '{{ src }}_dest/a/b1'
+ - '{{ src }}_dest/a/b2'
+ - '{{ src }}_dest/a/b1/c1'
+ - '{{ src }}_dest/a/b1/c2'
+ - '{{ src }}_dest/a/b2/c3'
+
+ - debug: msg="{{ stat_result }}"
+
+ - assert:
+ that:
+ - copy_result is changed
+ # all paths exist
+ - stat_result.results | map(attribute='stat') | map(attribute='exists') | unique == [true]
+ # all paths are dirs
+ - stat_result.results | map(attribute='stat') | map(attribute='isdir') | unique == [true]
+
+ - name: copy the src again to verify no changes will be made
+ copy:
+ src: '{{ src }}/'
+ remote_src: yes
+ dest: '{{ src }}_dest/'
+ register: copy_result
+ failed_when: copy_result is changed
+
+ - name: clean up src and dest
+ file:
+ path: '{{ item }}'
+ state: absent
+ loop:
+ - '{{ src }}'
+ - '{{ src }}_dest'
diff --git a/test/integration/targets/cron/aliases b/test/integration/targets/cron/aliases
index f2f9ac9..f3703f8 100644
--- a/test/integration/targets/cron/aliases
+++ b/test/integration/targets/cron/aliases
@@ -1,4 +1,3 @@
destructive
shippable/posix/group1
-skip/osx
skip/macos
diff --git a/test/integration/targets/deb822_repository/aliases b/test/integration/targets/deb822_repository/aliases
new file mode 100644
index 0000000..34e2b54
--- /dev/null
+++ b/test/integration/targets/deb822_repository/aliases
@@ -0,0 +1,6 @@
+destructive
+shippable/posix/group1
+skip/freebsd
+skip/osx
+skip/macos
+skip/rhel
diff --git a/test/integration/targets/deb822_repository/meta/main.yml b/test/integration/targets/deb822_repository/meta/main.yml
new file mode 100644
index 0000000..83e789e
--- /dev/null
+++ b/test/integration/targets/deb822_repository/meta/main.yml
@@ -0,0 +1,4 @@
+dependencies:
+ - prepare_tests
+ - role: setup_deb_repo
+ install_repo: false
diff --git a/test/integration/targets/deb822_repository/tasks/install.yml b/test/integration/targets/deb822_repository/tasks/install.yml
new file mode 100644
index 0000000..a5dce43
--- /dev/null
+++ b/test/integration/targets/deb822_repository/tasks/install.yml
@@ -0,0 +1,40 @@
+- name: Create repo to install from
+ deb822_repository:
+ name: ansible-test local
+ uris: file:{{ repodir }}
+ suites:
+ - stable
+ - testing
+ components:
+ - main
+ architectures:
+ - all
+ trusted: yes
+ register: deb822_install_repo
+
+- name: Update apt cache
+ apt:
+ update_cache: yes
+ when: deb822_install_repo is changed
+
+- block:
+ - name: Install package from local repo
+ apt:
+ name: foo=1.0.0
+ register: deb822_install_pkg
+ always:
+ - name: Uninstall foo
+ apt:
+ name: foo
+ state: absent
+ when: deb822_install_pkg is changed
+
+ - name: remove repo
+ deb822_repository:
+ name: ansible-test local
+ state: absent
+
+- assert:
+ that:
+ - deb822_install_repo is changed
+ - deb822_install_pkg is changed
diff --git a/test/integration/targets/deb822_repository/tasks/main.yml b/test/integration/targets/deb822_repository/tasks/main.yml
new file mode 100644
index 0000000..561ef2a
--- /dev/null
+++ b/test/integration/targets/deb822_repository/tasks/main.yml
@@ -0,0 +1,19 @@
+- meta: end_play
+ when: ansible_os_family != 'Debian'
+
+- block:
+ - name: install python3-debian
+ apt:
+ name: python3-debian
+ state: present
+ register: py3_deb_install
+
+ - import_tasks: test.yml
+
+ - import_tasks: install.yml
+ always:
+ - name: uninstall python3-debian
+ apt:
+ name: python3-debian
+ state: absent
+ when: py3_deb_install is changed
diff --git a/test/integration/targets/deb822_repository/tasks/test.yml b/test/integration/targets/deb822_repository/tasks/test.yml
new file mode 100644
index 0000000..4911bb9
--- /dev/null
+++ b/test/integration/targets/deb822_repository/tasks/test.yml
@@ -0,0 +1,229 @@
+- name: Create deb822 repo - check_mode
+ deb822_repository:
+ name: ansible-test focal archive
+ uris: http://us.archive.ubuntu.com/ubuntu
+ suites:
+ - focal
+ - focal-updates
+ components:
+ - main
+ - restricted
+ register: deb822_check_mode_1
+ check_mode: true
+
+- name: Create deb822 repo
+ deb822_repository:
+ name: ansible-test focal archive
+ uris: http://us.archive.ubuntu.com/ubuntu
+ suites:
+ - focal
+ - focal-updates
+ components:
+ - main
+ - restricted
+ date_max_future: 10
+ register: deb822_create_1
+
+- name: Check file mode
+ stat:
+ path: /etc/apt/sources.list.d/ansible-test-focal-archive.sources
+ register: deb822_create_1_stat_1
+
+- name: Create another deb822 repo
+ deb822_repository:
+ name: ansible-test focal security
+ uris: http://security.ubuntu.com/ubuntu
+ suites:
+ - focal-security
+ components:
+ - main
+ - restricted
+ register: deb822_create_2
+
+- name: Create deb822 repo idempotency
+ deb822_repository:
+ name: ansible-test focal archive
+ uris: http://us.archive.ubuntu.com/ubuntu
+ suites:
+ - focal
+ - focal-updates
+ components:
+ - main
+ - restricted
+ date_max_future: 10
+ register: deb822_create_1_idem
+
+- name: Create deb822 repo - check_mode
+ deb822_repository:
+ name: ansible-test focal archive
+ uris: http://us.archive.ubuntu.com/ubuntu
+ suites:
+ - focal
+ - focal-updates
+ components:
+ - main
+ - restricted
+ date_max_future: 10
+ register: deb822_check_mode_2
+ check_mode: yes
+
+- name: Change deb822 repo mode
+ deb822_repository:
+ name: ansible-test focal archive
+ uris: http://us.archive.ubuntu.com/ubuntu
+ suites:
+ - focal
+ - focal-updates
+ components:
+ - main
+ - restricted
+ date_max_future: 10
+ mode: '0600'
+ register: deb822_create_1_mode
+
+- name: Check file mode
+ stat:
+ path: /etc/apt/sources.list.d/ansible-test-focal-archive.sources
+ register: deb822_create_1_stat_2
+
+- assert:
+ that:
+ - deb822_check_mode_1 is changed
+
+ - deb822_check_mode_2 is not changed
+
+ - deb822_create_1 is changed
+ - deb822_create_1.dest == '/etc/apt/sources.list.d/ansible-test-focal-archive.sources'
+ - deb822_create_1.repo|trim == focal_archive_expected
+
+ - deb822_create_1_idem is not changed
+
+ - deb822_create_1_mode is changed
+ - deb822_create_1_stat_1.stat.mode == '0644'
+ - deb822_create_1_stat_2.stat.mode == '0600'
+ vars:
+ focal_archive_expected: |-
+ X-Repolib-Name: ansible-test focal archive
+ URIs: http://us.archive.ubuntu.com/ubuntu
+ Suites: focal focal-updates
+ Components: main restricted
+ Date-Max-Future: 10
+ Types: deb
+
+- name: Remove repos
+ deb822_repository:
+ name: '{{ item }}'
+ state: absent
+ register: remove_repos_1
+ loop:
+ - ansible-test focal archive
+ - ansible-test focal security
+
+- name: Check for repo files
+ stat:
+ path: /etc/apt/sources.list.d/ansible-test-{{ item }}.sources
+ register: remove_stats
+ loop:
+ - focal-archive
+ - focal-security
+
+- assert:
+ that:
+ - remove_repos_1 is changed
+ - remove_stats.results|map(attribute='stat')|selectattr('exists') == []
+
+- name: Add repo with signed_by
+ deb822_repository:
+ name: ansible-test
+ types: deb
+ uris: https://deb.debian.org
+ suites: stable
+ components:
+ - main
+ - contrib
+ - non-free
+ signed_by: |-
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
+
+ mDMEYCQjIxYJKwYBBAHaRw8BAQdAD/P5Nvvnvk66SxBBHDbhRml9ORg1WV5CvzKY
+ CuMfoIS0BmFiY2RlZoiQBBMWCgA4FiEErCIG1VhKWMWo2yfAREZd5NfO31cFAmAk
+ IyMCGyMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQREZd5NfO31fbOwD6ArzS
+ dM0Dkd5h2Ujy1b6KcAaVW9FOa5UNfJ9FFBtjLQEBAJ7UyWD3dZzhvlaAwunsk7DG
+ 3bHcln8DMpIJVXht78sL
+ =IE0r
+ -----END PGP PUBLIC KEY BLOCK-----
+ register: signed_by_inline
+
+- name: Change signed_by to URL
+ deb822_repository:
+ name: ansible-test
+ types: deb
+ uris: https://deb.debian.org
+ suites: stable
+ components:
+ - main
+ - contrib
+ - non-free
+ signed_by: https://ci-files.testing.ansible.com/test/integration/targets/apt_key/apt-key-example-binary.gpg
+ register: signed_by_url
+
+- assert:
+ that:
+ - signed_by_inline.key_filename is none
+ - signed_by_inline.repo|trim == signed_by_inline_expected
+ - signed_by_url is changed
+ - signed_by_url.key_filename == '/etc/apt/keyrings/ansible-test.gpg'
+ - >
+ 'BEGIN' not in signed_by_url.repo
+ vars:
+ signed_by_inline_expected: |-
+ X-Repolib-Name: ansible-test
+ Types: deb
+ URIs: https://deb.debian.org
+ Suites: stable
+ Components: main contrib non-free
+ Signed-By:
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
+ .
+ mDMEYCQjIxYJKwYBBAHaRw8BAQdAD/P5Nvvnvk66SxBBHDbhRml9ORg1WV5CvzKY
+ CuMfoIS0BmFiY2RlZoiQBBMWCgA4FiEErCIG1VhKWMWo2yfAREZd5NfO31cFAmAk
+ IyMCGyMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQREZd5NfO31fbOwD6ArzS
+ dM0Dkd5h2Ujy1b6KcAaVW9FOa5UNfJ9FFBtjLQEBAJ7UyWD3dZzhvlaAwunsk7DG
+ 3bHcln8DMpIJVXht78sL
+ =IE0r
+ -----END PGP PUBLIC KEY BLOCK-----
+
+- name: remove ansible-test repo
+ deb822_repository:
+ name: ansible-test
+ state: absent
+ register: ansible_test_repo_remove
+
+- name: check for ansible-test repo and key
+ stat:
+ path: '{{ item }}'
+ register: ansible_test_repo_stats
+ loop:
+ - /etc/apt/sources.list.d/ansible-test.sources
+ - /etc/apt/keyrings/ansible-test.gpg
+
+- assert:
+ that:
+ - ansible_test_repo_remove is changed
+ - ansible_test_repo_stats.results|map(attribute='stat')|selectattr('exists') == []
+
+- name: Check if http-agent works when using cloudflare repo - check_mode
+ deb822_repository:
+ name: cloudflared
+ types: deb
+ uris: https://pkg.cloudflare.com/cloudflared
+ suites: "bullseye"
+ components: main
+ signed_by: https://pkg.cloudflare.com/cloudflare-main.gpg
+ state: present
+ check_mode: true
+ register: ansible_test_http_agent
+
+- assert:
+ that:
+ - ansible_test_http_agent is changed
diff --git a/test/integration/targets/debconf/tasks/main.yml b/test/integration/targets/debconf/tasks/main.yml
index d3d63cd..f923626 100644
--- a/test/integration/targets/debconf/tasks/main.yml
+++ b/test/integration/targets/debconf/tasks/main.yml
@@ -33,4 +33,44 @@
- 'debconf_test0.current is defined'
- '"tzdata/Zones/Etc" in debconf_test0.current'
- 'debconf_test0.current["tzdata/Zones/Etc"] == "UTC"'
- when: ansible_distribution in ('Ubuntu', 'Debian')
+
+ - name: install debconf-utils
+ apt:
+ name: debconf-utils
+ state: present
+ register: debconf_utils_deb_install
+
+ - name: Check if password is set
+ debconf:
+ name: ddclient
+ question: ddclient/password
+ value: "MySecretValue"
+ vtype: password
+ register: debconf_test1
+
+ - name: validate results for test 1
+ assert:
+ that:
+ - debconf_test1.changed
+
+ - name: Change password again
+ debconf:
+ name: ddclient
+ question: ddclient/password
+ value: "MySecretValue"
+ vtype: password
+ no_log: yes
+ register: debconf_test2
+
+ - name: validate results for test 1
+ assert:
+ that:
+ - not debconf_test2.changed
+ always:
+ - name: uninstall debconf-utils
+ apt:
+ name: debconf-utils
+ state: absent
+ when: debconf_utils_deb_install is changed
+
+ when: ansible_distribution in ('Ubuntu', 'Debian') \ No newline at end of file
diff --git a/test/integration/targets/delegate_to/delegate_local_from_root.yml b/test/integration/targets/delegate_to/delegate_local_from_root.yml
index c9be4ff..b44f83b 100644
--- a/test/integration/targets/delegate_to/delegate_local_from_root.yml
+++ b/test/integration/targets/delegate_to/delegate_local_from_root.yml
@@ -3,7 +3,7 @@
gather_facts: false
remote_user: root
tasks:
- - name: ensure we copy w/o errors due to remote user not being overriden
+ - name: ensure we copy w/o errors due to remote user not being overridden
copy:
src: testfile
dest: "{{ playbook_dir }}"
diff --git a/test/integration/targets/delegate_to/runme.sh b/test/integration/targets/delegate_to/runme.sh
index 1bdf27c..e0dcc74 100755
--- a/test/integration/targets/delegate_to/runme.sh
+++ b/test/integration/targets/delegate_to/runme.sh
@@ -76,3 +76,7 @@ ansible-playbook test_delegate_to_lookup_context.yml -i inventory -v "$@"
ansible-playbook delegate_local_from_root.yml -i inventory -v "$@" -e 'ansible_user=root'
ansible-playbook delegate_with_fact_from_delegate_host.yml "$@"
ansible-playbook delegate_facts_loop.yml -i inventory -v "$@"
+ansible-playbook test_random_delegate_to_with_loop.yml -i inventory -v "$@"
+
+# Run playbook multiple times to ensure there are no false-negatives
+for i in $(seq 0 10); do ansible-playbook test_random_delegate_to_without_loop.yml -i inventory -v "$@"; done;
diff --git a/test/integration/targets/delegate_to/test_delegate_to.yml b/test/integration/targets/delegate_to/test_delegate_to.yml
index dcfa9d0..eb601e0 100644
--- a/test/integration/targets/delegate_to/test_delegate_to.yml
+++ b/test/integration/targets/delegate_to/test_delegate_to.yml
@@ -1,9 +1,9 @@
- hosts: testhost3
vars:
- - template_role: ./roles/test_template
- - output_dir: "{{ playbook_dir }}"
- - templated_var: foo
- - templated_dict: { 'hello': 'world' }
+ template_role: ./roles/test_template
+ output_dir: "{{ playbook_dir }}"
+ templated_var: foo
+ templated_dict: { 'hello': 'world' }
tasks:
- name: Test no delegate_to
setup:
@@ -57,6 +57,25 @@
- name: remove test file
file: path={{ output_dir }}/tmp.txt state=absent
+ - name: Use omit to thwart delegation
+ ping:
+ delegate_to: "{{ jenkins_install_key_on|default(omit) }}"
+ register: d_omitted
+
+ - name: Use empty to thwart delegation should fail
+ ping:
+ delegate_to: "{{ jenkins_install_key_on }}"
+ when: jenkins_install_key_on != ""
+ vars:
+ jenkins_install_key_on: ''
+ ignore_errors: true
+ register: d_empty
+
+ - name: Ensure previous 2 tests actually did what was expected
+ assert:
+ that:
+ - d_omitted is success
+ - d_empty is failed
- name: verify delegation with per host vars
hosts: testhost6
diff --git a/test/integration/targets/delegate_to/test_random_delegate_to_with_loop.yml b/test/integration/targets/delegate_to/test_random_delegate_to_with_loop.yml
new file mode 100644
index 0000000..cd7b888
--- /dev/null
+++ b/test/integration/targets/delegate_to/test_random_delegate_to_with_loop.yml
@@ -0,0 +1,26 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - add_host:
+ name: 'host{{ item }}'
+ groups:
+ - test
+ loop: '{{ range(10) }}'
+
+ # This task may fail, if it does, it means the same thing as if the assert below fails
+ - set_fact:
+ dv: '{{ ansible_delegated_vars[ansible_host]["ansible_host"] }}'
+ delegate_to: '{{ groups.test|random }}'
+ delegate_facts: true
+ # Purposefully smaller loop than group count
+ loop: '{{ range(5) }}'
+
+- hosts: test
+ gather_facts: false
+ tasks:
+ - assert:
+ that:
+ - dv == inventory_hostname
+ # The small loop above means we won't set this var for every host
+ # and a smaller loop is faster, and may catch the error in the above task
+ when: dv is defined
diff --git a/test/integration/targets/delegate_to/test_random_delegate_to_without_loop.yml b/test/integration/targets/delegate_to/test_random_delegate_to_without_loop.yml
new file mode 100644
index 0000000..9527862
--- /dev/null
+++ b/test/integration/targets/delegate_to/test_random_delegate_to_without_loop.yml
@@ -0,0 +1,13 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - add_host:
+ name: 'host{{ item }}'
+ groups:
+ - test
+ loop: '{{ range(10) }}'
+
+ - set_fact:
+ dv: '{{ ansible_delegated_vars[ansible_host]["ansible_host"] }}'
+ delegate_to: '{{ groups.test|random }}'
+ delegate_facts: true
diff --git a/test/integration/targets/dnf/aliases b/test/integration/targets/dnf/aliases
index d6f27b8..b12f354 100644
--- a/test/integration/targets/dnf/aliases
+++ b/test/integration/targets/dnf/aliases
@@ -1,6 +1,4 @@
destructive
shippable/posix/group1
-skip/power/centos
skip/freebsd
-skip/osx
skip/macos
diff --git a/test/integration/targets/dnf/tasks/dnf.yml b/test/integration/targets/dnf/tasks/dnf.yml
index ec1c36f..9845f3d 100644
--- a/test/integration/targets/dnf/tasks/dnf.yml
+++ b/test/integration/targets/dnf/tasks/dnf.yml
@@ -224,7 +224,7 @@
- assert:
that:
- dnf_result is success
- - dnf_result.results|length == 2
+ - dnf_result.results|length >= 2
- "dnf_result.results[0].startswith('Removed: ')"
- "dnf_result.results[1].startswith('Removed: ')"
@@ -427,6 +427,10 @@
- shell: 'dnf -y group install "Custom Group" && dnf -y group remove "Custom Group"'
register: shell_dnf_result
+- dnf:
+ name: "@Custom Group"
+ state: absent
+
# GROUP UPGRADE - this will go to the same method as group install
# but through group_update - it is its invocation we're testing here
# see commit 119c9e5d6eb572c4a4800fbe8136095f9063c37b
@@ -446,6 +450,10 @@
# cleanup until https://github.com/ansible/ansible/issues/27377 is resolved
- shell: dnf -y group install "Custom Group" && dnf -y group remove "Custom Group"
+- dnf:
+ name: "@Custom Group"
+ state: absent
+
- name: try to install non existing group
dnf:
name: "@non-existing-group"
@@ -551,30 +559,35 @@
- "'No package non-existent-rpm available' in dnf_result['failures'][0]"
- "'Failed to install some of the specified packages' in dnf_result['msg']"
-- name: use latest to install httpd
+- name: ensure sos isn't installed
dnf:
- name: httpd
+ name: sos
+ state: absent
+
+- name: use latest to install sos
+ dnf:
+ name: sos
state: latest
register: dnf_result
-- name: verify httpd was installed
+- name: verify sos was installed
assert:
that:
- - "'changed' in dnf_result"
+ - dnf_result is changed
-- name: uninstall httpd
+- name: uninstall sos
dnf:
- name: httpd
+ name: sos
state: removed
-- name: update httpd only if it exists
+- name: update sos only if it exists
dnf:
- name: httpd
+ name: sos
state: latest
update_only: yes
register: dnf_result
-- name: verify httpd not installed
+- name: verify sos not installed
assert:
that:
- "not dnf_result is changed"
@@ -655,6 +668,28 @@
- "'changed' in dnf_result"
- "'results' in dnf_result"
+# Install RPM from url with update_only
+- name: install from url with update_only
+ dnf:
+ name: "file://{{ pkg_path }}"
+ state: latest
+ update_only: true
+ disable_gpg_check: true
+ register: dnf_result
+
+- name: verify installation
+ assert:
+ that:
+ - "dnf_result is success"
+ - "not dnf_result is changed"
+ - "dnf_result is not failed"
+
+- name: verify dnf module outputs
+ assert:
+ that:
+ - "'changed' in dnf_result"
+ - "'results' in dnf_result"
+
- name: Create a temp RPM file which does not contain nevra information
file:
name: "/tmp/non_existent_pkg.rpm"
diff --git a/test/integration/targets/dnf/tasks/main.yml b/test/integration/targets/dnf/tasks/main.yml
index 65b77ce..4941e2c 100644
--- a/test/integration/targets/dnf/tasks/main.yml
+++ b/test/integration/targets/dnf/tasks/main.yml
@@ -61,6 +61,7 @@
when:
- (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('29', '>=')) or
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
+ - not dnf5|default(false)
tags:
- dnf_modularity
@@ -69,5 +70,6 @@
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
- include_tasks: cacheonly.yml
- when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or
- (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
+ when:
+ - (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or
+ (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
diff --git a/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml b/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml
index 503cb4c..f54c0a8 100644
--- a/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml
+++ b/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml
@@ -240,7 +240,8 @@
- name: Do an "upgrade" to an older version of broken-a, allow_downgrade=false
dnf:
name:
- - broken-a-1.2.3-1*
+ #- broken-a-1.2.3-1*
+ - broken-a-1.2.3-1.el7.x86_64
state: latest
allow_downgrade: false
check_mode: true
diff --git a/test/integration/targets/dnf/tasks/test_sos_removal.yml b/test/integration/targets/dnf/tasks/test_sos_removal.yml
index 0d70cf7..5e161db 100644
--- a/test/integration/targets/dnf/tasks/test_sos_removal.yml
+++ b/test/integration/targets/dnf/tasks/test_sos_removal.yml
@@ -15,5 +15,5 @@
that:
- sos_rm is successful
- sos_rm is changed
- - "'Removed: sos-' ~ sos_version ~ '-' ~ sos_release in sos_rm.results[0]"
- - sos_rm.results|length == 1
+ - sos_rm.results|select("contains", "Removed: sos-{{ sos_version }}-{{ sos_release }}")|length > 0
+ - sos_rm.results|length > 0
diff --git a/test/integration/targets/dnf5/aliases b/test/integration/targets/dnf5/aliases
new file mode 100644
index 0000000..4baf6e6
--- /dev/null
+++ b/test/integration/targets/dnf5/aliases
@@ -0,0 +1,6 @@
+destructive
+shippable/posix/group1
+skip/freebsd
+skip/macos
+context/target
+needs/target/dnf
diff --git a/test/integration/targets/dnf5/playbook.yml b/test/integration/targets/dnf5/playbook.yml
new file mode 100644
index 0000000..16dfd22
--- /dev/null
+++ b/test/integration/targets/dnf5/playbook.yml
@@ -0,0 +1,19 @@
+- hosts: localhost
+ tasks:
+ - block:
+ - command: "dnf install -y 'dnf-command(copr)'"
+ - command: dnf copr enable -y rpmsoftwaremanagement/dnf5-unstable
+ - command: dnf install -y python3-libdnf5
+
+ - include_role:
+ name: dnf
+ vars:
+ dnf5: true
+ dnf_log_files:
+ - /var/log/dnf5.log
+ when:
+ - ansible_distribution == 'Fedora'
+ - ansible_distribution_major_version is version('37', '>=')
+ module_defaults:
+ dnf:
+ use_backend: dnf5
diff --git a/test/integration/targets/dnf5/runme.sh b/test/integration/targets/dnf5/runme.sh
new file mode 100755
index 0000000..51a6bf4
--- /dev/null
+++ b/test/integration/targets/dnf5/runme.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -ux
+export ANSIBLE_ROLES_PATH=../
+ansible-playbook playbook.yml "$@"
diff --git a/test/integration/targets/dpkg_selections/aliases b/test/integration/targets/dpkg_selections/aliases
index c0d5684..9c44d75 100644
--- a/test/integration/targets/dpkg_selections/aliases
+++ b/test/integration/targets/dpkg_selections/aliases
@@ -1,6 +1,5 @@
shippable/posix/group1
destructive
skip/freebsd
-skip/osx
skip/macos
skip/rhel
diff --git a/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml b/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml
index 080db26..016d771 100644
--- a/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml
+++ b/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml
@@ -87,3 +87,15 @@
apt:
name: hello
state: absent
+
+- name: Try to select non-existent package
+ dpkg_selections:
+ name: kernel
+ selection: hold
+ ignore_errors: yes
+ register: result
+
+- name: Check that module fails for non-existent package
+ assert:
+ that:
+ - "'Failed to find package' in result.msg"
diff --git a/test/integration/targets/egg-info/lookup_plugins/import_pkg_resources.py b/test/integration/targets/egg-info/lookup_plugins/import_pkg_resources.py
index c0c5ccd..28227fc 100644
--- a/test/integration/targets/egg-info/lookup_plugins/import_pkg_resources.py
+++ b/test/integration/targets/egg-info/lookup_plugins/import_pkg_resources.py
@@ -1,7 +1,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import pkg_resources
+import pkg_resources # pylint: disable=unused-import
from ansible.plugins.lookup import LookupBase
diff --git a/test/integration/targets/environment/test_environment.yml b/test/integration/targets/environment/test_environment.yml
index 43f9c74..f295cf3 100644
--- a/test/integration/targets/environment/test_environment.yml
+++ b/test/integration/targets/environment/test_environment.yml
@@ -7,8 +7,8 @@
- hosts: testhost
vars:
- - test1:
- key1: val1
+ test1:
+ key1: val1
environment:
PATH: '{{ansible_env.PATH + ":/lola"}}'
lola: 'ido'
@@ -41,9 +41,9 @@
- hosts: testhost
vars:
- - test1:
- key1: val1
- - test2:
+ test1:
+ key1: val1
+ test2:
key1: not1
other1: val2
environment: "{{test1}}"
diff --git a/test/integration/targets/error_from_connection/connection_plugins/dummy.py b/test/integration/targets/error_from_connection/connection_plugins/dummy.py
index 59a81a1..d322fe0 100644
--- a/test/integration/targets/error_from_connection/connection_plugins/dummy.py
+++ b/test/integration/targets/error_from_connection/connection_plugins/dummy.py
@@ -11,7 +11,6 @@ DOCUMENTATION = """
version_added: "2.0"
options: {}
"""
-import ansible.constants as C
from ansible.errors import AnsibleError
from ansible.plugins.connection import ConnectionBase
diff --git a/test/integration/targets/expect/tasks/main.yml b/test/integration/targets/expect/tasks/main.yml
index 7bf18c5..2aef595 100644
--- a/test/integration/targets/expect/tasks/main.yml
+++ b/test/integration/targets/expect/tasks/main.yml
@@ -148,6 +148,15 @@
- "echo_result.stdout_lines[-2] == 'foobar'"
- "echo_result.stdout_lines[-1] == 'bar'"
+- name: test timeout is valid as null
+ expect:
+ command: "{{ansible_python_interpreter}} {{test_command_file}}"
+ responses:
+ foo: bar
+ echo: true
+ timeout: null # wait indefinitely
+ timeout: 2 # but shouldn't be waiting long
+
- name: test response list
expect:
command: "{{ansible_python_interpreter}} {{test_command_file}} foo foo"
diff --git a/test/integration/targets/facts_linux_network/aliases b/test/integration/targets/facts_linux_network/aliases
index 100ce23..c9e1dc5 100644
--- a/test/integration/targets/facts_linux_network/aliases
+++ b/test/integration/targets/facts_linux_network/aliases
@@ -1,7 +1,6 @@
needs/privileged
shippable/posix/group1
skip/freebsd
-skip/osx
skip/macos
context/target
destructive
diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml
index 8a6b5b7..d0bf9bd 100644
--- a/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml
+++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml
@@ -28,6 +28,15 @@
register: failed_fetch_dest_dir
ignore_errors: true
+- name: Test unreachable
+ fetch:
+ src: "{{ remote_tmp_dir }}/orig"
+ dest: "{{ output_dir }}"
+ register: unreachable_fetch
+ ignore_unreachable: true
+ vars:
+ ansible_user: wrong
+
- name: Ensure fetch failed
assert:
that:
@@ -39,3 +48,4 @@
- failed_fetch_no_access.msg is search('file is not readable')
- failed_fetch_dest_dir is failed
- failed_fetch_dest_dir.msg is search('dest is an existing directory')
+ - unreachable_fetch is unreachable
diff --git a/test/integration/targets/file/tasks/link_rewrite.yml b/test/integration/targets/file/tasks/link_rewrite.yml
index b0e1af3..2416c2c 100644
--- a/test/integration/targets/file/tasks/link_rewrite.yml
+++ b/test/integration/targets/file/tasks/link_rewrite.yml
@@ -16,11 +16,11 @@
dest: "{{ tempdir.path }}/somelink"
state: link
-- stat:
+- stat:
path: "{{ tempdir.path }}/somelink"
register: link
-- stat:
+- stat:
path: "{{ tempdir.path }}/somefile"
register: file
@@ -32,12 +32,12 @@
- file:
path: "{{ tempdir.path }}/somelink"
mode: 0644
-
-- stat:
+
+- stat:
path: "{{ tempdir.path }}/somelink"
register: link
-- stat:
+- stat:
path: "{{ tempdir.path }}/somefile"
register: file
diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml
index a5bd68d..c1b4c79 100644
--- a/test/integration/targets/file/tasks/main.yml
+++ b/test/integration/targets/file/tasks/main.yml
@@ -779,7 +779,7 @@
register: touch_result_in_check_mode_fails_not_existing_group
- assert:
- that:
+ that:
- touch_result_in_check_mode_not_existing.changed
- touch_result_in_check_mode_preserve_access_time.changed
- touch_result_in_check_mode_change_only_mode.changed
diff --git a/test/integration/targets/filter_core/tasks/main.yml b/test/integration/targets/filter_core/tasks/main.yml
index 2d08419..9d287a1 100644
--- a/test/integration/targets/filter_core/tasks/main.yml
+++ b/test/integration/targets/filter_core/tasks/main.yml
@@ -454,6 +454,38 @@
- password_hash_2 is failed
- "'not support' in password_hash_2.msg"
+- name: install passlib if needed
+ pip:
+ name: passlib
+ state: present
+ register: installed_passlib
+
+- name: test using passlib with an unsupported hash type
+ set_fact:
+ foo: '{{"hey"|password_hash("msdcc")}}'
+ ignore_errors: yes
+ register: unsupported_hash_type
+
+- name: remove passlib if it was installed
+ pip:
+ name: passlib
+ state: absent
+ when: installed_passlib.changed
+
+- assert:
+ that:
+ - unsupported_hash_type.msg == msg
+ vars:
+ msg: "msdcc is not in the list of supported passlib algorithms: md5, blowfish, sha256, sha512"
+
+- name: test password_hash can work with bcrypt without passlib installed
+ debug:
+ msg: "{{ 'somestring'|password_hash('bcrypt') }}"
+ register: crypt_bcrypt
+ # Some implementations of crypt do not fail outright and return some short value.
+ failed_when: crypt_bcrypt is failed or (crypt_bcrypt.msg|length|int) != 60
+ when: ansible_facts.os_family in ['RedHat', 'Debian']
+
- name: Verify to_uuid throws on weird namespace
set_fact:
foo: '{{"hey"|to_uuid(namespace=22)}}'
diff --git a/test/integration/targets/filter_encryption/base.yml b/test/integration/targets/filter_encryption/base.yml
index 8bf25f7..1479f73 100644
--- a/test/integration/targets/filter_encryption/base.yml
+++ b/test/integration/targets/filter_encryption/base.yml
@@ -2,6 +2,7 @@
gather_facts: true
vars:
data: secret
+ data2: 'foo: bar\n'
dvault: '{{ "secret"|vault("test")}}'
password: test
s_32: '{{(2**31-1)}}'
@@ -21,6 +22,15 @@
is_64: '{{ "64" in ansible_facts["architecture"] }}'
salt: '{{ is_64|bool|ternary(s_64, s_32)|random(seed=inventory_hostname)}}'
vaultedstring: '{{ is_64|bool|ternary(vaultedstring_64, vaultedstring_32) }}'
+ # command line vaulted data2
+ vaulted_id: !vault |
+ $ANSIBLE_VAULT;1.2;AES256;test1
+ 36383733336533656264393332663131613335333332346439356164383935656234663631356430
+ 3533353537343834333538356366376233326364613362640a623832636339363966336238393039
+ 35316562626335306534356162623030613566306235623863373036626531346364626166656134
+ 3063376436656635330a363636376131663362633731313964353061663661376638326461393736
+ 3863
+ vaulted_to_id: "{{data2|vault('test1@secret', vault_id='test1')}}"
tasks:
- name: check vaulting
@@ -35,3 +45,5 @@
that:
- vaultedstring|unvault(password) == data
- vault|unvault(password) == data
+ - vaulted_id|unvault('test1@secret', vault_id='test1')
+ - vaulted_to_id|unvault('test1@secret', vault_id='test1')
diff --git a/test/integration/targets/filter_mathstuff/tasks/main.yml b/test/integration/targets/filter_mathstuff/tasks/main.yml
index 019f00e..33fcae8 100644
--- a/test/integration/targets/filter_mathstuff/tasks/main.yml
+++ b/test/integration/targets/filter_mathstuff/tasks/main.yml
@@ -64,44 +64,44 @@
that:
- '[1,2,3]|intersect([4,5,6]) == []'
- '[1,2,3]|intersect([3,4,5,6]) == [3]'
- - '[1,2,3]|intersect([3,2,1]) == [1,2,3]'
- - '(1,2,3)|intersect((4,5,6))|list == []'
- - '(1,2,3)|intersect((3,4,5,6))|list == [3]'
+ - '[1,2,3]|intersect([3,2,1]) | sort == [1,2,3]'
+ - '(1,2,3)|intersect((4,5,6)) == []'
+ - '(1,2,3)|intersect((3,4,5,6)) == [3]'
- '["a","A","b"]|intersect(["B","c","C"]) == []'
- '["a","A","b"]|intersect(["b","B","c","C"]) == ["b"]'
- - '["a","A","b"]|intersect(["b","A","a"]) == ["a","A","b"]'
- - '("a","A","b")|intersect(("B","c","C"))|list == []'
- - '("a","A","b")|intersect(("b","B","c","C"))|list == ["b"]'
+ - '["a","A","b"]|intersect(["b","A","a"]) | sort(case_sensitive=True) == ["A","a","b"]'
+ - '("a","A","b")|intersect(("B","c","C")) == []'
+ - '("a","A","b")|intersect(("b","B","c","C")) == ["b"]'
- name: Verify difference
tags: difference
assert:
that:
- - '[1,2,3]|difference([4,5,6]) == [1,2,3]'
- - '[1,2,3]|difference([3,4,5,6]) == [1,2]'
+ - '[1,2,3]|difference([4,5,6]) | sort == [1,2,3]'
+ - '[1,2,3]|difference([3,4,5,6]) | sort == [1,2]'
- '[1,2,3]|difference([3,2,1]) == []'
- - '(1,2,3)|difference((4,5,6))|list == [1,2,3]'
- - '(1,2,3)|difference((3,4,5,6))|list == [1,2]'
- - '["a","A","b"]|difference(["B","c","C"]) == ["a","A","b"]'
- - '["a","A","b"]|difference(["b","B","c","C"]) == ["a","A"]'
+ - '(1,2,3)|difference((4,5,6)) | sort == [1,2,3]'
+ - '(1,2,3)|difference((3,4,5,6)) | sort == [1,2]'
+ - '["a","A","b"]|difference(["B","c","C"]) | sort(case_sensitive=True) == ["A","a","b"]'
+ - '["a","A","b"]|difference(["b","B","c","C"]) | sort(case_sensitive=True) == ["A","a"]'
- '["a","A","b"]|difference(["b","A","a"]) == []'
- - '("a","A","b")|difference(("B","c","C"))|list|sort(case_sensitive=True) == ["A","a","b"]'
- - '("a","A","b")|difference(("b","B","c","C"))|list|sort(case_sensitive=True) == ["A","a"]'
+ - '("a","A","b")|difference(("B","c","C")) | sort(case_sensitive=True) == ["A","a","b"]'
+ - '("a","A","b")|difference(("b","B","c","C")) | sort(case_sensitive=True) == ["A","a"]'
- name: Verify symmetric_difference
tags: symmetric_difference
assert:
that:
- - '[1,2,3]|symmetric_difference([4,5,6]) == [1,2,3,4,5,6]'
- - '[1,2,3]|symmetric_difference([3,4,5,6]) == [1,2,4,5,6]'
+ - '[1,2,3]|symmetric_difference([4,5,6]) | sort == [1,2,3,4,5,6]'
+ - '[1,2,3]|symmetric_difference([3,4,5,6]) | sort == [1,2,4,5,6]'
- '[1,2,3]|symmetric_difference([3,2,1]) == []'
- - '(1,2,3)|symmetric_difference((4,5,6))|list == [1,2,3,4,5,6]'
- - '(1,2,3)|symmetric_difference((3,4,5,6))|list == [1,2,4,5,6]'
- - '["a","A","b"]|symmetric_difference(["B","c","C"]) == ["a","A","b","B","c","C"]'
- - '["a","A","b"]|symmetric_difference(["b","B","c","C"]) == ["a","A","B","c","C"]'
+ - '(1,2,3)|symmetric_difference((4,5,6)) | sort == [1,2,3,4,5,6]'
+ - '(1,2,3)|symmetric_difference((3,4,5,6)) | sort == [1,2,4,5,6]'
+ - '["a","A","b"]|symmetric_difference(["B","c","C"]) | sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
+ - '["a","A","b"]|symmetric_difference(["b","B","c","C"]) | sort(case_sensitive=True) == ["A","B","C","a","c"]'
- '["a","A","b"]|symmetric_difference(["b","A","a"]) == []'
- - '("a","A","b")|symmetric_difference(("B","c","C"))|list|sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
- - '("a","A","b")|symmetric_difference(("b","B","c","C"))|list|sort(case_sensitive=True) == ["A","B","C","a","c"]'
+ - '("a","A","b")|symmetric_difference(("B","c","C")) | sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
+ - '("a","A","b")|symmetric_difference(("b","B","c","C")) | sort(case_sensitive=True) == ["A","B","C","a","c"]'
- name: Verify union
tags: union
@@ -112,11 +112,11 @@
- '[1,2,3]|union([3,2,1]) == [1,2,3]'
- '(1,2,3)|union((4,5,6))|list == [1,2,3,4,5,6]'
- '(1,2,3)|union((3,4,5,6))|list == [1,2,3,4,5,6]'
- - '["a","A","b"]|union(["B","c","C"]) == ["a","A","b","B","c","C"]'
- - '["a","A","b"]|union(["b","B","c","C"]) == ["a","A","b","B","c","C"]'
- - '["a","A","b"]|union(["b","A","a"]) == ["a","A","b"]'
- - '("a","A","b")|union(("B","c","C"))|list|sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
- - '("a","A","b")|union(("b","B","c","C"))|list|sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
+ - '["a","A","b"]|union(["B","c","C"]) | sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
+ - '["a","A","b"]|union(["b","B","c","C"]) | sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
+ - '["a","A","b"]|union(["b","A","a"]) | sort(case_sensitive=True) == ["A","a","b"]'
+ - '("a","A","b")|union(("B","c","C")) | sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
+ - '("a","A","b")|union(("b","B","c","C")) | sort(case_sensitive=True) == ["A","B","C","a","b","c"]'
- name: Verify min
tags: min
diff --git a/test/integration/targets/find/tasks/main.yml b/test/integration/targets/find/tasks/main.yml
index 89c62b9..9c4a960 100644
--- a/test/integration/targets/find/tasks/main.yml
+++ b/test/integration/targets/find/tasks/main.yml
@@ -374,3 +374,6 @@
- 'remote_tmp_dir_test ~ "/astest/old.txt" in astest_list'
- 'remote_tmp_dir_test ~ "/astest/.hidden.txt" in astest_list'
- '"checksum" in result.files[0]'
+
+- name: Run mode tests
+ import_tasks: mode.yml
diff --git a/test/integration/targets/find/tasks/mode.yml b/test/integration/targets/find/tasks/mode.yml
new file mode 100644
index 0000000..1c900ea
--- /dev/null
+++ b/test/integration/targets/find/tasks/mode.yml
@@ -0,0 +1,68 @@
+- name: create test files for mode matching
+ file:
+ path: '{{ remote_tmp_dir_test }}/mode_{{ item }}'
+ state: touch
+ mode: '{{ item }}'
+ loop:
+ - '0644'
+ - '0444'
+ - '0400'
+ - '0700'
+ - '0666'
+
+- name: exact mode octal
+ find:
+ path: '{{ remote_tmp_dir_test }}'
+ pattern: 'mode_*'
+ mode: '0644'
+ exact_mode: true
+ register: exact_mode_0644
+
+- name: exact mode symbolic
+ find:
+ path: '{{ remote_tmp_dir_test }}'
+ pattern: 'mode_*'
+ mode: 'u=rw,g=r,o=r'
+ exact_mode: true
+ register: exact_mode_0644_symbolic
+
+- name: find all user readable files octal
+ find:
+ path: '{{ remote_tmp_dir_test }}'
+ pattern: 'mode_*'
+ mode: '0400'
+ exact_mode: false
+ register: user_readable_octal
+
+- name: find all user readable files symbolic
+ find:
+ path: '{{ remote_tmp_dir_test }}'
+ pattern: 'mode_*'
+ mode: 'u=r'
+ exact_mode: false
+ register: user_readable_symbolic
+
+- name: all other readable files octal
+ find:
+ path: '{{ remote_tmp_dir_test }}'
+ pattern: 'mode_*'
+ mode: '0004'
+ exact_mode: false
+ register: other_readable_octal
+
+- name: all other readable files symbolic
+ find:
+ path: '{{ remote_tmp_dir_test }}'
+ pattern: 'mode_*'
+ mode: 'o=r'
+ exact_mode: false
+ register: other_readable_symbolic
+
+- assert:
+ that:
+ - exact_mode_0644.files == exact_mode_0644_symbolic.files
+ - exact_mode_0644.files[0].path == remote_tmp_dir_test ~ '/mode_0644'
+ - user_readable_octal.files == user_readable_symbolic.files
+ - user_readable_octal.files|map(attribute='path')|map('basename')|sort == ['mode_0400', 'mode_0444', 'mode_0644', 'mode_0666', 'mode_0700']
+ - other_readable_octal.files == other_readable_symbolic.files
+ - other_readable_octal.files|map(attribute='path')|map('basename')|sort == ['mode_0444', 'mode_0644', 'mode_0666']
diff --git a/test/integration/targets/fork_safe_stdio/aliases b/test/integration/targets/fork_safe_stdio/aliases
index e968db7..7761837 100644
--- a/test/integration/targets/fork_safe_stdio/aliases
+++ b/test/integration/targets/fork_safe_stdio/aliases
@@ -1,3 +1,3 @@
shippable/posix/group3
context/controller
-skip/macos
+needs/target/test_utils
diff --git a/test/integration/targets/fork_safe_stdio/runme.sh b/test/integration/targets/fork_safe_stdio/runme.sh
index 4438c3f..863582f 100755
--- a/test/integration/targets/fork_safe_stdio/runme.sh
+++ b/test/integration/targets/fork_safe_stdio/runme.sh
@@ -7,7 +7,7 @@ echo "testing for stdio deadlock on forked workers (10s timeout)..."
# Enable a callback that trips deadlocks on forked-child stdout, time out after 10s; forces running
# in a pty, since that tends to be much slower than raw file I/O and thus more likely to trigger the deadlock.
# Redirect stdout to /dev/null since it's full of non-printable garbage we don't want to display unless it failed
-ANSIBLE_CALLBACKS_ENABLED=spewstdio SPEWSTDIO_ENABLED=1 python run-with-pty.py timeout 10s ansible-playbook -i hosts -f 5 test.yml > stdout.txt && RC=$? || RC=$?
+ANSIBLE_CALLBACKS_ENABLED=spewstdio SPEWSTDIO_ENABLED=1 python run-with-pty.py ../test_utils/scripts/timeout.py -- 10 ansible-playbook -i hosts -f 5 test.yml > stdout.txt && RC=$? || RC=$?
if [ $RC != 0 ]; then
echo "failed; likely stdout deadlock. dumping raw output (may be very large)"
diff --git a/test/integration/targets/gathering_facts/library/dummy1 b/test/integration/targets/gathering_facts/library/dummy1
new file mode 100755
index 0000000..5a10e2d
--- /dev/null
+++ b/test/integration/targets/gathering_facts/library/dummy1
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+CANARY="${OUTPUT_DIR}/canary.txt"
+
+echo "$0" >> "${CANARY}"
+LINES=0
+
+until test "${LINES}" -gt 2
+do
+ LINES=`wc -l "${CANARY}" |awk '{print $1}'`
+ sleep 1
+done
+
+echo '{
+ "changed": false,
+ "ansible_facts": {
+ "dummy": "$0"
+ }
+}'
diff --git a/test/integration/targets/gathering_facts/library/dummy2 b/test/integration/targets/gathering_facts/library/dummy2
new file mode 100755
index 0000000..5a10e2d
--- /dev/null
+++ b/test/integration/targets/gathering_facts/library/dummy2
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+CANARY="${OUTPUT_DIR}/canary.txt"
+
+echo "$0" >> "${CANARY}"
+LINES=0
+
+until test "${LINES}" -gt 2
+do
+ LINES=`wc -l "${CANARY}" |awk '{print $1}'`
+ sleep 1
+done
+
+echo '{
+ "changed": false,
+ "ansible_facts": {
+ "dummy": "$0"
+ }
+}'
diff --git a/test/integration/targets/gathering_facts/library/dummy3 b/test/integration/targets/gathering_facts/library/dummy3
new file mode 100755
index 0000000..5a10e2d
--- /dev/null
+++ b/test/integration/targets/gathering_facts/library/dummy3
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+CANARY="${OUTPUT_DIR}/canary.txt"
+
+echo "$0" >> "${CANARY}"
+LINES=0
+
+until test "${LINES}" -gt 2
+do
+ LINES=`wc -l "${CANARY}" |awk '{print $1}'`
+ sleep 1
+done
+
+echo '{
+ "changed": false,
+ "ansible_facts": {
+ "dummy": "$0"
+ }
+}'
diff --git a/test/integration/targets/gathering_facts/library/file_utils.py b/test/integration/targets/gathering_facts/library/file_utils.py
index 5853802..38fa926 100644
--- a/test/integration/targets/gathering_facts/library/file_utils.py
+++ b/test/integration/targets/gathering_facts/library/file_utils.py
@@ -1,9 +1,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
-import sys
-
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.facts.utils import (
get_file_content,
diff --git a/test/integration/targets/gathering_facts/library/slow b/test/integration/targets/gathering_facts/library/slow
new file mode 100644
index 0000000..3984662
--- /dev/null
+++ b/test/integration/targets/gathering_facts/library/slow
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+sleep 10
+
+echo '{
+ "changed": false,
+ "ansible_facts": {
+ "factsone": "from slow module",
+ "common_fact": "also from slow module",
+ "common_dict_fact": {
+ "key_one": "from slow ",
+ "key_two": "from slow "
+ },
+ "common_list_fact": [
+ "never",
+ "does",
+ "see"
+ ],
+ "common_list_fact2": [
+ "see",
+ "does",
+ "never",
+ "theee"
+ ]
+ }
+}'
diff --git a/test/integration/targets/gathering_facts/runme.sh b/test/integration/targets/gathering_facts/runme.sh
index c1df560..a90de0f 100755
--- a/test/integration/targets/gathering_facts/runme.sh
+++ b/test/integration/targets/gathering_facts/runme.sh
@@ -25,3 +25,17 @@ ansible-playbook test_module_defaults.yml "$@" --tags default_fact_module
ANSIBLE_FACTS_MODULES='ansible.legacy.setup' ansible-playbook test_module_defaults.yml "$@" --tags custom_fact_module
ansible-playbook test_module_defaults.yml "$@" --tags networking
+
+# test it works by default
+ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ "$@"
+
+# test that gather_facts will timeout parallel modules that dont support gather_timeout when using gather_Timeout
+ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ -a 'gather_timeout=1 parallel=true' "$@" 2>&1 |grep 'Timeout exceeded'
+
+# test that gather_facts parallel w/o timing out
+ANSIBLE_FACTS_MODULES='ansible.legacy.slow' ansible -m gather_facts localhost --playbook-dir ./ -a 'gather_timeout=30 parallel=true' "$@" 2>&1 |grep -v 'Timeout exceeded'
+
+
+# test parallelism
+ANSIBLE_FACTS_MODULES='dummy1,dummy2,dummy3' ansible -m gather_facts localhost --playbook-dir ./ -a 'gather_timeout=30 parallel=true' "$@" 2>&1
+rm "${OUTPUT_DIR}/canary.txt"
diff --git a/test/integration/targets/get_url/tasks/hashlib.yml b/test/integration/targets/get_url/tasks/hashlib.yml
new file mode 100644
index 0000000..cc50ad7
--- /dev/null
+++ b/test/integration/targets/get_url/tasks/hashlib.yml
@@ -0,0 +1,20 @@
+- name: "Set hash algorithms to test"
+ set_fact:
+ algorithms:
+ sha256: b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006
+ sha384: 298553d31087fd3f6659801d2e5cde3ff63fad609dc50ad8e194dde80bfb8a084edfa761f025928448f39d720fce55f2
+ sha512: 69b589f7775fe04244e8a9db216a3c91db1680baa33ccd0c317b8d7f0334433f7362d00c8080b3365bf08d532956ba01dbebc497b51ced8f8b05a44a66b854bf
+ sha3_256: 64e5ea73a2f799f35abd0b1242df5e70c84248c9883f89343d4cd5f6d493a139
+ sha3_384: 976edebcb496ad8be0f7fa4411cc8e2404e7e65f1088fabf7be44484458726c61d4985bdaeff8700008ed1670a9b982d
+ sha3_512: f8cca1d98e750e2c2ab44954dc9f1b6e8e35ace71ffcc1cd21c7770eb8eccfbd77d40b2d7d145120efbbb781599294ccc6148c6cda1aa66146363e5fdddd2336
+
+- name: "Verify various checksum algorithms work"
+ get_url:
+ url: 'http://localhost:{{ http_port }}/27617.txt' # content is 'ptux'
+ dest: '{{ remote_tmp_dir }}/27617.{{ algorithm }}.txt'
+ checksum: "{{ algorithm }}:{{ algorithms[algorithm] }}"
+ force: yes
+ loop: "{{ algorithms.keys() }}"
+ loop_control:
+ loop_var: algorithm
+ when: ansible_python_version.startswith('3.') or not algorithm.startswith('sha3_')
diff --git a/test/integration/targets/get_url/tasks/main.yml b/test/integration/targets/get_url/tasks/main.yml
index 09814c7..c26cc08 100644
--- a/test/integration/targets/get_url/tasks/main.yml
+++ b/test/integration/targets/get_url/tasks/main.yml
@@ -398,6 +398,8 @@
port: '{{ http_port }}'
state: started
+- include_tasks: hashlib.yml
+
- name: download src with sha1 checksum url in check mode
get_url:
url: 'http://localhost:{{ http_port }}/27617.txt'
diff --git a/test/integration/targets/get_url/tasks/use_netrc.yml b/test/integration/targets/get_url/tasks/use_netrc.yml
index e1852a8..234c904 100644
--- a/test/integration/targets/get_url/tasks/use_netrc.yml
+++ b/test/integration/targets/get_url/tasks/use_netrc.yml
@@ -22,7 +22,7 @@
register: response_failed
- name: Parse token from msg.txt
- set_fact:
+ set_fact:
token: "{{ (response_failed['content'] | b64decode | from_json).token }}"
- name: assert Test Bearer authorization is failed with netrc
@@ -48,7 +48,7 @@
register: response
- name: Parse token from msg.txt
- set_fact:
+ set_fact:
token: "{{ (response['content'] | b64decode | from_json).token }}"
- name: assert Test Bearer authorization is successfull with use_netrc=False
@@ -64,4 +64,4 @@
state: absent
with_items:
- "{{ remote_tmp_dir }}/netrc"
- - "{{ remote_tmp_dir }}/msg.txt" \ No newline at end of file
+ - "{{ remote_tmp_dir }}/msg.txt"
diff --git a/test/integration/targets/git/tasks/depth.yml b/test/integration/targets/git/tasks/depth.yml
index e0585ca..20f1b4e 100644
--- a/test/integration/targets/git/tasks/depth.yml
+++ b/test/integration/targets/git/tasks/depth.yml
@@ -95,14 +95,16 @@
repo: 'file://{{ repo_dir|expanduser }}/shallow'
dest: '{{ checkout_dir }}'
depth: 1
- version: master
+ version: >-
+ {{ git_default_branch }}
- name: DEPTH | run a second time (now fetch, not clone)
git:
repo: 'file://{{ repo_dir|expanduser }}/shallow'
dest: '{{ checkout_dir }}'
depth: 1
- version: master
+ version: >-
+ {{ git_default_branch }}
register: git_fetch
- name: DEPTH | ensure the fetch succeeded
@@ -120,7 +122,8 @@
repo: 'file://{{ repo_dir|expanduser }}/shallow'
dest: '{{ checkout_dir }}'
depth: 1
- version: master
+ version: >-
+ {{ git_default_branch }}
- name: DEPTH | switch to older branch with depth=1 (uses fetch)
git:
diff --git a/test/integration/targets/git/tasks/forcefully-fetch-tag.yml b/test/integration/targets/git/tasks/forcefully-fetch-tag.yml
index 47c3747..db35e04 100644
--- a/test/integration/targets/git/tasks/forcefully-fetch-tag.yml
+++ b/test/integration/targets/git/tasks/forcefully-fetch-tag.yml
@@ -11,7 +11,7 @@
git add leet;
git commit -m uh-oh;
git tag -f herewego;
- git push --tags origin master
+ git push --tags origin '{{ git_default_branch }}'
args:
chdir: "{{ repo_dir }}/tag_force_push_clone1"
@@ -26,7 +26,7 @@
git add leet;
git commit -m uh-oh;
git tag -f herewego;
- git push -f --tags origin master
+ git push -f --tags origin '{{ git_default_branch }}'
args:
chdir: "{{ repo_dir }}/tag_force_push_clone1"
diff --git a/test/integration/targets/git/tasks/gpg-verification.yml b/test/integration/targets/git/tasks/gpg-verification.yml
index 8c8834a..bd57ed1 100644
--- a/test/integration/targets/git/tasks/gpg-verification.yml
+++ b/test/integration/targets/git/tasks/gpg-verification.yml
@@ -37,8 +37,10 @@
environment:
- GNUPGHOME: "{{ git_gpg_gpghome }}"
shell: |
- set -e
+ set -eEu
+
git init
+
touch an_empty_file
git add an_empty_file
git commit --no-gpg-sign --message "Commit, and don't sign"
@@ -48,11 +50,11 @@
git tag --annotate --message "This is not a signed tag" unsigned_annotated_tag HEAD
git commit --allow-empty --gpg-sign --message "Commit, and sign"
git tag --sign --message "This is a signed tag" signed_annotated_tag HEAD
- git checkout -b some_branch/signed_tip master
+ git checkout -b some_branch/signed_tip '{{ git_default_branch }}'
git commit --allow-empty --gpg-sign --message "Commit, and sign"
- git checkout -b another_branch/unsigned_tip master
+ git checkout -b another_branch/unsigned_tip '{{ git_default_branch }}'
git commit --allow-empty --no-gpg-sign --message "Commit, and don't sign"
- git checkout master
+ git checkout '{{ git_default_branch }}'
args:
chdir: "{{ git_gpg_source }}"
diff --git a/test/integration/targets/git/tasks/localmods.yml b/test/integration/targets/git/tasks/localmods.yml
index 0e0cf68..409bbae 100644
--- a/test/integration/targets/git/tasks/localmods.yml
+++ b/test/integration/targets/git/tasks/localmods.yml
@@ -1,6 +1,17 @@
# test for https://github.com/ansible/ansible-modules-core/pull/5505
- name: LOCALMODS | prepare old git repo
- shell: rm -rf localmods; mkdir localmods; cd localmods; git init; echo "1" > a; git add a; git commit -m "1"
+ shell: |
+ set -eEu
+
+ rm -rf localmods
+ mkdir localmods
+ cd localmods
+
+ git init
+
+ echo "1" > a
+ git add a
+ git commit -m "1"
args:
chdir: "{{repo_dir}}"
@@ -55,7 +66,18 @@
# localmods and shallow clone
- name: LOCALMODS | prepare old git repo
- shell: rm -rf localmods; mkdir localmods; cd localmods; git init; echo "1" > a; git add a; git commit -m "1"
+ shell: |
+ set -eEu
+
+ rm -rf localmods
+ mkdir localmods
+ cd localmods
+
+ git init
+
+ echo "1" > a
+ git add a
+ git commit -m "1"
args:
chdir: "{{repo_dir}}"
diff --git a/test/integration/targets/git/tasks/main.yml b/test/integration/targets/git/tasks/main.yml
index ed06eab..c990251 100644
--- a/test/integration/targets/git/tasks/main.yml
+++ b/test/integration/targets/git/tasks/main.yml
@@ -16,27 +16,37 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-- import_tasks: setup.yml
-- import_tasks: setup-local-repos.yml
+# NOTE: Moving `$HOME` to tmp dir allows this integration test be
+# NOTE: non-destructive. There is no other way to instruct Git use a custom
+# NOTE: config path. There are new `$GIT_CONFIG_KEY_{COUNT,KEY,VALUE}` vars
+# NOTE: for setting specific configuration values but those are only available
+# NOTE: since Git v2.31 which is why we cannot rely on them yet.
-- import_tasks: formats.yml
-- import_tasks: missing_hostkey.yml
-- import_tasks: missing_hostkey_acceptnew.yml
-- import_tasks: no-destination.yml
-- import_tasks: specific-revision.yml
-- import_tasks: submodules.yml
-- import_tasks: change-repo-url.yml
-- import_tasks: depth.yml
-- import_tasks: single-branch.yml
-- import_tasks: checkout-new-tag.yml
-- include_tasks: gpg-verification.yml
- when:
+- block:
+ - import_tasks: setup.yml
+ - import_tasks: setup-local-repos.yml
+
+ - import_tasks: formats.yml
+ - import_tasks: missing_hostkey.yml
+ - import_tasks: missing_hostkey_acceptnew.yml
+ - import_tasks: no-destination.yml
+ - import_tasks: specific-revision.yml
+ - import_tasks: submodules.yml
+ - import_tasks: change-repo-url.yml
+ - import_tasks: depth.yml
+ - import_tasks: single-branch.yml
+ - import_tasks: checkout-new-tag.yml
+ - include_tasks: gpg-verification.yml
+ when:
- not gpg_version.stderr
- gpg_version.stdout
- not (ansible_os_family == 'RedHat' and ansible_distribution_major_version is version('7', '<'))
-- import_tasks: localmods.yml
-- import_tasks: reset-origin.yml
-- import_tasks: ambiguous-ref.yml
-- import_tasks: archive.yml
-- import_tasks: separate-git-dir.yml
-- import_tasks: forcefully-fetch-tag.yml
+ - import_tasks: localmods.yml
+ - import_tasks: reset-origin.yml
+ - import_tasks: ambiguous-ref.yml
+ - import_tasks: archive.yml
+ - import_tasks: separate-git-dir.yml
+ - import_tasks: forcefully-fetch-tag.yml
+ environment:
+ HOME: >-
+ {{ remote_tmp_dir }}
diff --git a/test/integration/targets/git/tasks/missing_hostkey.yml b/test/integration/targets/git/tasks/missing_hostkey.yml
index 136c5d5..d8a2a81 100644
--- a/test/integration/targets/git/tasks/missing_hostkey.yml
+++ b/test/integration/targets/git/tasks/missing_hostkey.yml
@@ -35,7 +35,8 @@
git:
repo: '{{ repo_format3 }}'
dest: '{{ checkout_dir }}'
- version: 'master'
+ version: >-
+ {{ git_default_branch }}
accept_hostkey: false # should already have been accepted
key_file: '{{ github_ssh_private_key }}'
ssh_opts: '-o UserKnownHostsFile={{ remote_tmp_dir }}/known_hosts'
diff --git a/test/integration/targets/git/tasks/missing_hostkey_acceptnew.yml b/test/integration/targets/git/tasks/missing_hostkey_acceptnew.yml
index 3fd1906..338ae08 100644
--- a/test/integration/targets/git/tasks/missing_hostkey_acceptnew.yml
+++ b/test/integration/targets/git/tasks/missing_hostkey_acceptnew.yml
@@ -55,7 +55,8 @@
git:
repo: '{{ repo_format3 }}'
dest: '{{ checkout_dir }}'
- version: 'master'
+ version: >-
+ {{ git_default_branch }}
accept_newhostkey: false # should already have been accepted
key_file: '{{ github_ssh_private_key }}'
ssh_opts: '-o UserKnownHostsFile={{ remote_tmp_dir }}/known_hosts'
diff --git a/test/integration/targets/git/tasks/reset-origin.yml b/test/integration/targets/git/tasks/reset-origin.yml
index 8fddd4b..cb497c4 100644
--- a/test/integration/targets/git/tasks/reset-origin.yml
+++ b/test/integration/targets/git/tasks/reset-origin.yml
@@ -12,7 +12,14 @@
state: directory
- name: RESET-ORIGIN | Initialise the repo with a file named origin,see github.com/ansible/ansible/pull/22502
- shell: git init; echo "PR 22502" > origin; git add origin; git commit -m "PR 22502"
+ shell: |
+ set -eEu
+
+ git init
+
+ echo "PR 22502" > origin
+ git add origin
+ git commit -m "PR 22502"
args:
chdir: "{{ repo_dir }}/origin"
diff --git a/test/integration/targets/git/tasks/setup-local-repos.yml b/test/integration/targets/git/tasks/setup-local-repos.yml
index 584a169..4626f10 100644
--- a/test/integration/targets/git/tasks/setup-local-repos.yml
+++ b/test/integration/targets/git/tasks/setup-local-repos.yml
@@ -9,15 +9,32 @@
- "{{ repo_dir }}/tag_force_push"
- name: SETUP-LOCAL-REPOS | prepare minimal git repo
- shell: git init; echo "1" > a; git add a; git commit -m "1"
+ shell: |
+ set -eEu
+
+ git init
+
+ echo "1" > a
+ git add a
+ git commit -m "1"
args:
chdir: "{{ repo_dir }}/minimal"
- name: SETUP-LOCAL-REPOS | prepare git repo for shallow clone
shell: |
- git init;
- echo "1" > a; git add a; git commit -m "1"; git tag earlytag; git branch earlybranch;
- echo "2" > a; git add a; git commit -m "2";
+ set -eEu
+
+ git init
+
+ echo "1" > a
+ git add a
+ git commit -m "1"
+ git tag earlytag
+ git branch earlybranch
+
+ echo "2" > a
+ git add a
+ git commit -m "2"
args:
chdir: "{{ repo_dir }}/shallow"
@@ -29,7 +46,10 @@
- name: SETUP-LOCAL-REPOS | prepare tmp git repo with two branches
shell: |
+ set -eEu
+
git init
+
echo "1" > a; git add a; git commit -m "1"
git checkout -b test_branch; echo "2" > a; git commit -m "2 on branch" a
git checkout -b new_branch; echo "3" > a; git commit -m "3 on new branch" a
@@ -40,6 +60,9 @@
# We make the repo here for consistency with the other repos,
# but we finish setting it up in forcefully-fetch-tag.yml.
- name: SETUP-LOCAL-REPOS | prepare tag_force_push git repo
- shell: git init --bare
+ shell: |
+ set -eEu
+
+ git init --bare
args:
chdir: "{{ repo_dir }}/tag_force_push"
diff --git a/test/integration/targets/git/tasks/setup.yml b/test/integration/targets/git/tasks/setup.yml
index 0651105..982c03f 100644
--- a/test/integration/targets/git/tasks/setup.yml
+++ b/test/integration/targets/git/tasks/setup.yml
@@ -28,10 +28,44 @@
register: gpg_version
- name: SETUP | set git global user.email if not already set
- shell: git config --global user.email || git config --global user.email "noreply@example.com"
+ shell: git config --global user.email 'noreply@example.com'
- name: SETUP | set git global user.name if not already set
- shell: git config --global user.name || git config --global user.name "Ansible Test Runner"
+ shell: git config --global user.name 'Ansible Test Runner'
+
+- name: SETUP | set git global init.defaultBranch
+ shell: >-
+ git config --global init.defaultBranch '{{ git_default_branch }}'
+
+- name: SETUP | set git global init.templateDir
+ # NOTE: This custom Git repository template emulates the `init.defaultBranch`
+ # NOTE: setting on Git versions below 2.28.
+ # NOTE: Ref: https://superuser.com/a/1559582.
+ # NOTE: Other workarounds mentioned there, like invoking
+ # NOTE: `git symbolic-ref HEAD refs/heads/main` after each `git init` turned
+ # NOTE: out to have mysterious side effects that break the tests in surprising
+ # NOTE: ways.
+ shell: |
+ set -eEu
+
+ git config --global \
+ init.templateDir '{{ remote_tmp_dir }}/git-templates/git.git'
+
+ mkdir -pv '{{ remote_tmp_dir }}/git-templates'
+ set +e
+ GIT_TEMPLATES_DIR=$(\
+ 2>/dev/null \
+ ls -1d \
+ '/Library/Developer/CommandLineTools/usr/share/git-core/templates' \
+ '/usr/local/share/git-core/templates' \
+ '/usr/share/git-core/templates' \
+ )
+ set -e
+ >&2 echo "Found Git's default templates directory: ${GIT_TEMPLATES_DIR}"
+ cp -r "${GIT_TEMPLATES_DIR}" '{{ remote_tmp_dir }}/git-templates/git.git'
+
+ echo 'ref: refs/heads/{{ git_default_branch }}' \
+ > '{{ remote_tmp_dir }}/git-templates/git.git/HEAD'
- name: SETUP | create repo_dir
file:
diff --git a/test/integration/targets/git/tasks/single-branch.yml b/test/integration/targets/git/tasks/single-branch.yml
index 5cfb4d5..ca8457a 100644
--- a/test/integration/targets/git/tasks/single-branch.yml
+++ b/test/integration/targets/git/tasks/single-branch.yml
@@ -52,7 +52,8 @@
repo: 'file://{{ repo_dir|expanduser }}/shallow_branches'
dest: '{{ checkout_dir }}'
single_branch: yes
- version: master
+ version: >-
+ {{ git_default_branch }}
register: single_branch_3
- name: SINGLE_BRANCH | Clone example git repo using single_branch with version again
@@ -60,7 +61,8 @@
repo: 'file://{{ repo_dir|expanduser }}/shallow_branches'
dest: '{{ checkout_dir }}'
single_branch: yes
- version: master
+ version: >-
+ {{ git_default_branch }}
register: single_branch_4
- name: SINGLE_BRANCH | List revisions
diff --git a/test/integration/targets/git/tasks/specific-revision.yml b/test/integration/targets/git/tasks/specific-revision.yml
index 26fa7cf..f1fe41d 100644
--- a/test/integration/targets/git/tasks/specific-revision.yml
+++ b/test/integration/targets/git/tasks/specific-revision.yml
@@ -162,7 +162,14 @@
path: "{{ checkout_dir }}"
- name: SPECIFIC-REVISION | prepare origina repo
- shell: git init; echo "1" > a; git add a; git commit -m "1"
+ shell: |
+ set -eEu
+
+ git init
+
+ echo "1" > a
+ git add a
+ git commit -m "1"
args:
chdir: "{{ checkout_dir }}"
@@ -191,7 +198,14 @@
force: yes
- name: SPECIFIC-REVISION | create new commit in original
- shell: git init; echo "2" > b; git add b; git commit -m "2"
+ shell: |
+ set -eEu
+
+ git init
+
+ echo "2" > b
+ git add b
+ git commit -m "2"
args:
chdir: "{{ checkout_dir }}"
diff --git a/test/integration/targets/git/vars/main.yml b/test/integration/targets/git/vars/main.yml
index b38531f..55c7c43 100644
--- a/test/integration/targets/git/vars/main.yml
+++ b/test/integration/targets/git/vars/main.yml
@@ -41,6 +41,7 @@ repo_update_url_2: 'https://github.com/ansible-test-robinro/git-test-new'
known_host_files:
- "{{ lookup('env','HOME') }}/.ssh/known_hosts"
- '/etc/ssh/ssh_known_hosts'
+git_default_branch: main
git_version_supporting_depth: 1.9.1
git_version_supporting_ls_remote: 1.7.5
git_version_supporting_single_branch: 1.7.10
diff --git a/test/integration/targets/group/files/get_free_gid.py b/test/integration/targets/group/files/get_free_gid.py
new file mode 100644
index 0000000..4c07b5e
--- /dev/null
+++ b/test/integration/targets/group/files/get_free_gid.py
@@ -0,0 +1,23 @@
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import grp
+
+
+def main():
+ gids = [g.gr_gid for g in grp.getgrall()]
+
+ # Start the gid numbering with 1
+ # FreeBSD doesn't support the usage of gid 0, it doesn't fail (rc=0) but instead a number in the normal
+ # range is picked.
+ i = 1
+ while True:
+ if i not in gids:
+ print(i)
+ break
+ i += 1
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/group/files/get_gid_for_group.py b/test/integration/targets/group/files/get_gid_for_group.py
new file mode 100644
index 0000000..5a8cc41
--- /dev/null
+++ b/test/integration/targets/group/files/get_gid_for_group.py
@@ -0,0 +1,18 @@
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import grp
+import sys
+
+
+def main():
+ group_name = None
+ if len(sys.argv) >= 2:
+ group_name = sys.argv[1]
+
+ print(grp.getgrnam(group_name).gr_gid)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/group/files/gidget.py b/test/integration/targets/group/files/gidget.py
deleted file mode 100644
index 4b77151..0000000
--- a/test/integration/targets/group/files/gidget.py
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-import grp
-
-gids = [g.gr_gid for g in grp.getgrall()]
-
-i = 0
-while True:
- if i not in gids:
- print(i)
- break
- i += 1
diff --git a/test/integration/targets/group/tasks/main.yml b/test/integration/targets/group/tasks/main.yml
index eb8126d..2123524 100644
--- a/test/integration/targets/group/tasks/main.yml
+++ b/test/integration/targets/group/tasks/main.yml
@@ -16,25 +16,4 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-- name: ensure test groups are deleted before the test
- group:
- name: '{{ item }}'
- state: absent
- loop:
- - ansibullgroup
- - ansibullgroup2
- - ansibullgroup3
-
-- block:
- - name: run tests
- include_tasks: tests.yml
-
- always:
- - name: remove test groups after test
- group:
- name: '{{ item }}'
- state: absent
- loop:
- - ansibullgroup
- - ansibullgroup2
- - ansibullgroup3 \ No newline at end of file
+- import_tasks: tests.yml
diff --git a/test/integration/targets/group/tasks/tests.yml b/test/integration/targets/group/tasks/tests.yml
index f9a8122..eb92cd1 100644
--- a/test/integration/targets/group/tasks/tests.yml
+++ b/test/integration/targets/group/tasks/tests.yml
@@ -1,343 +1,412 @@
---
-##
-## group add
-##
-
-- name: create group (check mode)
- group:
- name: ansibullgroup
- state: present
- register: create_group_check
- check_mode: True
-
-- name: get result of create group (check mode)
- script: 'grouplist.sh "{{ ansible_distribution }}"'
- register: create_group_actual_check
-
-- name: assert create group (check mode)
- assert:
- that:
- - create_group_check is changed
- - '"ansibullgroup" not in create_group_actual_check.stdout_lines'
-
-- name: create group
- group:
- name: ansibullgroup
- state: present
- register: create_group
-
-- name: get result of create group
- script: 'grouplist.sh "{{ ansible_distribution }}"'
- register: create_group_actual
-
-- name: assert create group
- assert:
- that:
- - create_group is changed
- - create_group.gid is defined
- - '"ansibullgroup" in create_group_actual.stdout_lines'
-
-- name: create group (idempotent)
+- name: ensure test groups are deleted before the test
group:
- name: ansibullgroup
- state: present
- register: create_group_again
+ name: '{{ item }}'
+ state: absent
+ loop:
+ - ansibullgroup
+ - ansibullgroup2
+ - ansibullgroup3
-- name: assert create group (idempotent)
- assert:
- that:
- - not create_group_again is changed
+- block:
+ ##
+ ## group add
+ ##
-##
-## group check
-##
+ - name: create group (check mode)
+ group:
+ name: ansibullgroup
+ state: present
+ register: create_group_check
+ check_mode: true
-- name: run existing group check tests
- group:
- name: "{{ create_group_actual.stdout_lines|random }}"
- state: present
- with_sequence: start=1 end=5
- register: group_test1
-
-- name: validate results for testcase 1
- assert:
- that:
- - group_test1.results is defined
- - group_test1.results|length == 5
-
-- name: validate change results for testcase 1
- assert:
- that:
- - not group_test1 is changed
-
-##
-## group add with gid
-##
-
-- name: get the next available gid
- script: gidget.py
- args:
- executable: '{{ ansible_python_interpreter }}'
- register: gid
-
-- name: create a group with a gid (check mode)
- group:
- name: ansibullgroup2
- gid: '{{ gid.stdout_lines[0] }}'
- state: present
- register: create_group_gid_check
- check_mode: True
-
-- name: get result of create a group with a gid (check mode)
- script: 'grouplist.sh "{{ ansible_distribution }}"'
- register: create_group_gid_actual_check
-
-- name: assert create group with a gid (check mode)
- assert:
- that:
- - create_group_gid_check is changed
- - '"ansibullgroup2" not in create_group_gid_actual_check.stdout_lines'
-
-- name: create a group with a gid
- group:
- name: ansibullgroup2
- gid: '{{ gid.stdout_lines[0] }}'
- state: present
- register: create_group_gid
-
-- name: get gid of created group
- command: "{{ ansible_python_interpreter | quote }} -c \"import grp; print(grp.getgrnam('ansibullgroup2').gr_gid)\""
- register: create_group_gid_actual
-
-- name: assert create group with a gid
- assert:
- that:
- - create_group_gid is changed
- - create_group_gid.gid | int == gid.stdout_lines[0] | int
- - create_group_gid_actual.stdout | trim | int == gid.stdout_lines[0] | int
-
-- name: create a group with a gid (idempotent)
- group:
- name: ansibullgroup2
- gid: '{{ gid.stdout_lines[0] }}'
- state: present
- register: create_group_gid_again
+ - name: get result of create group (check mode)
+ script: 'grouplist.sh "{{ ansible_distribution }}"'
+ register: create_group_actual_check
-- name: assert create group with a gid (idempotent)
- assert:
- that:
- - not create_group_gid_again is changed
- - create_group_gid_again.gid | int == gid.stdout_lines[0] | int
+ - name: assert create group (check mode)
+ assert:
+ that:
+ - create_group_check is changed
+ - '"ansibullgroup" not in create_group_actual_check.stdout_lines'
-- block:
- - name: create a group with a non-unique gid
+ - name: create group
group:
- name: ansibullgroup3
- gid: '{{ gid.stdout_lines[0] }}'
- non_unique: true
+ name: ansibullgroup
state: present
- register: create_group_gid_non_unique
+ register: create_group
- - name: validate gid required with non_unique
+ - name: get result of create group
+ script: 'grouplist.sh "{{ ansible_distribution }}"'
+ register: create_group_actual
+
+ - name: assert create group
+ assert:
+ that:
+ - create_group is changed
+ - create_group.gid is defined
+ - '"ansibullgroup" in create_group_actual.stdout_lines'
+
+ - name: create group (idempotent)
group:
- name: foo
- non_unique: true
- register: missing_gid
- ignore_errors: true
+ name: ansibullgroup
+ state: present
+ register: create_group_again
- - name: assert create group with a non unique gid
+ - name: assert create group (idempotent)
assert:
that:
- - create_group_gid_non_unique is changed
- - create_group_gid_non_unique.gid | int == gid.stdout_lines[0] | int
- - missing_gid is failed
- when: ansible_facts.distribution not in ['MacOSX', 'Alpine']
+ - not create_group_again is changed
-##
-## group remove
-##
+ ##
+ ## group check
+ ##
-- name: delete group (check mode)
- group:
- name: ansibullgroup
- state: absent
- register: delete_group_check
- check_mode: True
+ - name: run existing group check tests
+ group:
+ name: "{{ create_group_actual.stdout_lines|random }}"
+ state: present
+ with_sequence: start=1 end=5
+ register: group_test1
-- name: get result of delete group (check mode)
- script: grouplist.sh "{{ ansible_distribution }}"
- register: delete_group_actual_check
+ - name: validate results for testcase 1
+ assert:
+ that:
+ - group_test1.results is defined
+ - group_test1.results|length == 5
-- name: assert delete group (check mode)
- assert:
- that:
- - delete_group_check is changed
- - '"ansibullgroup" in delete_group_actual_check.stdout_lines'
+ - name: validate change results for testcase 1
+ assert:
+ that:
+ - not group_test1 is changed
-- name: delete group
- group:
- name: ansibullgroup
- state: absent
- register: delete_group
+ ##
+ ## group add with gid
+ ##
-- name: get result of delete group
- script: grouplist.sh "{{ ansible_distribution }}"
- register: delete_group_actual
+ - name: get the next available gid
+ script: get_free_gid.py
+ args:
+ executable: '{{ ansible_python_interpreter }}'
+ register: gid
-- name: assert delete group
- assert:
- that:
- - delete_group is changed
- - '"ansibullgroup" not in delete_group_actual.stdout_lines'
+ - name: create a group with a gid (check mode)
+ group:
+ name: ansibullgroup2
+ gid: '{{ gid.stdout_lines[0] }}'
+ state: present
+ register: create_group_gid_check
+ check_mode: true
-- name: delete group (idempotent)
- group:
- name: ansibullgroup
- state: absent
- register: delete_group_again
-
-- name: assert delete group (idempotent)
- assert:
- that:
- - not delete_group_again is changed
-
-- name: Ensure lgroupadd is present
- action: "{{ ansible_facts.pkg_mgr }}"
- args:
- name: libuser
- state: present
- when: ansible_facts.system in ['Linux'] and ansible_distribution != 'Alpine' and ansible_os_family != 'Suse'
- tags:
- - user_test_local_mode
-
-- name: Ensure lgroupadd is present - Alpine
- command: apk add -U libuser
- when: ansible_distribution == 'Alpine'
- tags:
- - user_test_local_mode
-
-# https://github.com/ansible/ansible/issues/56481
-- block:
- - name: Test duplicate GID with local=yes
- group:
- name: "{{ item }}"
- gid: 1337
- local: yes
- loop:
- - group1_local_test
- - group2_local_test
- ignore_errors: yes
- register: local_duplicate_gid_result
-
- - assert:
- that:
- - local_duplicate_gid_result['results'][0] is success
- - local_duplicate_gid_result['results'][1]['msg'] == "GID '1337' already exists with group 'group1_local_test'"
- always:
- - name: Cleanup
+ - name: get result of create a group with a gid (check mode)
+ script: 'grouplist.sh "{{ ansible_distribution }}"'
+ register: create_group_gid_actual_check
+
+ - name: assert create group with a gid (check mode)
+ assert:
+ that:
+ - create_group_gid_check is changed
+ - '"ansibullgroup2" not in create_group_gid_actual_check.stdout_lines'
+
+ - name: create a group with a gid
group:
- name: group1_local_test
- state: absent
- # only applicable to Linux, limit further to CentOS where 'luseradd' is installed
- when: ansible_distribution == 'CentOS'
+ name: ansibullgroup2
+ gid: '{{ gid.stdout_lines[0] }}'
+ state: present
+ register: create_group_gid
-# https://github.com/ansible/ansible/pull/59769
-- block:
- - name: create a local group with a gid
- group:
- name: group1_local_test
- gid: 1337
- local: yes
- state: present
- register: create_local_group_gid
-
- - name: get gid of created local group
- command: "{{ ansible_python_interpreter | quote }} -c \"import grp; print(grp.getgrnam('group1_local_test').gr_gid)\""
- register: create_local_group_gid_actual
-
- - name: assert create local group with a gid
- assert:
+ - name: get gid of created group
+ script: "get_gid_for_group.py ansibullgroup2"
+ args:
+ executable: '{{ ansible_python_interpreter }}'
+ register: create_group_gid_actual
+
+ - name: assert create group with a gid
+ assert:
that:
- - create_local_group_gid is changed
- - create_local_group_gid.gid | int == 1337 | int
- - create_local_group_gid_actual.stdout | trim | int == 1337 | int
-
- - name: create a local group with a gid (idempotent)
- group:
- name: group1_local_test
- gid: 1337
- state: present
- register: create_local_group_gid_again
-
- - name: assert create local group with a gid (idempotent)
- assert:
+ - create_group_gid is changed
+ - create_group_gid.gid | int == gid.stdout_lines[0] | int
+ - create_group_gid_actual.stdout | trim | int == gid.stdout_lines[0] | int
+
+ - name: create a group with a gid (idempotent)
+ group:
+ name: ansibullgroup2
+ gid: '{{ gid.stdout_lines[0] }}'
+ state: present
+ register: create_group_gid_again
+
+ - name: assert create group with a gid (idempotent)
+ assert:
that:
- - not create_local_group_gid_again is changed
- - create_local_group_gid_again.gid | int == 1337 | int
- always:
- - name: Cleanup create local group with a gid
+ - not create_group_gid_again is changed
+ - create_group_gid_again.gid | int == gid.stdout_lines[0] | int
+
+ - block:
+ - name: create a group with a non-unique gid
+ group:
+ name: ansibullgroup3
+ gid: '{{ gid.stdout_lines[0] }}'
+ non_unique: true
+ state: present
+ register: create_group_gid_non_unique
+
+ - name: validate gid required with non_unique
+ group:
+ name: foo
+ non_unique: true
+ register: missing_gid
+ ignore_errors: true
+
+ - name: assert create group with a non unique gid
+ assert:
+ that:
+ - create_group_gid_non_unique is changed
+ - create_group_gid_non_unique.gid | int == gid.stdout_lines[0] | int
+ - missing_gid is failed
+ when: ansible_facts.distribution not in ['MacOSX', 'Alpine']
+
+ ##
+ ## group remove
+ ##
+
+ - name: delete group (check mode)
group:
- name: group1_local_test
+ name: ansibullgroup
state: absent
- # only applicable to Linux, limit further to CentOS where 'luseradd' is installed
- when: ansible_distribution == 'CentOS'
+ register: delete_group_check
+ check_mode: true
-# https://github.com/ansible/ansible/pull/59772
-- block:
- - name: create group with a gid
- group:
- name: group1_test
- gid: 1337
- local: no
- state: present
- register: create_group_gid
-
- - name: get gid of created group
- command: "{{ ansible_python_interpreter | quote }} -c \"import grp; print(grp.getgrnam('group1_test').gr_gid)\""
- register: create_group_gid_actual
-
- - name: assert create group with a gid
- assert:
- that:
- - create_group_gid is changed
- - create_group_gid.gid | int == 1337 | int
- - create_group_gid_actual.stdout | trim | int == 1337 | int
-
- - name: create local group with the same gid
- group:
- name: group1_test
- gid: 1337
- local: yes
- state: present
- register: create_local_group_gid
-
- - name: assert create local group with a gid
- assert:
+ - name: get result of delete group (check mode)
+ script: 'grouplist.sh "{{ ansible_distribution }}"'
+ register: delete_group_actual_check
+
+ - name: assert delete group (check mode)
+ assert:
that:
- - create_local_group_gid.gid | int == 1337 | int
- always:
- - name: Cleanup create group with a gid
+ - delete_group_check is changed
+ - '"ansibullgroup" in delete_group_actual_check.stdout_lines'
+
+ - name: delete group
group:
- name: group1_test
- local: no
+ name: ansibullgroup
state: absent
- - name: Cleanup create local group with the same gid
+ register: delete_group
+
+ - name: get result of delete group
+ script: 'grouplist.sh "{{ ansible_distribution }}"'
+ register: delete_group_actual
+
+ - name: assert delete group
+ assert:
+ that:
+ - delete_group is changed
+ - '"ansibullgroup" not in delete_group_actual.stdout_lines'
+
+ - name: delete group (idempotent)
group:
- name: group1_test
- local: yes
+ name: ansibullgroup
state: absent
- # only applicable to Linux, limit further to CentOS where 'lgroupadd' is installed
- when: ansible_distribution == 'CentOS'
+ register: delete_group_again
-# create system group
+ - name: assert delete group (idempotent)
+ assert:
+ that:
+ - not delete_group_again is changed
-- name: remove group
- group:
- name: ansibullgroup
- state: absent
+ - name: Ensure lgroupadd is present
+ action: "{{ ansible_facts.pkg_mgr }}"
+ args:
+ name: libuser
+ state: present
+ when: ansible_facts.system in ['Linux'] and ansible_distribution != 'Alpine' and ansible_os_family != 'Suse'
+ tags:
+ - user_test_local_mode
+
+ - name: Ensure lgroupadd is present - Alpine
+ command: apk add -U libuser
+ when: ansible_distribution == 'Alpine'
+ tags:
+ - user_test_local_mode
+
+ # https://github.com/ansible/ansible/issues/56481
+ - block:
+ - name: Test duplicate GID with local=yes
+ group:
+ name: "{{ item }}"
+ gid: 1337
+ local: true
+ loop:
+ - group1_local_test
+ - group2_local_test
+ ignore_errors: true
+ register: local_duplicate_gid_result
+
+ - assert:
+ that:
+ - local_duplicate_gid_result['results'][0] is success
+ - local_duplicate_gid_result['results'][1]['msg'] == "GID '1337' already exists with group 'group1_local_test'"
+ always:
+ - name: Cleanup
+ group:
+ name: group1_local_test
+ state: absent
+ # only applicable to Linux, limit further to CentOS where 'luseradd' is installed
+ when: ansible_distribution == 'CentOS'
+
+ # https://github.com/ansible/ansible/pull/59769
+ - block:
+ - name: create a local group with a gid
+ group:
+ name: group1_local_test
+ gid: 1337
+ local: true
+ state: present
+ register: create_local_group_gid
+
+ - name: get gid of created local group
+ script: "get_gid_for_group.py group1_local_test"
+ args:
+ executable: '{{ ansible_python_interpreter }}'
+ register: create_local_group_gid_actual
+
+ - name: assert create local group with a gid
+ assert:
+ that:
+ - create_local_group_gid is changed
+ - create_local_group_gid.gid | int == 1337 | int
+ - create_local_group_gid_actual.stdout | trim | int == 1337 | int
+
+ - name: create a local group with a gid (idempotent)
+ group:
+ name: group1_local_test
+ gid: 1337
+ state: present
+ register: create_local_group_gid_again
+
+ - name: assert create local group with a gid (idempotent)
+ assert:
+ that:
+ - not create_local_group_gid_again is changed
+ - create_local_group_gid_again.gid | int == 1337 | int
+ always:
+ - name: Cleanup create local group with a gid
+ group:
+ name: group1_local_test
+ state: absent
+ # only applicable to Linux, limit further to CentOS where 'luseradd' is installed
+ when: ansible_distribution == 'CentOS'
+
+ # https://github.com/ansible/ansible/pull/59772
+ - block:
+ - name: create group with a gid
+ group:
+ name: group1_test
+ gid: 1337
+ local: false
+ state: present
+ register: create_group_gid
+
+ - name: get gid of created group
+ script: "get_gid_for_group.py group1_test"
+ args:
+ executable: '{{ ansible_python_interpreter }}'
+ register: create_group_gid_actual
+
+ - name: assert create group with a gid
+ assert:
+ that:
+ - create_group_gid is changed
+ - create_group_gid.gid | int == 1337 | int
+ - create_group_gid_actual.stdout | trim | int == 1337 | int
+
+ - name: create local group with the same gid
+ group:
+ name: group1_test
+ gid: 1337
+ local: true
+ state: present
+ register: create_local_group_gid
+
+ - name: assert create local group with a gid
+ assert:
+ that:
+ - create_local_group_gid.gid | int == 1337 | int
+ always:
+ - name: Cleanup create group with a gid
+ group:
+ name: group1_test
+ local: false
+ state: absent
+ - name: Cleanup create local group with the same gid
+ group:
+ name: group1_test
+ local: true
+ state: absent
+ # only applicable to Linux, limit further to CentOS where 'lgroupadd' is installed
+ when: ansible_distribution == 'CentOS'
+
+ # https://github.com/ansible/ansible/pull/78172
+ - block:
+ - name: Create a group
+ group:
+ name: groupdeltest
+ state: present
+
+ - name: Create user with primary group of groupdeltest
+ user:
+ name: groupdeluser
+ group: groupdeltest
+ state: present
+
+ - name: Show we can't delete the group usually
+ group:
+ name: groupdeltest
+ state: absent
+ ignore_errors: true
+ register: failed_delete
+
+ - name: assert we couldn't delete the group
+ assert:
+ that:
+ - failed_delete is failed
+
+ - name: force delete the group
+ group:
+ name: groupdeltest
+ force: true
+ state: absent
+
+ always:
+ - name: Cleanup user
+ user:
+ name: groupdeluser
+ state: absent
+
+ - name: Cleanup group
+ group:
+ name: groupdeltest
+ state: absent
+ when: ansible_distribution not in ["MacOSX", "Alpine", "FreeBSD"]
+
+ # create system group
+
+ - name: remove group
+ group:
+ name: ansibullgroup
+ state: absent
-- name: create system group
- group:
- name: ansibullgroup
- state: present
- system: yes
+ - name: create system group
+ group:
+ name: ansibullgroup
+ state: present
+ system: true
+
+ always:
+ - name: remove test groups after test
+ group:
+ name: '{{ item }}'
+ state: absent
+ loop:
+ - ansibullgroup
+ - ansibullgroup2
+ - ansibullgroup3
diff --git a/test/integration/targets/handlers/80880.yml b/test/integration/targets/handlers/80880.yml
new file mode 100644
index 0000000..d362ea8
--- /dev/null
+++ b/test/integration/targets/handlers/80880.yml
@@ -0,0 +1,34 @@
+---
+- name: Test notification of handlers from other handlers
+ hosts: localhost
+ gather_facts: no
+ handlers:
+ - name: Handler 1
+ debug:
+ msg: Handler 1
+ changed_when: true
+ notify: Handler 2
+ register: handler1_res
+ - name: Handler 2
+ debug:
+ msg: Handler 2
+ changed_when: true
+ notify: Handler 3
+ register: handler2_res
+ - name: Handler 3
+ debug:
+ msg: Handler 3
+ register: handler3_res
+ tasks:
+ - name: Trigger handlers
+ ansible.builtin.debug:
+ msg: Task 1
+ changed_when: true
+ notify: Handler 1
+ post_tasks:
+ - name: Assert results
+ ansible.builtin.assert:
+ that:
+ - "handler1_res is defined and handler1_res is success"
+ - "handler2_res is defined and handler2_res is success"
+ - "handler3_res is defined and handler3_res is success"
diff --git a/test/integration/targets/handlers/82241.yml b/test/integration/targets/handlers/82241.yml
new file mode 100644
index 0000000..4a9421f
--- /dev/null
+++ b/test/integration/targets/handlers/82241.yml
@@ -0,0 +1,6 @@
+- hosts: A
+ gather_facts: false
+ tasks:
+ - import_role:
+ name: role-82241
+ tasks_from: entry_point.yml
diff --git a/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml b/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml
new file mode 100644
index 0000000..7380923
--- /dev/null
+++ b/test/integration/targets/handlers/nested_flush_handlers_failure_force.yml
@@ -0,0 +1,19 @@
+- hosts: A,B
+ gather_facts: false
+ force_handlers: true
+ tasks:
+ - block:
+ - command: echo
+ notify: h
+
+ - meta: flush_handlers
+ rescue:
+ - debug:
+ msg: flush_handlers_rescued
+ always:
+ - debug:
+ msg: flush_handlers_always
+ handlers:
+ - name: h
+ fail:
+ when: inventory_hostname == "A"
diff --git a/test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/include_handlers.yml b/test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/include_handlers.yml
new file mode 100644
index 0000000..f39ac4f
--- /dev/null
+++ b/test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/include_handlers.yml
@@ -0,0 +1,2 @@
+- debug:
+ msg: handler ran
diff --git a/test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/main.yml b/test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/main.yml
new file mode 100644
index 0000000..4ce8a3f
--- /dev/null
+++ b/test/integration/targets/handlers/roles/include_role_include_tasks_handler/handlers/main.yml
@@ -0,0 +1,2 @@
+- name: handler
+ include_tasks: include_handlers.yml
diff --git a/test/integration/targets/handlers/roles/include_role_include_tasks_handler/tasks/main.yml b/test/integration/targets/handlers/roles/include_role_include_tasks_handler/tasks/main.yml
new file mode 100644
index 0000000..50aec1c
--- /dev/null
+++ b/test/integration/targets/handlers/roles/include_role_include_tasks_handler/tasks/main.yml
@@ -0,0 +1,2 @@
+- command: echo
+ notify: handler
diff --git a/test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml b/test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml
new file mode 100644
index 0000000..555ff0e
--- /dev/null
+++ b/test/integration/targets/handlers/roles/r1-dep_chain-vars/defaults/main.yml
@@ -0,0 +1 @@
+v: foo
diff --git a/test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml b/test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml
new file mode 100644
index 0000000..72576a0
--- /dev/null
+++ b/test/integration/targets/handlers/roles/r1-dep_chain-vars/tasks/main.yml
@@ -0,0 +1,2 @@
+- include_role:
+ name: r2-dep_chain-vars
diff --git a/test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml b/test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml
new file mode 100644
index 0000000..88f1248
--- /dev/null
+++ b/test/integration/targets/handlers/roles/r2-dep_chain-vars/handlers/main.yml
@@ -0,0 +1,4 @@
+- name: h
+ assert:
+ that:
+ - v is defined
diff --git a/test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml b/test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml
new file mode 100644
index 0000000..72eae5d
--- /dev/null
+++ b/test/integration/targets/handlers/roles/r2-dep_chain-vars/tasks/main.yml
@@ -0,0 +1,2 @@
+- command: echo
+ notify: h
diff --git a/test/integration/targets/handlers/roles/role-82241/handlers/main.yml b/test/integration/targets/handlers/roles/role-82241/handlers/main.yml
new file mode 100644
index 0000000..ad59b96
--- /dev/null
+++ b/test/integration/targets/handlers/roles/role-82241/handlers/main.yml
@@ -0,0 +1,2 @@
+- name: handler
+ include_tasks: included_tasks.yml
diff --git a/test/integration/targets/handlers/roles/role-82241/tasks/entry_point.yml b/test/integration/targets/handlers/roles/role-82241/tasks/entry_point.yml
new file mode 100644
index 0000000..50aec1c
--- /dev/null
+++ b/test/integration/targets/handlers/roles/role-82241/tasks/entry_point.yml
@@ -0,0 +1,2 @@
+- command: echo
+ notify: handler
diff --git a/test/integration/targets/handlers/roles/role-82241/tasks/included_tasks.yml b/test/integration/targets/handlers/roles/role-82241/tasks/included_tasks.yml
new file mode 100644
index 0000000..e3ffeb7
--- /dev/null
+++ b/test/integration/targets/handlers/roles/role-82241/tasks/included_tasks.yml
@@ -0,0 +1,2 @@
+- debug:
+ msg: included_task_from_tasks_dir
diff --git a/test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml b/test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml
new file mode 100644
index 0000000..6ce84e4
--- /dev/null
+++ b/test/integration/targets/handlers/roles/test_listen_role_dedup_global/handlers/main.yml
@@ -0,0 +1,4 @@
+- name: role_handler
+ debug:
+ msg: "a handler from a role"
+ listen: role_handler
diff --git a/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml b/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml
new file mode 100644
index 0000000..b6a70c2
--- /dev/null
+++ b/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - test_listen_role_dedup_global
diff --git a/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml b/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml
new file mode 100644
index 0000000..42911e5
--- /dev/null
+++ b/test/integration/targets/handlers/roles/test_listen_role_dedup_role1/tasks/main.yml
@@ -0,0 +1,3 @@
+- name: a task from role1
+ command: echo
+ notify: role_handler
diff --git a/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml b/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml
new file mode 100644
index 0000000..b6a70c2
--- /dev/null
+++ b/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - test_listen_role_dedup_global
diff --git a/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml b/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml
new file mode 100644
index 0000000..3d5e544
--- /dev/null
+++ b/test/integration/targets/handlers/roles/test_listen_role_dedup_role2/tasks/main.yml
@@ -0,0 +1,3 @@
+- name: a task from role2
+ command: echo
+ notify: role_handler
diff --git a/test/integration/targets/handlers/roles/two_tasks_files_role/handlers/main.yml b/test/integration/targets/handlers/roles/two_tasks_files_role/handlers/main.yml
new file mode 100644
index 0000000..3fd1318
--- /dev/null
+++ b/test/integration/targets/handlers/roles/two_tasks_files_role/handlers/main.yml
@@ -0,0 +1,3 @@
+- name: handler
+ debug:
+ msg: handler ran
diff --git a/test/integration/targets/handlers/roles/two_tasks_files_role/tasks/main.yml b/test/integration/targets/handlers/roles/two_tasks_files_role/tasks/main.yml
new file mode 100644
index 0000000..e6c1239
--- /dev/null
+++ b/test/integration/targets/handlers/roles/two_tasks_files_role/tasks/main.yml
@@ -0,0 +1,3 @@
+- name: main.yml task
+ command: echo
+ notify: handler
diff --git a/test/integration/targets/handlers/roles/two_tasks_files_role/tasks/other.yml b/test/integration/targets/handlers/roles/two_tasks_files_role/tasks/other.yml
new file mode 100644
index 0000000..d90d46e
--- /dev/null
+++ b/test/integration/targets/handlers/roles/two_tasks_files_role/tasks/other.yml
@@ -0,0 +1,3 @@
+- name: other.yml task
+ command: echo
+ notify: handler
diff --git a/test/integration/targets/handlers/runme.sh b/test/integration/targets/handlers/runme.sh
index 76fc99d..368ca44 100755
--- a/test/integration/targets/handlers/runme.sh
+++ b/test/integration/targets/handlers/runme.sh
@@ -50,6 +50,9 @@ for strategy in linear free; do
[ "$(ansible-playbook test_force_handlers.yml -i inventory.handlers -v "$@" --tags force_false_in_play --force-handlers \
| grep -E -o CALLED_HANDLER_. | sort | uniq | xargs)" = "CALLED_HANDLER_B" ]
+ # https://github.com/ansible/ansible/pull/80898
+ [ "$(ansible-playbook 80880.yml -i inventory.handlers -vv "$@" 2>&1)" ]
+
unset ANSIBLE_STRATEGY
done
@@ -66,6 +69,9 @@ done
# Notify handler listen
ansible-playbook test_handlers_listen.yml -i inventory.handlers -v "$@"
+# https://github.com/ansible/ansible/issues/82363
+ansible-playbook test_multiple_handlers_with_recursive_notification.yml -i inventory.handlers -v "$@"
+
# Notify inexistent handlers results in error
set +e
result="$(ansible-playbook test_handlers_inexistent_notify.yml -i inventory.handlers "$@" 2>&1)"
@@ -181,3 +187,24 @@ grep out.txt -e "ERROR! Using a block as a handler is not supported."
ansible-playbook test_block_as_handler-import.yml "$@" 2>&1 | tee out.txt
grep out.txt -e "ERROR! Using a block as a handler is not supported."
+
+ansible-playbook test_include_role_handler_once.yml -i inventory.handlers "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'handler ran')" = "1" ]
+
+ansible-playbook test_listen_role_dedup.yml "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'a handler from a role')" = "1" ]
+
+ansible localhost -m include_role -a "name=r1-dep_chain-vars" "$@"
+
+ansible-playbook test_include_tasks_in_include_role.yml "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'handler ran')" = "1" ]
+
+ansible-playbook test_run_once.yml -i inventory.handlers "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'handler ran once')" = "1" ]
+
+ansible-playbook 82241.yml -i inventory.handlers "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'included_task_from_tasks_dir')" = "1" ]
+
+ansible-playbook nested_flush_handlers_failure_force.yml -i inventory.handlers "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'flush_handlers_rescued')" = "1" ]
+[ "$(grep out.txt -ce 'flush_handlers_always')" = "2" ]
diff --git a/test/integration/targets/handlers/test_include_role_handler_once.yml b/test/integration/targets/handlers/test_include_role_handler_once.yml
new file mode 100644
index 0000000..764aef6
--- /dev/null
+++ b/test/integration/targets/handlers/test_include_role_handler_once.yml
@@ -0,0 +1,20 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - name: "Call main entry point"
+ include_role:
+ name: two_tasks_files_role
+
+ - name: "Call main entry point again"
+ include_role:
+ name: two_tasks_files_role
+
+ - name: "Call other entry point"
+ include_role:
+ name: two_tasks_files_role
+ tasks_from: other
+
+ - name: "Call other entry point again"
+ include_role:
+ name: two_tasks_files_role
+ tasks_from: other
diff --git a/test/integration/targets/handlers/test_include_tasks_in_include_role.yml b/test/integration/targets/handlers/test_include_tasks_in_include_role.yml
new file mode 100644
index 0000000..405e4b5
--- /dev/null
+++ b/test/integration/targets/handlers/test_include_tasks_in_include_role.yml
@@ -0,0 +1,5 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - include_role:
+ name: include_role_include_tasks_handler
diff --git a/test/integration/targets/handlers/test_listen_role_dedup.yml b/test/integration/targets/handlers/test_listen_role_dedup.yml
new file mode 100644
index 0000000..508eaf5
--- /dev/null
+++ b/test/integration/targets/handlers/test_listen_role_dedup.yml
@@ -0,0 +1,5 @@
+- hosts: localhost
+ gather_facts: false
+ roles:
+ - test_listen_role_dedup_role1
+ - test_listen_role_dedup_role2
diff --git a/test/integration/targets/handlers/test_multiple_handlers_with_recursive_notification.yml b/test/integration/targets/handlers/test_multiple_handlers_with_recursive_notification.yml
new file mode 100644
index 0000000..c4b6983
--- /dev/null
+++ b/test/integration/targets/handlers/test_multiple_handlers_with_recursive_notification.yml
@@ -0,0 +1,36 @@
+---
+- name: test multiple handlers with recursive notification
+ hosts: localhost
+ gather_facts: false
+
+ tasks:
+ - name: notify handler 1
+ command: echo
+ changed_when: true
+ notify: handler 1
+
+ - meta: flush_handlers
+
+ - name: verify handlers
+ assert:
+ that:
+ - "ran_handler_1 is defined"
+ - "ran_handler_2a is defined"
+ - "ran_handler_2b is defined"
+
+ handlers:
+ - name: handler 1
+ set_fact:
+ ran_handler_1: True
+ changed_when: true
+ notify: handler_2
+
+ - name: handler 2a
+ set_fact:
+ ran_handler_2a: True
+ listen: handler_2
+
+ - name: handler 2b
+ set_fact:
+ ran_handler_2b: True
+ listen: handler_2
diff --git a/test/integration/targets/handlers/test_run_once.yml b/test/integration/targets/handlers/test_run_once.yml
new file mode 100644
index 0000000..5418b46
--- /dev/null
+++ b/test/integration/targets/handlers/test_run_once.yml
@@ -0,0 +1,10 @@
+- hosts: A,B,C
+ gather_facts: false
+ tasks:
+ - command: echo
+ notify: handler
+ handlers:
+ - name: handler
+ run_once: true
+ debug:
+ msg: handler ran once
diff --git a/test/integration/targets/include_vars/files/test_depth/sub1/sub11.yml b/test/integration/targets/include_vars/files/test_depth/sub1/sub11.yml
new file mode 100644
index 0000000..9a5ecb8
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub1/sub11.yml
@@ -0,0 +1 @@
+sub11: defined
diff --git a/test/integration/targets/include_vars/files/test_depth/sub1/sub11/config11.yml b/test/integration/targets/include_vars/files/test_depth/sub1/sub11/config11.yml
new file mode 100644
index 0000000..02c2897
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub1/sub11/config11.yml
@@ -0,0 +1 @@
+config11: defined
diff --git a/test/integration/targets/include_vars/files/test_depth/sub1/sub11/config112.yml b/test/integration/targets/include_vars/files/test_depth/sub1/sub11/config112.yml
new file mode 100644
index 0000000..e8bc9d9
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub1/sub11/config112.yml
@@ -0,0 +1 @@
+config112: defined
diff --git a/test/integration/targets/include_vars/files/test_depth/sub1/sub12.yml b/test/integration/targets/include_vars/files/test_depth/sub1/sub12.yml
new file mode 100644
index 0000000..9aff287
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub1/sub12.yml
@@ -0,0 +1 @@
+sub12: defined
diff --git a/test/integration/targets/include_vars/files/test_depth/sub2/sub21.yml b/test/integration/targets/include_vars/files/test_depth/sub2/sub21.yml
new file mode 100644
index 0000000..1f7c455
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub2/sub21.yml
@@ -0,0 +1 @@
+sub21: defined
diff --git a/test/integration/targets/include_vars/files/test_depth/sub2/sub21/config211.yml b/test/integration/targets/include_vars/files/test_depth/sub2/sub21/config211.yml
new file mode 100644
index 0000000..a5126a7
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub2/sub21/config211.yml
@@ -0,0 +1 @@
+config211: defined
diff --git a/test/integration/targets/include_vars/files/test_depth/sub2/sub21/config212.yml b/test/integration/targets/include_vars/files/test_depth/sub2/sub21/config212.yml
new file mode 100644
index 0000000..633841d
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub2/sub21/config212.yml
@@ -0,0 +1 @@
+config212: defined
diff --git a/test/integration/targets/include_vars/files/test_depth/sub3/config3.yml b/test/integration/targets/include_vars/files/test_depth/sub3/config3.yml
new file mode 100644
index 0000000..d6a8192
--- /dev/null
+++ b/test/integration/targets/include_vars/files/test_depth/sub3/config3.yml
@@ -0,0 +1 @@
+config3: defined
diff --git a/test/integration/targets/include_vars/tasks/main.yml b/test/integration/targets/include_vars/tasks/main.yml
index 6fc4e85..97636d9 100644
--- a/test/integration/targets/include_vars/tasks/main.yml
+++ b/test/integration/targets/include_vars/tasks/main.yml
@@ -208,6 +208,21 @@
- "config.key2.b == 22"
- "config.key3 == 3"
+- name: Include a vars dir with hash variables
+ include_vars:
+ dir: "{{ role_path }}/vars2/hashes/"
+ hash_behaviour: merge
+
+- name: Verify that the hash is merged after vars files are accumulated
+ assert:
+ that:
+ - "config | length == 3"
+ - "config.key0 is undefined"
+ - "config.key1 == 1"
+ - "config.key2 | length == 1"
+ - "config.key2.b == 22"
+ - "config.key3 == 3"
+
- include_vars:
file: no_auto_unsafe.yml
register: baz
@@ -215,3 +230,40 @@
- assert:
that:
- baz.ansible_facts.foo|type_debug != "AnsibleUnsafeText"
+
+- name: setup test following symlinks
+ delegate_to: localhost
+ block:
+ - name: create directory to test following symlinks
+ file:
+ path: "{{ role_path }}/test_symlink"
+ state: directory
+
+ - name: create symlink to the vars2 dir
+ file:
+ src: "{{ role_path }}/vars2"
+ dest: "{{ role_path }}/test_symlink/symlink"
+ state: link
+
+- name: include vars by following the symlink
+ include_vars:
+ dir: "{{ role_path }}/test_symlink"
+ register: follow_sym
+
+- assert:
+ that: follow_sym.ansible_included_var_files | sort == [hash1, hash2]
+ vars:
+ hash1: "{{ role_path }}/test_symlink/symlink/hashes/hash1.yml"
+ hash2: "{{ role_path }}/test_symlink/symlink/hashes/hash2.yml"
+
+- name: Test include_vars includes everything to the correct depth
+ ansible.builtin.include_vars:
+ dir: "{{ role_path }}/files/test_depth"
+ depth: 3
+ name: test_depth_var
+ register: test_depth
+
+- assert:
+ that:
+ - "test_depth.ansible_included_var_files|length == 8"
+ - "test_depth_var.keys()|length == 8"
diff --git a/test/integration/targets/include_vars/vars/services/service_vars.yml b/test/integration/targets/include_vars/vars/services/service_vars.yml
index 96b05d6..bcac764 100644
--- a/test/integration/targets/include_vars/vars/services/service_vars.yml
+++ b/test/integration/targets/include_vars/vars/services/service_vars.yml
@@ -1,2 +1,2 @@
---
-service_name: 'my_custom_service' \ No newline at end of file
+service_name: 'my_custom_service'
diff --git a/test/integration/targets/include_vars/vars/services/service_vars_fqcn.yml b/test/integration/targets/include_vars/vars/services/service_vars_fqcn.yml
index 2c04fee..cd82eca 100644
--- a/test/integration/targets/include_vars/vars/services/service_vars_fqcn.yml
+++ b/test/integration/targets/include_vars/vars/services/service_vars_fqcn.yml
@@ -1,3 +1,3 @@
---
service_name_fqcn: 'my_custom_service'
-service_name_tmpl_fqcn: '{{ service_name_fqcn }}' \ No newline at end of file
+service_name_tmpl_fqcn: '{{ service_name_fqcn }}'
diff --git a/test/integration/targets/include_when_parent_is_dynamic/tasks.yml b/test/integration/targets/include_when_parent_is_dynamic/tasks.yml
index 6831245..d500f0d 100644
--- a/test/integration/targets/include_when_parent_is_dynamic/tasks.yml
+++ b/test/integration/targets/include_when_parent_is_dynamic/tasks.yml
@@ -9,4 +9,4 @@
# perform an include task which should be static if all of the task's parents are static, otherwise it should be dynamic
# this file was loaded using include_tasks, which is dynamic, so this include should also be dynamic
-- include: syntax_error.yml
+- include_tasks: syntax_error.yml
diff --git a/test/integration/targets/include_when_parent_is_static/tasks.yml b/test/integration/targets/include_when_parent_is_static/tasks.yml
index a234a3d..50dd234 100644
--- a/test/integration/targets/include_when_parent_is_static/tasks.yml
+++ b/test/integration/targets/include_when_parent_is_static/tasks.yml
@@ -9,4 +9,4 @@
# perform an include task which should be static if all of the task's parents are static, otherwise it should be dynamic
# this file was loaded using import_tasks, which is static, so this include should also be static
-- include: syntax_error.yml
+- import_tasks: syntax_error.yml
diff --git a/test/integration/targets/includes/include_on_playbook_should_fail.yml b/test/integration/targets/includes/include_on_playbook_should_fail.yml
index 953459d..c9b1e81 100644
--- a/test/integration/targets/includes/include_on_playbook_should_fail.yml
+++ b/test/integration/targets/includes/include_on_playbook_should_fail.yml
@@ -1 +1 @@
-- include: test_includes3.yml
+- include_tasks: test_includes3.yml
diff --git a/test/integration/targets/includes/roles/test_includes/handlers/main.yml b/test/integration/targets/includes/roles/test_includes/handlers/main.yml
index 7d3e625..453fa96 100644
--- a/test/integration/targets/includes/roles/test_includes/handlers/main.yml
+++ b/test/integration/targets/includes/roles/test_includes/handlers/main.yml
@@ -1 +1 @@
-- include: more_handlers.yml
+- import_tasks: more_handlers.yml
diff --git a/test/integration/targets/includes/roles/test_includes/tasks/main.yml b/test/integration/targets/includes/roles/test_includes/tasks/main.yml
index 83ca468..2ba1ae6 100644
--- a/test/integration/targets/includes/roles/test_includes/tasks/main.yml
+++ b/test/integration/targets/includes/roles/test_includes/tasks/main.yml
@@ -17,47 +17,9 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-- include: included_task1.yml a=1 b=2 c=3
-
-- name: verify non-variable include params
- assert:
- that:
- - "ca == '1'"
- - "cb == '2'"
- - "cc == '3'"
-
-- set_fact:
- a: 101
- b: 102
- c: 103
-
-- include: included_task1.yml a={{a}} b={{b}} c=103
-
-- name: verify variable include params
- assert:
- that:
- - "ca == 101"
- - "cb == 102"
- - "cc == 103"
-
-# Test that strings are not turned into numbers
-- set_fact:
- a: "101"
- b: "102"
- c: "103"
-
-- include: included_task1.yml a={{a}} b={{b}} c=103
-
-- name: verify variable include params
- assert:
- that:
- - "ca == '101'"
- - "cb == '102'"
- - "cc == '103'"
-
# now try long form includes
-- include: included_task1.yml
+- include_tasks: included_task1.yml
vars:
a: 201
b: 202
diff --git a/test/integration/targets/includes/roles/test_includes_free/tasks/main.yml b/test/integration/targets/includes/roles/test_includes_free/tasks/main.yml
index 5ae7882..d7bcf8e 100644
--- a/test/integration/targets/includes/roles/test_includes_free/tasks/main.yml
+++ b/test/integration/targets/includes/roles/test_includes_free/tasks/main.yml
@@ -1,9 +1,9 @@
- name: this needs to be here
debug:
msg: "hello"
-- include: inner.yml
+- include_tasks: inner.yml
with_items:
- '1'
-- ansible.builtin.include: inner_fqcn.yml
+- ansible.builtin.include_tasks: inner_fqcn.yml
with_items:
- '1'
diff --git a/test/integration/targets/includes/roles/test_includes_host_pinned/tasks/main.yml b/test/integration/targets/includes/roles/test_includes_host_pinned/tasks/main.yml
index 7bc19fa..c06d3fe 100644
--- a/test/integration/targets/includes/roles/test_includes_host_pinned/tasks/main.yml
+++ b/test/integration/targets/includes/roles/test_includes_host_pinned/tasks/main.yml
@@ -1,6 +1,6 @@
- name: this needs to be here
debug:
msg: "hello"
-- include: inner.yml
+- include_tasks: inner.yml
with_items:
- '1'
diff --git a/test/integration/targets/includes/runme.sh b/test/integration/targets/includes/runme.sh
index e619fea..8622cf6 100755
--- a/test/integration/targets/includes/runme.sh
+++ b/test/integration/targets/includes/runme.sh
@@ -10,7 +10,7 @@ echo "EXPECTED ERROR: Ensure we fail if using 'include' to include a playbook."
set +e
result="$(ansible-playbook -i ../../inventory include_on_playbook_should_fail.yml -v "$@" 2>&1)"
set -e
-grep -q "ERROR! 'include' is not a valid attribute for a Play" <<< "$result"
+grep -q "ERROR! 'include_tasks' is not a valid attribute for a Play" <<< "$result"
ansible-playbook includes_loop_rescue.yml --extra-vars strategy=linear "$@"
ansible-playbook includes_loop_rescue.yml --extra-vars strategy=free "$@"
diff --git a/test/integration/targets/includes/test_includes2.yml b/test/integration/targets/includes/test_includes2.yml
index a32e851..da6b914 100644
--- a/test/integration/targets/includes/test_includes2.yml
+++ b/test/integration/targets/includes/test_includes2.yml
@@ -13,8 +13,8 @@
- role: test_includes
tags: test_includes
tasks:
- - include: roles/test_includes/tasks/not_a_role_task.yml
- - include: roles/test_includes/tasks/empty.yml
+ - include_tasks: roles/test_includes/tasks/not_a_role_task.yml
+ - include_tasks: roles/test_includes/tasks/empty.yml
- assert:
that:
- "ca == 33000"
diff --git a/test/integration/targets/includes/test_includes3.yml b/test/integration/targets/includes/test_includes3.yml
index 0b4c631..f3c4964 100644
--- a/test/integration/targets/includes/test_includes3.yml
+++ b/test/integration/targets/includes/test_includes3.yml
@@ -1,6 +1,6 @@
- hosts: testhost
tasks:
- - include: test_includes4.yml
+ - include_tasks: test_includes4.yml
with_items: ["a"]
loop_control:
loop_var: r
diff --git a/test/integration/targets/inventory/inventory_plugins/contructed_with_hostvars.py b/test/integration/targets/inventory/inventory_plugins/contructed_with_hostvars.py
index 7ca445a..43cad4f 100644
--- a/test/integration/targets/inventory/inventory_plugins/contructed_with_hostvars.py
+++ b/test/integration/targets/inventory/inventory_plugins/contructed_with_hostvars.py
@@ -14,7 +14,7 @@ DOCUMENTATION = '''
'''
from ansible.errors import AnsibleParserError
-from ansible.module_utils._text import to_native
+from ansible.module_utils.common.text.converters import to_native
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
diff --git a/test/integration/targets/inventory_ini/inventory.ini b/test/integration/targets/inventory_ini/inventory.ini
index a0c99ad..a5de421 100644
--- a/test/integration/targets/inventory_ini/inventory.ini
+++ b/test/integration/targets/inventory_ini/inventory.ini
@@ -1,3 +1,5 @@
+gitlab-runner-01 ansible_host=gitlab-runner-01.internal.example.net ansible_user=root
+
[local]
testhost ansible_connection=local ansible_become=no ansible_become_user=ansibletest1
diff --git a/test/integration/targets/inventory_ini/runme.sh b/test/integration/targets/inventory_ini/runme.sh
index 81bf147..919e188 100755
--- a/test/integration/targets/inventory_ini/runme.sh
+++ b/test/integration/targets/inventory_ini/runme.sh
@@ -3,3 +3,6 @@
set -eux
ansible-playbook -v -i inventory.ini test_ansible_become.yml
+
+ansible-inventory -v -i inventory.ini --list 2> out
+test "$(grep -c 'SyntaxWarning' out)" -eq 0
diff --git a/test/integration/targets/iptables/aliases b/test/integration/targets/iptables/aliases
index 7d66ecf..73df8aa 100644
--- a/test/integration/targets/iptables/aliases
+++ b/test/integration/targets/iptables/aliases
@@ -1,5 +1,4 @@
shippable/posix/group2
skip/freebsd
-skip/osx
skip/macos
skip/docker
diff --git a/test/integration/targets/iptables/tasks/chain_management.yml b/test/integration/targets/iptables/tasks/chain_management.yml
index 0355122..dae4103 100644
--- a/test/integration/targets/iptables/tasks/chain_management.yml
+++ b/test/integration/targets/iptables/tasks/chain_management.yml
@@ -45,6 +45,26 @@
- result is not failed
- '"FOOBAR-CHAIN" in result.stdout'
+- name: add rule to foobar chain
+ become: true
+ iptables:
+ chain: FOOBAR-CHAIN
+ source: 0.0.0.0
+ destination: 0.0.0.0
+ jump: DROP
+ comment: "FOOBAR-CHAIN RULE"
+
+- name: get the state of the iptable rules after rule is added to foobar chain
+ become: true
+ shell: "{{ iptables_bin }} -L"
+ register: result
+
+- name: assert rule is present in foobar chain
+ assert:
+ that:
+ - result is not failed
+ - '"FOOBAR-CHAIN RULE" in result.stdout'
+
- name: flush the foobar chain
become: true
iptables:
@@ -68,4 +88,3 @@
that:
- result is not failed
- '"FOOBAR-CHAIN" not in result.stdout'
- - '"FOOBAR-RULE" not in result.stdout'
diff --git a/test/integration/targets/known_hosts/defaults/main.yml b/test/integration/targets/known_hosts/defaults/main.yml
index b1b56ac..cd43843 100644
--- a/test/integration/targets/known_hosts/defaults/main.yml
+++ b/test/integration/targets/known_hosts/defaults/main.yml
@@ -3,4 +3,4 @@ example_org_rsa_key: >
example.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAglyZmHHWskQ9wkh8LYbIqzvg99/oloneH7BaZ02ripJUy/2Zynv4tgUfm9fdXvAb1XXCEuTRnts9FBer87+voU0FPRgx3CfY9Sgr0FspUjnm4lqs53FIab1psddAaS7/F7lrnjl6VqBtPwMRQZG7qlml5uogGJwYJHxX0PGtsdoTJsM=
example_org_ed25519_key: >
- example.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIzlnSq5ESxLgW0avvPk3j7zLV59hcAPkxrMNdnZMKP2 \ No newline at end of file
+ example.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIzlnSq5ESxLgW0avvPk3j7zLV59hcAPkxrMNdnZMKP2
diff --git a/test/integration/targets/known_hosts/tasks/main.yml b/test/integration/targets/known_hosts/tasks/main.yml
index dc00ded..d5ffec4 100644
--- a/test/integration/targets/known_hosts/tasks/main.yml
+++ b/test/integration/targets/known_hosts/tasks/main.yml
@@ -99,7 +99,7 @@
# https://github.com/ansible/ansible/issues/78598
# test removing nonexistent host key when the other keys exist for the host
- name: remove different key
- known_hosts:
+ known_hosts:
name: example.org
key: "{{ example_org_ed25519_key }}"
state: absent
diff --git a/test/integration/targets/lookup-option-name/aliases b/test/integration/targets/lookup-option-name/aliases
new file mode 100644
index 0000000..498fedd
--- /dev/null
+++ b/test/integration/targets/lookup-option-name/aliases
@@ -0,0 +1,2 @@
+shippable/posix/group4
+context/controller
diff --git a/test/integration/targets/lookup-option-name/tasks/main.yml b/test/integration/targets/lookup-option-name/tasks/main.yml
new file mode 100644
index 0000000..4f248c8
--- /dev/null
+++ b/test/integration/targets/lookup-option-name/tasks/main.yml
@@ -0,0 +1,6 @@
+---
+- debug:
+ msg: "{{ lookup('vars', name='test') }}"
+
+- debug:
+ msg: "{{ query('vars', name='test') }}"
diff --git a/test/integration/targets/lookup_config/tasks/main.yml b/test/integration/targets/lookup_config/tasks/main.yml
index 356d2f8..e5699d3 100644
--- a/test/integration/targets/lookup_config/tasks/main.yml
+++ b/test/integration/targets/lookup_config/tasks/main.yml
@@ -42,6 +42,7 @@
- name: remote user and port for ssh connection
set_fact:
ssh_user_and_port: '{{q("config", "remote_user", "port", plugin_type="connection", plugin_name="ssh")}}'
+ ssh_user_and_port_and_origin: '{{q("config", "remote_user", "port", plugin_type="connection", plugin_name="ssh", show_origin=True)}}'
vars:
ansible_ssh_user: lola
ansible_ssh_port: 2022
@@ -71,4 +72,5 @@
- lookup_config_7 is failed
- '"Invalid setting" in lookup_config_7.msg'
- ssh_user_and_port == ['lola', 2022]
+ - "ssh_user_and_port_and_origin == [['lola', 'var: ansible_ssh_user'], [2022, 'var: ansible_ssh_port']]"
- yolo_remote == ["yolo"]
diff --git a/test/integration/targets/lookup_fileglob/issue72873/test.yml b/test/integration/targets/lookup_fileglob/issue72873/test.yml
index 218ee58..92d93d4 100644
--- a/test/integration/targets/lookup_fileglob/issue72873/test.yml
+++ b/test/integration/targets/lookup_fileglob/issue72873/test.yml
@@ -5,7 +5,7 @@
dir: files
tasks:
- file: path='{{ dir }}' state=directory
-
+
- file: path='setvars.bat' state=touch # in current directory!
- file: path='{{ dir }}/{{ item }}' state=touch
@@ -20,11 +20,11 @@
- name: Get working order results and sort them
set_fact:
- working: '{{ query("fileglob", "setvars.bat", "{{ dir }}/*.[ch]") | sort }}'
+ working: '{{ query("fileglob", "setvars.bat", dir ~ "/*.[ch]") | sort }}'
- name: Get broken order results and sort them
set_fact:
- broken: '{{ query("fileglob", "{{ dir }}/*.[ch]", "setvars.bat") | sort }}'
+ broken: '{{ query("fileglob", dir ~ "/*.[ch]", "setvars.bat") | sort }}'
- assert:
that:
diff --git a/test/integration/targets/lookup_first_found/tasks/main.yml b/test/integration/targets/lookup_first_found/tasks/main.yml
index 9aeaf1d..ba248bd 100644
--- a/test/integration/targets/lookup_first_found/tasks/main.yml
+++ b/test/integration/targets/lookup_first_found/tasks/main.yml
@@ -94,3 +94,56 @@
- assert:
that:
- foo is defined
+
+# TODO: no 'terms' test
+- name: test first_found lookup with no terms
+ set_fact:
+ no_terms: "{{ query('first_found', files=['missing1', 'hosts', 'missing2'], paths=['/etc'], errors='ignore') }}"
+
+- assert:
+ that: "no_terms|first == '/etc/hosts'"
+
+- name: handle templatable dictionary entries
+ block:
+
+ - name: Load variables specific for OS family
+ assert:
+ that:
+ - "item is file"
+ - "item|basename == 'itworks.yml'"
+ with_first_found:
+ - files:
+ - "{{ansible_id}}-{{ansible_lsb.major_release}}.yml" # invalid var, should be skipped
+ - "{{ansible_lsb.id}}-{{ansible_lsb.major_release}}.yml" # does not exist, but should try
+ - "{{ansible_distribution}}-{{ansible_distribution_major_version}}.yml" # does not exist, but should try
+ - itworks.yml
+ - ishouldnotbefound.yml # this exist, but should not be found
+ paths:
+ - "{{role_path}}/vars"
+
+ - name: Load variables specific for OS family, but now as list of dicts, same options as above
+ assert:
+ that:
+ - "item is file"
+ - "item|basename == 'itworks.yml'"
+ with_first_found:
+ - files:
+ - "{{ansible_id}}-{{ansible_lsb.major_release}}.yml"
+ paths:
+ - "{{role_path}}/vars"
+ - files:
+ - "{{ansible_lsb.id}}-{{ansible_lsb.major_release}}.yml"
+ paths:
+ - "{{role_path}}/vars"
+ - files:
+ - "{{ansible_distribution}}-{{ansible_distribution_major_version}}.yml"
+ paths:
+ - "{{role_path}}/vars"
+ - files:
+ - itworks.yml
+ paths:
+ - "{{role_path}}/vars"
+ - files:
+ - ishouldnotbefound.yml
+ paths:
+ - "{{role_path}}/vars"
diff --git a/test/integration/targets/lookup_first_found/vars/ishouldnotbefound.yml b/test/integration/targets/lookup_first_found/vars/ishouldnotbefound.yml
new file mode 100644
index 0000000..e4cc6d5
--- /dev/null
+++ b/test/integration/targets/lookup_first_found/vars/ishouldnotbefound.yml
@@ -0,0 +1 @@
+really: i hide
diff --git a/test/integration/targets/lookup_first_found/vars/itworks.yml b/test/integration/targets/lookup_first_found/vars/itworks.yml
new file mode 100644
index 0000000..8f8a21a
--- /dev/null
+++ b/test/integration/targets/lookup_first_found/vars/itworks.yml
@@ -0,0 +1 @@
+doesit: yes it does
diff --git a/test/integration/targets/lookup_sequence/tasks/main.yml b/test/integration/targets/lookup_sequence/tasks/main.yml
index bd0a4d8..e64801d 100644
--- a/test/integration/targets/lookup_sequence/tasks/main.yml
+++ b/test/integration/targets/lookup_sequence/tasks/main.yml
@@ -195,4 +195,4 @@
- ansible_failed_task.name == "EXPECTED FAILURE - test bad format string message"
- ansible_failed_result.msg == expected
vars:
- expected: "bad formatting string: d" \ No newline at end of file
+ expected: "bad formatting string: d"
diff --git a/test/integration/targets/lookup_together/tasks/main.yml b/test/integration/targets/lookup_together/tasks/main.yml
index 71365a1..115c9e5 100644
--- a/test/integration/targets/lookup_together/tasks/main.yml
+++ b/test/integration/targets/lookup_together/tasks/main.yml
@@ -26,4 +26,4 @@
- assert:
that:
- ansible_failed_task.name == "EXPECTED FAILURE - test empty list"
- - ansible_failed_result.msg == "with_together requires at least one element in each list" \ No newline at end of file
+ - ansible_failed_result.msg == "with_together requires at least one element in each list"
diff --git a/test/integration/targets/lookup_url/aliases b/test/integration/targets/lookup_url/aliases
index ef37fce..19b7d98 100644
--- a/test/integration/targets/lookup_url/aliases
+++ b/test/integration/targets/lookup_url/aliases
@@ -1,4 +1,11 @@
destructive
shippable/posix/group3
needs/httptester
-skip/macos/12.0 # This test crashes Python due to https://wefearchange.org/2018/11/forkmacos.rst.html
+skip/macos # This test crashes Python due to https://wefearchange.org/2018/11/forkmacos.rst.html
+# Example failure:
+#
+# TASK [lookup_url : Test that retrieving a url works] ***************************
+# objc[15394]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called.
+# objc[15394]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in t
+# he fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
+# ERROR! A worker was found in a dead state
diff --git a/test/integration/targets/lookup_url/meta/main.yml b/test/integration/targets/lookup_url/meta/main.yml
index 374b5fd..6853708 100644
--- a/test/integration/targets/lookup_url/meta/main.yml
+++ b/test/integration/targets/lookup_url/meta/main.yml
@@ -1,2 +1,2 @@
-dependencies:
+dependencies:
- prepare_http_tests
diff --git a/test/integration/targets/lookup_url/tasks/main.yml b/test/integration/targets/lookup_url/tasks/main.yml
index 2fb227a..83fd5db 100644
--- a/test/integration/targets/lookup_url/tasks/main.yml
+++ b/test/integration/targets/lookup_url/tasks/main.yml
@@ -1,6 +1,6 @@
- name: Test that retrieving a url works
set_fact:
- web_data: "{{ lookup('url', 'https://{{ httpbin_host }}/get?one') }}"
+ web_data: "{{ lookup('url', 'https://' ~ httpbin_host ~ '/get?one') }}"
- name: Assert that the url was retrieved
assert:
@@ -9,7 +9,7 @@
- name: Test that retrieving a url with invalid cert fails
set_fact:
- web_data: "{{ lookup('url', 'https://{{ badssl_host }}/') }}"
+ web_data: "{{ lookup('url', 'https://' ~ badssl_host ~ '/') }}"
ignore_errors: True
register: url_invalid_cert
@@ -20,12 +20,12 @@
- name: Test that retrieving a url with invalid cert with validate_certs=False works
set_fact:
- web_data: "{{ lookup('url', 'https://{{ badssl_host }}/', validate_certs=False) }}"
+ web_data: "{{ lookup('url', 'https://' ~ badssl_host ~ '/', validate_certs=False) }}"
register: url_no_validate_cert
- assert:
that:
- - "'{{ badssl_host_substring }}' in web_data"
+ - badssl_host_substring in web_data
- vars:
url: https://{{ httpbin_host }}/get
@@ -52,3 +52,27 @@
- name: Test use_netrc=False
import_tasks: use_netrc.yml
+
+- vars:
+ ansible_lookup_url_agent: ansible-test-lookup-url-agent
+ block:
+ - name: Test user agent
+ set_fact:
+ web_data: "{{ lookup('url', 'https://' ~ httpbin_host ~ '/user-agent') }}"
+
+ - name: Assert that user agent is set
+ assert:
+ that:
+ - ansible_lookup_url_agent in web_data['user-agent']
+
+- vars:
+ ansible_lookup_url_force_basic_auth: yes
+ block:
+ - name: Test force basic auth
+ set_fact:
+ web_data: "{{ lookup('url', 'https://' ~ httpbin_host ~ '/headers', username='abc') }}"
+
+ - name: Assert that Authorization header is set
+ assert:
+ that:
+ - "'Authorization' in web_data.headers"
diff --git a/test/integration/targets/lookup_url/tasks/use_netrc.yml b/test/integration/targets/lookup_url/tasks/use_netrc.yml
index 68dc893..b90d05d 100644
--- a/test/integration/targets/lookup_url/tasks/use_netrc.yml
+++ b/test/integration/targets/lookup_url/tasks/use_netrc.yml
@@ -10,7 +10,7 @@
- name: test Url lookup with ~/.netrc forced Basic auth
set_fact:
- web_data: "{{ lookup('ansible.builtin.url', 'https://{{ httpbin_host }}/bearer', headers={'Authorization':'Bearer foobar'}) }}"
+ web_data: "{{ lookup('ansible.builtin.url', 'https://' ~ httpbin_host ~ '/bearer', headers={'Authorization':'Bearer foobar'}) }}"
ignore_errors: yes
- name: assert test Url lookup with ~/.netrc forced Basic auth
@@ -18,11 +18,11 @@
that:
- "web_data.token.find('v=' ~ 'Zm9vOmJhcg==') == -1"
fail_msg: "Was expecting 'foo:bar' in base64, but received: {{ web_data }}"
- success_msg: "Expected Basic authentication even Bearer headers were sent"
+ success_msg: "Expected Basic authentication even Bearer headers were sent"
- name: test Url lookup with use_netrc=False
set_fact:
- web_data: "{{ lookup('ansible.builtin.url', 'https://{{ httpbin_host }}/bearer', headers={'Authorization':'Bearer foobar'}, use_netrc='False') }}"
+ web_data: "{{ lookup('ansible.builtin.url', 'https://' ~ httpbin_host ~ '/bearer', headers={'Authorization':'Bearer foobar'}, use_netrc='False') }}"
- name: assert test Url lookup with netrc=False used Bearer authentication
assert:
@@ -34,4 +34,4 @@
- name: Clean up. Removing ~/.netrc
file:
path: ~/.netrc
- state: absent \ No newline at end of file
+ state: absent
diff --git a/test/integration/targets/loop-connection/collections/ansible_collections/ns/name/meta/runtime.yml b/test/integration/targets/loop-connection/collections/ansible_collections/ns/name/meta/runtime.yml
index 09322a9..bd892de 100644
--- a/test/integration/targets/loop-connection/collections/ansible_collections/ns/name/meta/runtime.yml
+++ b/test/integration/targets/loop-connection/collections/ansible_collections/ns/name/meta/runtime.yml
@@ -1,4 +1,4 @@
plugin_routing:
connection:
redirected_dummy:
- redirect: ns.name.dummy \ No newline at end of file
+ redirect: ns.name.dummy
diff --git a/test/integration/targets/loop-connection/main.yml b/test/integration/targets/loop-connection/main.yml
index fbffe30..ba60e64 100644
--- a/test/integration/targets/loop-connection/main.yml
+++ b/test/integration/targets/loop-connection/main.yml
@@ -30,4 +30,4 @@
- assert:
that:
- connected_test.results[0].stderr == "ran - 1"
- - connected_test.results[1].stderr == "ran - 2" \ No newline at end of file
+ - connected_test.results[1].stderr == "ran - 2"
diff --git a/test/integration/targets/missing_required_lib/library/missing_required_lib.py b/test/integration/targets/missing_required_lib/library/missing_required_lib.py
index 480ea00..8c7ba88 100644
--- a/test/integration/targets/missing_required_lib/library/missing_required_lib.py
+++ b/test/integration/targets/missing_required_lib/library/missing_required_lib.py
@@ -8,7 +8,7 @@ __metaclass__ = type
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
try:
- import ansible_missing_lib
+ import ansible_missing_lib # pylint: disable=unused-import
HAS_LIB = True
except ImportError as e:
HAS_LIB = False
diff --git a/test/integration/targets/module_defaults/action_plugins/debug.py b/test/integration/targets/module_defaults/action_plugins/debug.py
index 2584fd3..0c43201 100644
--- a/test/integration/targets/module_defaults/action_plugins/debug.py
+++ b/test/integration/targets/module_defaults/action_plugins/debug.py
@@ -20,7 +20,7 @@ __metaclass__ = type
from ansible.errors import AnsibleUndefinedVariable
from ansible.module_utils.six import string_types
-from ansible.module_utils._text import to_text
+from ansible.module_utils.common.text.converters import to_text
from ansible.plugins.action import ActionBase
diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py
index 0d39f26..174f372 100644
--- a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py
+++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py
@@ -5,7 +5,6 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.action.normal import ActionModule as ActionBase
-from ansible.utils.vars import merge_hash
class ActionModule(ActionBase):
diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py
index 20284fd..7ba2434 100644
--- a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py
+++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py
@@ -5,7 +5,6 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.action.normal import ActionModule as ActionBase
-from ansible.utils.vars import merge_hash
class ActionModule(ActionBase):
diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py
index b0e1904..67050fb 100644
--- a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py
+++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py
@@ -5,7 +5,6 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.action.normal import ActionModule as ActionBase
-from ansible.utils.vars import merge_hash
class ActionModule(ActionBase):
diff --git a/test/integration/targets/module_no_log/aliases b/test/integration/targets/module_no_log/aliases
index 9e84f63..afa1c9c 100644
--- a/test/integration/targets/module_no_log/aliases
+++ b/test/integration/targets/module_no_log/aliases
@@ -1,5 +1,4 @@
shippable/posix/group3
context/controller
skip/freebsd # not configured to log user.info to /var/log/syslog
-skip/osx # not configured to log user.info to /var/log/syslog
skip/macos # not configured to log user.info to /var/log/syslog
diff --git a/test/integration/targets/module_no_log/library/module_that_has_secret.py b/test/integration/targets/module_no_log/library/module_that_has_secret.py
new file mode 100644
index 0000000..035228c
--- /dev/null
+++ b/test/integration/targets/module_no_log/library/module_that_has_secret.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ module = AnsibleModule(argument_spec=dict(
+ secret=dict(no_log=True),
+ notsecret=dict(no_log=False),
+ ))
+
+ msg = "My secret is: (%s), but don't tell %s" % (module.params['secret'], module.params['notsecret'])
+ module.exit_json(msg=msg, changed=bool(module.params['secret'] == module.params['notsecret']))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/module_no_log/tasks/main.yml b/test/integration/targets/module_no_log/tasks/main.yml
index cf9e580..bf02410 100644
--- a/test/integration/targets/module_no_log/tasks/main.yml
+++ b/test/integration/targets/module_no_log/tasks/main.yml
@@ -59,3 +59,41 @@
# 2) the AnsibleModule.log method is not working
- good_message in grep.stdout
- bad_message not in grep.stdout
+
+- name: Ensure we do not obscure what we should not
+ block:
+ - module_that_has_secret:
+ secret: u
+ notsecret: u
+ register: ouch
+ ignore_errors: true
+
+ - name: no log wont obscure booleans when True, but still hide in msg
+ assert:
+ that:
+ - ouch['changed'] is boolean
+ - "'*' in ouch['msg']"
+
+ - module_that_has_secret:
+ secret: a
+ notsecret: b
+ register: ouch
+ ignore_errors: true
+
+ - name: no log wont obscure booleans when False, but still hide in msg
+ assert:
+ that:
+ - ouch['changed'] is boolean
+ - "'*' in ouch['msg']"
+
+ - module_that_has_secret:
+ secret: True
+ notsecret: False
+ register: ouch
+ ignore_errors: true
+
+ - name: no log does not hide bool values
+ assert:
+ that:
+ - ouch['changed'] is boolean
+ - "'*' not in ouch['msg']"
diff --git a/test/integration/targets/module_utils/library/test.py b/test/integration/targets/module_utils/library/test.py
index fb6c8a8..857d3d8 100644
--- a/test/integration/targets/module_utils/library/test.py
+++ b/test/integration/targets/module_utils/library/test.py
@@ -11,8 +11,8 @@ import ansible.module_utils.foo0
results['foo0'] = ansible.module_utils.foo0.data
# Test depthful import with no from
-import ansible.module_utils.bar0.foo
-results['bar0'] = ansible.module_utils.bar0.foo.data
+import ansible.module_utils.bar0.foo3
+results['bar0'] = ansible.module_utils.bar0.foo3.data
# Test import of module_utils/foo1.py
from ansible.module_utils import foo1
@@ -72,12 +72,12 @@ from ansible.module_utils.spam8.ham import eggs
results['spam8'] = (bacon.data, eggs)
# Test that import of module_utils/qux1/quux.py using as works
-from ansible.module_utils.qux1 import quux as one
-results['qux1'] = one.data
+from ansible.module_utils.qux1 import quux as two
+results['qux1'] = two.data
# Test that importing qux2/quux.py and qux2/quuz.py using as works
-from ansible.module_utils.qux2 import quux as one, quuz as two
-results['qux2'] = (one.data, two.data)
+from ansible.module_utils.qux2 import quux as three, quuz as four
+results['qux2'] = (three.data, four.data)
# Test depth
from ansible.module_utils.a.b.c.d.e.f.g.h import data
diff --git a/test/integration/targets/module_utils/library/test_failure.py b/test/integration/targets/module_utils/library/test_failure.py
index efb3dda..ab80cea 100644
--- a/test/integration/targets/module_utils/library/test_failure.py
+++ b/test/integration/targets/module_utils/library/test_failure.py
@@ -6,9 +6,9 @@ results = {}
# Test that we are rooted correctly
# Following files:
# module_utils/yak/zebra/foo.py
-from ansible.module_utils.zebra import foo
+from ansible.module_utils.zebra import foo4
-results['zebra'] = foo.data
+results['zebra'] = foo4.data
from ansible.module_utils.basic import AnsibleModule
AnsibleModule(argument_spec=dict()).exit_json(**results)
diff --git a/test/integration/targets/module_utils/module_utils/bar0/foo.py b/test/integration/targets/module_utils/module_utils/bar0/foo3.py
index 1072dcc..1072dcc 100644
--- a/test/integration/targets/module_utils/module_utils/bar0/foo.py
+++ b/test/integration/targets/module_utils/module_utils/bar0/foo3.py
diff --git a/test/integration/targets/module_utils/module_utils/foo.py b/test/integration/targets/module_utils/module_utils/foo.py
deleted file mode 100644
index 20698f1..0000000
--- a/test/integration/targets/module_utils/module_utils/foo.py
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env python
-
-foo = "FOO FROM foo.py"
diff --git a/test/integration/targets/module_utils/module_utils/sub/bar/__init__.py b/test/integration/targets/module_utils/module_utils/sub/bar/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/test/integration/targets/module_utils/module_utils/sub/bar/__init__.py
+++ /dev/null
diff --git a/test/integration/targets/module_utils/module_utils/sub/bar/bam.py b/test/integration/targets/module_utils/module_utils/sub/bar/bam.py
deleted file mode 100644
index 02fafd4..0000000
--- a/test/integration/targets/module_utils/module_utils/sub/bar/bam.py
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env python
-
-bam = "BAM FROM sub/bar/bam.py"
diff --git a/test/integration/targets/module_utils/module_utils/sub/bar/bar.py b/test/integration/targets/module_utils/module_utils/sub/bar/bar.py
deleted file mode 100644
index 8566901..0000000
--- a/test/integration/targets/module_utils/module_utils/sub/bar/bar.py
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env python
-
-bar = "BAR FROM sub/bar/bar.py"
diff --git a/test/integration/targets/module_utils/module_utils/yak/zebra/foo.py b/test/integration/targets/module_utils/module_utils/yak/zebra/foo4.py
index 89b2bfe..89b2bfe 100644
--- a/test/integration/targets/module_utils/module_utils/yak/zebra/foo.py
+++ b/test/integration/targets/module_utils/module_utils/yak/zebra/foo4.py
diff --git a/test/integration/targets/module_utils/module_utils_test.yml b/test/integration/targets/module_utils/module_utils_test.yml
index 4e948bd..352bc58 100644
--- a/test/integration/targets/module_utils/module_utils_test.yml
+++ b/test/integration/targets/module_utils/module_utils_test.yml
@@ -47,7 +47,7 @@
assert:
that:
- result is failed
- - result['msg'] == "Could not find imported module support code for ansible.modules.test_failure. Looked for (['ansible.module_utils.zebra.foo', 'ansible.module_utils.zebra'])"
+ - result['msg'] == "Could not find imported module support code for ansible.modules.test_failure. Looked for (['ansible.module_utils.zebra.foo4', 'ansible.module_utils.zebra'])"
- name: Test that alias deprecation works
test_alias_deprecation:
diff --git a/test/integration/targets/module_utils_Ansible.Basic/library/ansible_basic_tests.ps1 b/test/integration/targets/module_utils_Ansible.Basic/library/ansible_basic_tests.ps1
index 6170f04..9644df9 100644
--- a/test/integration/targets/module_utils_Ansible.Basic/library/ansible_basic_tests.ps1
+++ b/test/integration/targets/module_utils_Ansible.Basic/library/ansible_basic_tests.ps1
@@ -87,7 +87,7 @@ Function Assert-DictionaryEqual {
}
Function Exit-Module {
- # Make sure Exit actually calls exit and not our overriden test behaviour
+ # Make sure Exit actually calls exit and not our overridden test behaviour
[Ansible.Basic.AnsibleModule]::Exit = { param([Int32]$rc) exit $rc }
Write-Output -InputObject (ConvertTo-Json -InputObject $module.Result -Compress -Depth 99)
$module.ExitJson()
diff --git a/test/integration/targets/module_utils_Ansible.ModuleUtils.AddType/library/add_type_test.ps1 b/test/integration/targets/module_utils_Ansible.ModuleUtils.AddType/library/add_type_test.ps1
index d18c42d..5cb1a72 100644
--- a/test/integration/targets/module_utils_Ansible.ModuleUtils.AddType/library/add_type_test.ps1
+++ b/test/integration/targets/module_utils_Ansible.ModuleUtils.AddType/library/add_type_test.ps1
@@ -328,5 +328,73 @@ finally {
}
Assert-Equal -actual ([Namespace12.Class12]::GetString()) -expected "b"
+$unsafe_code_fail = @'
+using System;
+
+namespace Namespace13
+{
+ public class Class13
+ {
+
+ public static int GetNumber()
+ {
+ int num = 2;
+ int* numPtr = &num;
+
+ DoubleNumber(numPtr);
+
+ return num;
+ }
+
+ private unsafe static void DoubleNumber(int* num)
+ {
+ *num = *num * 3;
+ }
+ }
+}
+'@
+$failed = $false
+try {
+ Add-CSharpType -Reference $unsafe_code_fail
+}
+catch {
+ $failed = $true
+ $actual = $_.Exception.Message.Contains("error CS0227: Unsafe code may only appear if compiling with /unsafe")
+ Assert-Equal -actual $actual -expected $true
+}
+Assert-Equal -actual $failed -expected $true
+
+$unsafe_code = @'
+using System;
+
+//AllowUnsafe
+
+namespace Namespace13
+{
+ public class Class13
+ {
+ public static int GetNumber()
+ {
+ int num = 2;
+ unsafe
+ {
+ int* numPtr = &num;
+
+ DoubleNumber(numPtr);
+ }
+
+ return num;
+ }
+
+ private unsafe static void DoubleNumber(int* num)
+ {
+ *num = *num * 2;
+ }
+ }
+}
+'@
+Add-CSharpType -Reference $unsafe_code
+Assert-Equal -actual ([Namespace13.Class13]::GetNumber()) -expected 4
+
$result.res = "success"
Exit-Json -obj $result
diff --git a/test/integration/targets/no_log/no_log_config.yml b/test/integration/targets/no_log/no_log_config.yml
new file mode 100644
index 0000000..8a50880
--- /dev/null
+++ b/test/integration/targets/no_log/no_log_config.yml
@@ -0,0 +1,13 @@
+- hosts: testhost
+ gather_facts: false
+ tasks:
+ - debug:
+ no_log: true
+
+ - debug:
+ no_log: false
+
+ - debug:
+
+ - debug:
+ loop: '{{ range(3) }}'
diff --git a/test/integration/targets/no_log/runme.sh b/test/integration/targets/no_log/runme.sh
index bb5c048..bf764bf 100755
--- a/test/integration/targets/no_log/runme.sh
+++ b/test/integration/targets/no_log/runme.sh
@@ -5,7 +5,7 @@ set -eux
# This test expects 7 loggable vars and 0 non-loggable ones.
# If either mismatches it fails, run the ansible-playbook command to debug.
[ "$(ansible-playbook no_log_local.yml -i ../../inventory -vvvvv "$@" | awk \
-'BEGIN { logme = 0; nolog = 0; } /LOG_ME/ { logme += 1;} /DO_NOT_LOG/ { nolog += 1;} END { printf "%d/%d", logme, nolog; }')" = "26/0" ]
+'BEGIN { logme = 0; nolog = 0; } /LOG_ME/ { logme += 1;} /DO_NOT_LOG/ { nolog += 1;} END { printf "%d/%d", logme, nolog; }')" = "27/0" ]
# deal with corner cases with no log and loops
# no log enabled, should produce 6 censored messages
@@ -19,3 +19,8 @@ set -eux
# test invalid data passed to a suboption
[ "$(ansible-playbook no_log_suboptions_invalid.yml -i ../../inventory -vvvvv "$@" | grep -Ec '(SUPREME|IDIOM|MOCKUP|EDUCATED|FOOTREST|CRAFTY|FELINE|CRYSTAL|EXPECTANT|AGROUND|GOLIATH|FREEFALL)')" = "0" ]
+
+# test variations on ANSIBLE_NO_LOG
+[ "$(ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ]
+[ "$(ANSIBLE_NO_LOG=0 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ]
+[ "$(ANSIBLE_NO_LOG=1 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "6" ]
diff --git a/test/integration/targets/old_style_cache_plugins/aliases b/test/integration/targets/old_style_cache_plugins/aliases
index 3777383..163129e 100644
--- a/test/integration/targets/old_style_cache_plugins/aliases
+++ b/test/integration/targets/old_style_cache_plugins/aliases
@@ -2,5 +2,4 @@ destructive
needs/root
shippable/posix/group5
context/controller
-skip/osx
skip/macos
diff --git a/test/integration/targets/old_style_cache_plugins/plugins/cache/configurable_redis.py b/test/integration/targets/old_style_cache_plugins/plugins/cache/configurable_redis.py
index 44b6cf9..23c7789 100644
--- a/test/integration/targets/old_style_cache_plugins/plugins/cache/configurable_redis.py
+++ b/test/integration/targets/old_style_cache_plugins/plugins/cache/configurable_redis.py
@@ -44,7 +44,6 @@ DOCUMENTATION = '''
import time
import json
-from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.parsing.ajson import AnsibleJSONEncoder, AnsibleJSONDecoder
from ansible.plugins.cache import BaseCacheModule
diff --git a/test/integration/targets/old_style_cache_plugins/setup_redis_cache.yml b/test/integration/targets/old_style_cache_plugins/setup_redis_cache.yml
index 8aad37a..b7cd483 100644
--- a/test/integration/targets/old_style_cache_plugins/setup_redis_cache.yml
+++ b/test/integration/targets/old_style_cache_plugins/setup_redis_cache.yml
@@ -20,8 +20,9 @@
- name: get the latest stable redis server release
get_url:
- url: http://download.redis.io/redis-stable.tar.gz
+ url: https://download.redis.io/redis-stable.tar.gz
dest: ./
+ timeout: 60
- name: unzip download
unarchive:
diff --git a/test/integration/targets/old_style_vars_plugins/deprecation_warning/v2_vars_plugin.py b/test/integration/targets/old_style_vars_plugins/deprecation_warning/v2_vars_plugin.py
new file mode 100644
index 0000000..f342b69
--- /dev/null
+++ b/test/integration/targets/old_style_vars_plugins/deprecation_warning/v2_vars_plugin.py
@@ -0,0 +1,6 @@
+class VarsModule:
+ def get_host_vars(self, entity):
+ return {}
+
+ def get_group_vars(self, entity):
+ return {}
diff --git a/test/integration/targets/old_style_vars_plugins/deprecation_warning/vars.py b/test/integration/targets/old_style_vars_plugins/deprecation_warning/vars.py
index d5c9a42..f554be0 100644
--- a/test/integration/targets/old_style_vars_plugins/deprecation_warning/vars.py
+++ b/test/integration/targets/old_style_vars_plugins/deprecation_warning/vars.py
@@ -2,7 +2,7 @@ from ansible.plugins.vars import BaseVarsPlugin
class VarsModule(BaseVarsPlugin):
- REQUIRES_WHITELIST = False
+ REQUIRES_WHITELIST = True
def get_vars(self, loader, path, entities):
return {}
diff --git a/test/integration/targets/old_style_vars_plugins/roles/a/tasks/main.yml b/test/integration/targets/old_style_vars_plugins/roles/a/tasks/main.yml
new file mode 100644
index 0000000..8e0742a
--- /dev/null
+++ b/test/integration/targets/old_style_vars_plugins/roles/a/tasks/main.yml
@@ -0,0 +1,3 @@
+- assert:
+ that:
+ - auto_role_var is defined
diff --git a/test/integration/targets/old_style_vars_plugins/roles/a/vars_plugins/auto_role_vars.py b/test/integration/targets/old_style_vars_plugins/roles/a/vars_plugins/auto_role_vars.py
new file mode 100644
index 0000000..a1cd30d
--- /dev/null
+++ b/test/integration/targets/old_style_vars_plugins/roles/a/vars_plugins/auto_role_vars.py
@@ -0,0 +1,11 @@
+from __future__ import annotations
+
+from ansible.plugins.vars import BaseVarsPlugin
+
+
+class VarsModule(BaseVarsPlugin):
+ # Implicitly
+ # REQUIRES_ENABLED = False
+
+ def get_vars(self, loader, path, entities):
+ return {'auto_role_var': True}
diff --git a/test/integration/targets/old_style_vars_plugins/runme.sh b/test/integration/targets/old_style_vars_plugins/runme.sh
index 4cd1916..9f41623 100755
--- a/test/integration/targets/old_style_vars_plugins/runme.sh
+++ b/test/integration/targets/old_style_vars_plugins/runme.sh
@@ -12,9 +12,39 @@ export ANSIBLE_VARS_PLUGINS=./vars_plugins
export ANSIBLE_VARS_ENABLED=require_enabled
[ "$(ansible-inventory -i localhost, --list --yaml all "$@" | grep -c 'require_enabled')" = "1" ]
-# Test the deprecated class attribute
+# Test deprecated features
export ANSIBLE_VARS_PLUGINS=./deprecation_warning
-WARNING="The VarsModule class variable 'REQUIRES_WHITELIST' is deprecated. Use 'REQUIRES_ENABLED' instead."
+WARNING_1="The VarsModule class variable 'REQUIRES_WHITELIST' is deprecated. Use 'REQUIRES_ENABLED' instead."
+WARNING_2="The vars plugin v2_vars_plugin .* is relying on the deprecated entrypoints 'get_host_vars' and 'get_group_vars'"
ANSIBLE_DEPRECATION_WARNINGS=True ANSIBLE_NOCOLOR=True ANSIBLE_FORCE_COLOR=False \
- ansible-inventory -i localhost, --list all 2> err.txt
-ansible localhost -m debug -a "msg={{ lookup('file', 'err.txt') | regex_replace('\n', '') }}" | grep "$WARNING"
+ ansible-inventory -i localhost, --list all "$@" 2> err.txt
+for WARNING in "$WARNING_1" "$WARNING_2"; do
+ ansible localhost -m debug -a "msg={{ lookup('file', 'err.txt') | regex_replace('\n', '') }}" | grep "$WARNING"
+done
+
+# Test how many times vars plugins are loaded for a simple play containing a task
+# host_group_vars is stateless, so we can load it once and reuse it, every other vars plugin should be instantiated before it runs
+cat << EOF > "test_task_vars.yml"
+---
+- hosts: localhost
+ connection: local
+ gather_facts: no
+ tasks:
+ - debug:
+EOF
+
+# hide the debug noise by dumping to a file
+trap 'rm -rf -- "out.txt"' EXIT
+
+ANSIBLE_DEBUG=True ansible-playbook test_task_vars.yml > out.txt
+[ "$(grep -c "Loading VarsModule 'host_group_vars'" out.txt)" -eq 1 ]
+[ "$(grep -c "Loading VarsModule 'require_enabled'" out.txt)" -gt 50 ]
+[ "$(grep -c "Loading VarsModule 'auto_enabled'" out.txt)" -gt 50 ]
+
+export ANSIBLE_VARS_ENABLED=ansible.builtin.host_group_vars
+ANSIBLE_DEBUG=True ansible-playbook test_task_vars.yml > out.txt
+[ "$(grep -c "Loading VarsModule 'host_group_vars'" out.txt)" -eq 1 ]
+[ "$(grep -c "Loading VarsModule 'require_enabled'" out.txt)" -lt 3 ]
+[ "$(grep -c "Loading VarsModule 'auto_enabled'" out.txt)" -gt 50 ]
+
+ansible localhost -m include_role -a 'name=a' "$@"
diff --git a/test/integration/targets/omit/75692.yml b/test/integration/targets/omit/75692.yml
index b4000c9..5ba8a2d 100644
--- a/test/integration/targets/omit/75692.yml
+++ b/test/integration/targets/omit/75692.yml
@@ -2,10 +2,10 @@
hosts: testhost
gather_facts: false
become: yes
+ # become_user needed at play level for testing this behavior
become_user: nobody
roles:
- name: setup_test_user
- become: yes
become_user: root
tasks:
- shell: whoami
diff --git a/test/integration/targets/package/tasks/main.yml b/test/integration/targets/package/tasks/main.yml
index c17525d..37267aa 100644
--- a/test/integration/targets/package/tasks/main.yml
+++ b/test/integration/targets/package/tasks/main.yml
@@ -239,4 +239,4 @@
that:
- "result is changed"
- when: ansible_distribution == "Fedora" \ No newline at end of file
+ when: ansible_distribution == "Fedora"
diff --git a/test/integration/targets/package_facts/aliases b/test/integration/targets/package_facts/aliases
index 5a5e464..f5edf4b 100644
--- a/test/integration/targets/package_facts/aliases
+++ b/test/integration/targets/package_facts/aliases
@@ -1,3 +1,2 @@
shippable/posix/group2
-skip/osx
skip/macos
diff --git a/test/integration/targets/parsing/bad_parsing.yml b/test/integration/targets/parsing/bad_parsing.yml
deleted file mode 100644
index 953ec07..0000000
--- a/test/integration/targets/parsing/bad_parsing.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-- hosts: testhost
-
- # the following commands should all parse fine and execute fine
- # and represent quoting scenarios that should be legit
-
- gather_facts: False
-
- roles:
-
- # this one has a lot of things that should fail, see makefile for operation w/ tags
-
- - { role: test_bad_parsing }
diff --git a/test/integration/targets/parsing/parsing.yml b/test/integration/targets/parsing/parsing.yml
new file mode 100644
index 0000000..9d5ff41
--- /dev/null
+++ b/test/integration/targets/parsing/parsing.yml
@@ -0,0 +1,35 @@
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ - name: test that a variable cannot inject raw arguments
+ shell: echo hi {{ chdir }}
+ vars:
+ chdir: mom chdir=/tmp
+ register: raw_injection
+
+ - name: test that a variable cannot inject kvp arguments as a kvp
+ file: path={{ test_file }} {{ test_input }}
+ vars:
+ test_file: "{{ output_dir }}/ansible_test_file"
+ test_input: "owner=test"
+ register: kvp_kvp_injection
+ ignore_errors: yes
+
+ - name: test that a variable cannot inject kvp arguments as a value
+ file: state=absent path='{{ kvp_in_var }}'
+ vars:
+ kvp_in_var: "{{ output_dir }}' owner='test"
+ register: kvp_value_injection
+
+ - name: test that a missing filter fails
+ debug:
+ msg: "{{ output_dir | badfiltername }}"
+ register: filter_missing
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - raw_injection.stdout == 'hi mom chdir=/tmp'
+ - kvp_kvp_injection is failed
+ - kvp_value_injection.path.endswith("' owner='test")
+ - filter_missing is failed
diff --git a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/main.yml b/test/integration/targets/parsing/roles/test_bad_parsing/tasks/main.yml
deleted file mode 100644
index f1b2ec6..0000000
--- a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/main.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-# test code for the ping module
-# (c) 2014, Michael DeHaan <michael@ansible.com>
-
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-
-# the following tests all raise errors, to use them in a Makefile, we run them with different flags, as
-# otherwise ansible stops at the first one and we want to ensure STOP conditions for each
-
-- set_fact:
- test_file: "{{ output_dir }}/ansible_test_file" # FIXME, use set tempdir
- test_input: "owner=test"
- bad_var: "{{ output_dir }}' owner=test"
- chdir: "mom chdir=/tmp"
- tags: common
-
-- file: name={{test_file}} state=touch
- tags: common
-
-- name: remove touched file
- file: name={{test_file}} state=absent
- tags: common
-
-- name: include test that we cannot insert arguments
- include: scenario1.yml
- tags: scenario1
-
-- name: include test that we cannot duplicate arguments
- include: scenario2.yml
- tags: scenario2
-
-- name: include test that we can't do this for the shell module
- include: scenario3.yml
- tags: scenario3
-
-- name: include test that we can't go all Little Bobby Droptables on a quoted var to add more
- include: scenario4.yml
- tags: scenario4
-
-- name: test that a missing/malformed jinja2 filter fails
- debug: msg="{{output_dir|badfiltername}}"
- tags: scenario5
- register: filter_fail
- ignore_errors: yes
-
-- assert:
- that:
- - filter_fail is failed
diff --git a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario1.yml b/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario1.yml
deleted file mode 100644
index 8a82fb9..0000000
--- a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario1.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-- name: test that we cannot insert arguments
- file: path={{ test_file }} {{ test_input }}
- failed_when: False # ignore the module, just test the parser
- tags: scenario1
diff --git a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario2.yml b/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario2.yml
deleted file mode 100644
index c3b4b13..0000000
--- a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario2.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-- name: test that we cannot duplicate arguments
- file: path={{ test_file }} owner=test2 {{ test_input }}
- failed_when: False # ignore the module, just test the parser
- tags: scenario2
diff --git a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario3.yml b/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario3.yml
deleted file mode 100644
index a228f70..0000000
--- a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario3.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-- name: test that we can't do this for the shell module
- shell: echo hi {{ chdir }}
- failed_when: False
- tags: scenario3
diff --git a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario4.yml b/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario4.yml
deleted file mode 100644
index 2845adc..0000000
--- a/test/integration/targets/parsing/roles/test_bad_parsing/tasks/scenario4.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-- name: test that we can't go all Little Bobby Droptables on a quoted var to add more
- file: "name={{ bad_var }}"
- failed_when: False
- tags: scenario4
diff --git a/test/integration/targets/parsing/roles/test_bad_parsing/vars/main.yml b/test/integration/targets/parsing/roles/test_bad_parsing/vars/main.yml
deleted file mode 100644
index 1aaeac7..0000000
--- a/test/integration/targets/parsing/roles/test_bad_parsing/vars/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
----
-output_dir: .
diff --git a/test/integration/targets/parsing/roles/test_good_parsing/tasks/main.yml b/test/integration/targets/parsing/roles/test_good_parsing/tasks/main.yml
index d225c0f..25e91f2 100644
--- a/test/integration/targets/parsing/roles/test_good_parsing/tasks/main.yml
+++ b/test/integration/targets/parsing/roles/test_good_parsing/tasks/main.yml
@@ -121,7 +121,10 @@
- result.cmd == "echo foo --arg=a --arg=b"
- name: test includes with params
- include: test_include.yml fact_name=include_params param="{{ test_input }}"
+ include_tasks: test_include.yml
+ vars:
+ fact_name: include_params
+ param: "{{ test_input }}"
- name: assert the include set the correct fact for the param
assert:
@@ -129,7 +132,10 @@
- include_params == test_input
- name: test includes with quoted params
- include: test_include.yml fact_name=double_quoted_param param="this is a param with double quotes"
+ include_tasks: test_include.yml
+ vars:
+ fact_name: double_quoted_param
+ param: "this is a param with double quotes"
- name: assert the include set the correct fact for the double quoted param
assert:
@@ -137,7 +143,10 @@
- double_quoted_param == "this is a param with double quotes"
- name: test includes with single quoted params
- include: test_include.yml fact_name=single_quoted_param param='this is a param with single quotes'
+ include_tasks: test_include.yml
+ vars:
+ fact_name: single_quoted_param
+ param: 'this is a param with single quotes'
- name: assert the include set the correct fact for the single quoted param
assert:
@@ -145,7 +154,7 @@
- single_quoted_param == "this is a param with single quotes"
- name: test includes with quoted params in complex args
- include: test_include.yml
+ include_tasks: test_include.yml
vars:
fact_name: complex_param
param: "this is a param in a complex arg with double quotes"
@@ -165,7 +174,7 @@
- result.msg == "this should be debugged"
- name: test conditional includes
- include: test_include_conditional.yml
+ include_tasks: test_include_conditional.yml
when: false
- name: assert the nested include from test_include_conditional was not set
diff --git a/test/integration/targets/parsing/roles/test_good_parsing/tasks/test_include_conditional.yml b/test/integration/targets/parsing/roles/test_good_parsing/tasks/test_include_conditional.yml
index 070888d..a1d8b7c 100644
--- a/test/integration/targets/parsing/roles/test_good_parsing/tasks/test_include_conditional.yml
+++ b/test/integration/targets/parsing/roles/test_good_parsing/tasks/test_include_conditional.yml
@@ -1 +1 @@
-- include: test_include_nested.yml
+- include_tasks: test_include_nested.yml
diff --git a/test/integration/targets/parsing/runme.sh b/test/integration/targets/parsing/runme.sh
index 022ce4c..2d55008 100755
--- a/test/integration/targets/parsing/runme.sh
+++ b/test/integration/targets/parsing/runme.sh
@@ -2,5 +2,5 @@
set -eux
-ansible-playbook bad_parsing.yml -i ../../inventory -vvv "$@" --tags prepare,common,scenario5
-ansible-playbook good_parsing.yml -i ../../inventory -v "$@"
+ansible-playbook parsing.yml -i ../../inventory "$@" -e "output_dir=${OUTPUT_DIR}"
+ansible-playbook good_parsing.yml -i ../../inventory "$@"
diff --git a/test/integration/targets/path_lookups/roles/showfile/tasks/main.yml b/test/integration/targets/path_lookups/roles/showfile/tasks/notmain.yml
index 1b38057..1b38057 100644
--- a/test/integration/targets/path_lookups/roles/showfile/tasks/main.yml
+++ b/test/integration/targets/path_lookups/roles/showfile/tasks/notmain.yml
diff --git a/test/integration/targets/path_lookups/testplay.yml b/test/integration/targets/path_lookups/testplay.yml
index 8bf4553..bc05c7e 100644
--- a/test/integration/targets/path_lookups/testplay.yml
+++ b/test/integration/targets/path_lookups/testplay.yml
@@ -4,9 +4,11 @@
pre_tasks:
- name: remove {{ remove }}
file: path={{ playbook_dir }}/{{ remove }} state=absent
- roles:
- - showfile
- post_tasks:
+ tasks:
+ - import_role:
+ name: showfile
+ tasks_from: notmain.yml
+
- name: from play
set_fact: play_result="{{lookup('file', 'testfile')}}"
diff --git a/test/integration/targets/pause/pause-6.yml b/test/integration/targets/pause/pause-6.yml
new file mode 100644
index 0000000..f7315bb
--- /dev/null
+++ b/test/integration/targets/pause/pause-6.yml
@@ -0,0 +1,25 @@
+- name: Test pause module input isn't captured with a timeout
+ hosts: localhost
+ become: no
+ gather_facts: no
+
+ tasks:
+ - name: pause with the default message
+ pause:
+ seconds: 3
+ register: default_msg_pause
+
+ - name: pause with a custom message
+ pause:
+ prompt: Wait for three seconds
+ seconds: 3
+ register: custom_msg_pause
+
+ - name: Ensure that input was not captured
+ assert:
+ that:
+ - default_msg_pause.user_input == ''
+ - custom_msg_pause.user_input == ''
+
+ - debug:
+ msg: Task after pause
diff --git a/test/integration/targets/pause/test-pause.py b/test/integration/targets/pause/test-pause.py
index 3703470..ab771fa 100755
--- a/test/integration/targets/pause/test-pause.py
+++ b/test/integration/targets/pause/test-pause.py
@@ -168,7 +168,9 @@ pause_test = pexpect.spawn(
pause_test.logfile = log_buffer
pause_test.expect(r'Pausing for \d+ seconds')
pause_test.expect(r"\(ctrl\+C then 'C' = continue early, ctrl\+C then 'A' = abort\)")
+pause_test.send('\n') # test newline does not stop the prompt - waiting for a timeout or ctrl+C
pause_test.send('\x03')
+pause_test.expect("Press 'C' to continue the play or 'A' to abort")
pause_test.send('C')
pause_test.expect('Task after pause')
pause_test.expect(pexpect.EOF)
@@ -187,6 +189,7 @@ pause_test.logfile = log_buffer
pause_test.expect(r'Pausing for \d+ seconds')
pause_test.expect(r"\(ctrl\+C then 'C' = continue early, ctrl\+C then 'A' = abort\)")
pause_test.send('\x03')
+pause_test.expect("Press 'C' to continue the play or 'A' to abort")
pause_test.send('A')
pause_test.expect('user requested abort!')
pause_test.expect(pexpect.EOF)
@@ -225,6 +228,7 @@ pause_test.expect(r'Pausing for \d+ seconds')
pause_test.expect(r"\(ctrl\+C then 'C' = continue early, ctrl\+C then 'A' = abort\)")
pause_test.expect(r"Waiting for two seconds:")
pause_test.send('\x03')
+pause_test.expect("Press 'C' to continue the play or 'A' to abort")
pause_test.send('C')
pause_test.expect('Task after pause')
pause_test.expect(pexpect.EOF)
@@ -244,6 +248,7 @@ pause_test.expect(r'Pausing for \d+ seconds')
pause_test.expect(r"\(ctrl\+C then 'C' = continue early, ctrl\+C then 'A' = abort\)")
pause_test.expect(r"Waiting for two seconds:")
pause_test.send('\x03')
+pause_test.expect("Press 'C' to continue the play or 'A' to abort")
pause_test.send('A')
pause_test.expect('user requested abort!')
pause_test.expect(pexpect.EOF)
@@ -275,6 +280,24 @@ pause_test.send('\r')
pause_test.expect(pexpect.EOF)
pause_test.close()
+# Test input is not returned if a timeout is given
+
+playbook = 'pause-6.yml'
+
+pause_test = pexpect.spawn(
+ 'ansible-playbook',
+ args=[playbook] + args,
+ timeout=10,
+ env=os.environ
+)
+
+pause_test.logfile = log_buffer
+pause_test.expect(r'Wait for three seconds:')
+pause_test.send('ignored user input')
+pause_test.expect('Task after pause')
+pause_test.expect(pexpect.EOF)
+pause_test.close()
+
# Test that enter presses may not continue the play when a timeout is set.
diff --git a/test/integration/targets/pip/tasks/main.yml b/test/integration/targets/pip/tasks/main.yml
index 66992fd..a377070 100644
--- a/test/integration/targets/pip/tasks/main.yml
+++ b/test/integration/targets/pip/tasks/main.yml
@@ -40,6 +40,9 @@
extra_args: "-c {{ remote_constraints }}"
- include_tasks: pip.yml
+
+ - include_tasks: no_setuptools.yml
+ when: ansible_python.version_info[:2] >= [3, 8]
always:
- name: platform specific cleanup
include_tasks: "{{ cleanup_filename }}"
diff --git a/test/integration/targets/pip/tasks/no_setuptools.yml b/test/integration/targets/pip/tasks/no_setuptools.yml
new file mode 100644
index 0000000..695605e
--- /dev/null
+++ b/test/integration/targets/pip/tasks/no_setuptools.yml
@@ -0,0 +1,48 @@
+- name: Get coverage version
+ pip:
+ name: coverage
+ check_mode: true
+ register: pip_coverage
+
+- name: create a virtualenv for use without setuptools
+ pip:
+ name:
+ - packaging
+ # coverage is needed when ansible-test is invoked with --coverage
+ # and using a custom ansible_python_interpreter below
+ - '{{ pip_coverage.stdout_lines|select("match", "coverage==")|first }}'
+ virtualenv: "{{ remote_tmp_dir }}/no_setuptools"
+
+- name: Remove setuptools
+ pip:
+ name:
+ - setuptools
+ - pkg_resources # This shouldn't be a thing, but ubuntu 20.04...
+ virtualenv: "{{ remote_tmp_dir }}/no_setuptools"
+ state: absent
+
+- name: Ensure pkg_resources is gone
+ command: "{{ remote_tmp_dir }}/no_setuptools/bin/python -c 'import pkg_resources'"
+ register: result
+ failed_when: result.rc == 0
+
+- vars:
+ ansible_python_interpreter: "{{ remote_tmp_dir }}/no_setuptools/bin/python"
+ block:
+ - name: Checkmode install pip
+ pip:
+ name: pip
+ virtualenv: "{{ remote_tmp_dir }}/no_setuptools"
+ check_mode: true
+ register: pip_check_mode
+
+ - assert:
+ that:
+ - pip_check_mode.stdout is contains "pip=="
+ - pip_check_mode.stdout is not contains "setuptools=="
+
+ - name: Install fallible
+ pip:
+ name: fallible==0.0.1a2
+ virtualenv: "{{ remote_tmp_dir }}/no_setuptools"
+ register: fallible_install
diff --git a/test/integration/targets/pip/tasks/pip.yml b/test/integration/targets/pip/tasks/pip.yml
index 3948061..9f1034d 100644
--- a/test/integration/targets/pip/tasks/pip.yml
+++ b/test/integration/targets/pip/tasks/pip.yml
@@ -568,6 +568,28 @@
that:
- "version13 is success"
+- name: Test virtualenv command with venv formatting
+ when: ansible_python.version.major > 2
+ block:
+ - name: Clean up the virtualenv
+ file:
+ state: absent
+ name: "{{ remote_tmp_dir }}/pipenv"
+
+ # ref: https://github.com/ansible/ansible/issues/76372
+ - name: install using different venv formatting
+ pip:
+ name: "{{ pip_test_package }}"
+ virtualenv: "{{ remote_tmp_dir }}/pipenv"
+ virtualenv_command: "{{ ansible_python_interpreter ~ ' -mvenv' }}"
+ state: present
+ register: version14
+
+ - name: ensure install using virtualenv_command with venv formatting
+ assert:
+ that:
+ - "version14 is changed"
+
### test virtualenv_command end ###
# https://github.com/ansible/ansible/issues/68592
diff --git a/test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py b/test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py
index 9f1c5c0..44412f2 100644
--- a/test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py
+++ b/test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py
@@ -11,7 +11,7 @@ __metaclass__ = type
# noinspection PyUnresolvedReferences
try:
- from pkg_resources import Requirement
+ from pkg_resources import Requirement # pylint: disable=unused-import
except ImportError:
Requirement = None
diff --git a/test/integration/targets/plugin_filtering/filter_lookup.yml b/test/integration/targets/plugin_filtering/filter_lookup.yml
index 694ebfc..5f183e9 100644
--- a/test/integration/targets/plugin_filtering/filter_lookup.yml
+++ b/test/integration/targets/plugin_filtering/filter_lookup.yml
@@ -1,6 +1,6 @@
---
filter_version: 1.0
-module_blacklist:
+module_rejectlist:
# Specify the name of a lookup plugin here. This should have no effect as
# this is only for filtering modules
- list
diff --git a/test/integration/targets/plugin_filtering/filter_modules.yml b/test/integration/targets/plugin_filtering/filter_modules.yml
index 6cffa67..bef7d6d 100644
--- a/test/integration/targets/plugin_filtering/filter_modules.yml
+++ b/test/integration/targets/plugin_filtering/filter_modules.yml
@@ -1,6 +1,6 @@
---
filter_version: 1.0
-module_blacklist:
+module_rejectlist:
# A pure action plugin
- pause
# A hybrid action plugin with module
diff --git a/test/integration/targets/plugin_filtering/filter_ping.yml b/test/integration/targets/plugin_filtering/filter_ping.yml
index 08e56f2..8604716 100644
--- a/test/integration/targets/plugin_filtering/filter_ping.yml
+++ b/test/integration/targets/plugin_filtering/filter_ping.yml
@@ -1,5 +1,5 @@
---
filter_version: 1.0
-module_blacklist:
+module_rejectlist:
# Ping is special
- ping
diff --git a/test/integration/targets/plugin_filtering/filter_stat.yml b/test/integration/targets/plugin_filtering/filter_stat.yml
index c1ce42e..132bf03 100644
--- a/test/integration/targets/plugin_filtering/filter_stat.yml
+++ b/test/integration/targets/plugin_filtering/filter_stat.yml
@@ -1,5 +1,5 @@
---
filter_version: 1.0
-module_blacklist:
+module_rejectlist:
# Stat is special
- stat
diff --git a/test/integration/targets/plugin_filtering/no_blacklist_module.ini b/test/integration/targets/plugin_filtering/no_blacklist_module.ini
deleted file mode 100644
index 65b51d6..0000000
--- a/test/integration/targets/plugin_filtering/no_blacklist_module.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[defaults]
-retry_files_enabled = False
-plugin_filters_cfg = ./no_blacklist_module.yml
diff --git a/test/integration/targets/plugin_filtering/no_blacklist_module.yml b/test/integration/targets/plugin_filtering/no_rejectlist_module.yml
index 52a55df..91e60a1 100644
--- a/test/integration/targets/plugin_filtering/no_blacklist_module.yml
+++ b/test/integration/targets/plugin_filtering/no_rejectlist_module.yml
@@ -1,3 +1,3 @@
---
filter_version: 1.0
-module_blacklist:
+module_rejectlist:
diff --git a/test/integration/targets/plugin_filtering/runme.sh b/test/integration/targets/plugin_filtering/runme.sh
index aa0e2b0..03d78ab 100755
--- a/test/integration/targets/plugin_filtering/runme.sh
+++ b/test/integration/targets/plugin_filtering/runme.sh
@@ -22,11 +22,11 @@ if test $? != 0 ; then
fi
#
-# Check that if no modules are blacklisted then Ansible should not through traceback
+# Check that if no modules are rejected then Ansible should not through traceback
#
-ANSIBLE_CONFIG=no_blacklist_module.ini ansible-playbook tempfile.yml -i ../../inventory -vvv "$@"
+ANSIBLE_CONFIG=no_rejectlist_module.ini ansible-playbook tempfile.yml -i ../../inventory -vvv "$@"
if test $? != 0 ; then
- echo "### Failed to run tempfile with no modules blacklisted"
+ echo "### Failed to run tempfile with no modules rejected"
exit 1
fi
@@ -87,7 +87,7 @@ fi
ANSIBLE_CONFIG=filter_lookup.ini ansible-playbook lookup.yml -i ../../inventory -vvv "$@"
if test $? != 0 ; then
- echo "### Failed to use a lookup plugin when it is incorrectly specified in the *module* blacklist"
+ echo "### Failed to use a lookup plugin when it is incorrectly specified in the *module* reject list"
exit 1
fi
@@ -107,10 +107,10 @@ ANSIBLE_CONFIG=filter_stat.ini
export ANSIBLE_CONFIG
CAPTURE=$(ansible-playbook copy.yml -i ../../inventory -vvv "$@" 2>&1)
if test $? = 0 ; then
- echo "### Copy ran even though stat is in the module blacklist"
+ echo "### Copy ran even though stat is in the module reject list"
exit 1
else
- echo "$CAPTURE" | grep 'The stat module was specified in the module blacklist file,.*, but Ansible will not function without the stat module. Please remove stat from the blacklist.'
+ echo "$CAPTURE" | grep 'The stat module was specified in the module reject list file,.*, but Ansible will not function without the stat module. Please remove stat from the reject list.'
if test $? != 0 ; then
echo "### Stat did not give us our custom error message"
exit 1
@@ -124,10 +124,10 @@ ANSIBLE_CONFIG=filter_stat.ini
export ANSIBLE_CONFIG
CAPTURE=$(ansible-playbook stat.yml -i ../../inventory -vvv "$@" 2>&1)
if test $? = 0 ; then
- echo "### Stat ran even though it is in the module blacklist"
+ echo "### Stat ran even though it is in the module reject list"
exit 1
else
- echo "$CAPTURE" | grep 'The stat module was specified in the module blacklist file,.*, but Ansible will not function without the stat module. Please remove stat from the blacklist.'
+ echo "$CAPTURE" | grep 'The stat module was specified in the module reject list file,.*, but Ansible will not function without the stat module. Please remove stat from the reject list.'
if test $? != 0 ; then
echo "### Stat did not give us our custom error message"
exit 1
diff --git a/test/integration/targets/plugin_loader/collections/ansible_collections/n/c/plugins/action/a.py b/test/integration/targets/plugin_loader/collections/ansible_collections/n/c/plugins/action/a.py
new file mode 100644
index 0000000..685b159
--- /dev/null
+++ b/test/integration/targets/plugin_loader/collections/ansible_collections/n/c/plugins/action/a.py
@@ -0,0 +1,6 @@
+from ansible.plugins.action import ActionBase
+
+
+class ActionModule(ActionBase):
+ def run(self, tmp=None, task_vars=None):
+ return {"nca_executed": True}
diff --git a/test/integration/targets/plugin_loader/file_collision/play.yml b/test/integration/targets/plugin_loader/file_collision/play.yml
new file mode 100644
index 0000000..cc55800
--- /dev/null
+++ b/test/integration/targets/plugin_loader/file_collision/play.yml
@@ -0,0 +1,7 @@
+- hosts: localhost
+ gather_facts: false
+ roles:
+ - r1
+ - r2
+ tasks:
+ - debug: msg={{'a'|filter1|filter2|filter3}}
diff --git a/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py b/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py
new file mode 100644
index 0000000..7adbf7d
--- /dev/null
+++ b/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/custom.py
@@ -0,0 +1,15 @@
+from __future__ import annotations
+
+
+def do_nothing(myval):
+ return myval
+
+
+class FilterModule(object):
+ ''' Ansible core jinja2 filters '''
+
+ def filters(self):
+ return {
+ 'filter1': do_nothing,
+ 'filter3': do_nothing,
+ }
diff --git a/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml b/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml
new file mode 100644
index 0000000..5bb3e34
--- /dev/null
+++ b/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter1.yml
@@ -0,0 +1,18 @@
+DOCUMENTATION:
+ name: filter1
+ version_added: "1.9"
+ short_description: Does nothing
+ description:
+ - Really, does nothing
+ notes:
+ - This is a test filter
+ positional: _input
+ options:
+ _input:
+ description: the input
+ required: true
+
+EXAMPLES: ''
+RETURN:
+ _value:
+ description: The input
diff --git a/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml b/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml
new file mode 100644
index 0000000..4270b32
--- /dev/null
+++ b/test/integration/targets/plugin_loader/file_collision/roles/r1/filter_plugins/filter3.yml
@@ -0,0 +1,18 @@
+DOCUMENTATION:
+ name: filter3
+ version_added: "1.9"
+ short_description: Does nothing
+ description:
+ - Really, does nothing
+ notes:
+ - This is a test filter
+ positional: _input
+ options:
+ _input:
+ description: the input
+ required: true
+
+EXAMPLES: ''
+RETURN:
+ _value:
+ description: The input
diff --git a/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py b/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py
new file mode 100644
index 0000000..8a7a4f5
--- /dev/null
+++ b/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/custom.py
@@ -0,0 +1,14 @@
+from __future__ import annotations
+
+
+def do_nothing(myval):
+ return myval
+
+
+class FilterModule(object):
+ ''' Ansible core jinja2 filters '''
+
+ def filters(self):
+ return {
+ 'filter2': do_nothing,
+ }
diff --git a/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml b/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml
new file mode 100644
index 0000000..de9195e
--- /dev/null
+++ b/test/integration/targets/plugin_loader/file_collision/roles/r2/filter_plugins/filter2.yml
@@ -0,0 +1,18 @@
+DOCUMENTATION:
+ name: filter2
+ version_added: "1.9"
+ short_description: Does nothing
+ description:
+ - Really, does nothing
+ notes:
+ - This is a test filter
+ positional: _input
+ options:
+ _input:
+ description: the input
+ required: true
+
+EXAMPLES: ''
+RETURN:
+ _value:
+ description: The input
diff --git a/test/integration/targets/plugin_loader/override/filters.yml b/test/integration/targets/plugin_loader/override/filters.yml
index e51ab4e..569a447 100644
--- a/test/integration/targets/plugin_loader/override/filters.yml
+++ b/test/integration/targets/plugin_loader/override/filters.yml
@@ -1,7 +1,7 @@
- hosts: testhost
gather_facts: false
tasks:
- - name: ensure local 'flag' filter works, 'flatten' is overriden and 'ternary' is still from core
+ - name: ensure local 'flag' filter works, 'flatten' is overridden and 'ternary' is still from core
assert:
that:
- a|flag == 'flagged'
diff --git a/test/integration/targets/plugin_loader/runme.sh b/test/integration/targets/plugin_loader/runme.sh
index e30f624..e68f06a 100755
--- a/test/integration/targets/plugin_loader/runme.sh
+++ b/test/integration/targets/plugin_loader/runme.sh
@@ -34,3 +34,8 @@ done
# test config loading
ansible-playbook use_coll_name.yml -i ../../inventory -e 'ansible_connection=ansible.builtin.ssh' "$@"
+
+# test filter loading ignoring duplicate file basename
+ansible-playbook file_collision/play.yml "$@"
+
+ANSIBLE_COLLECTIONS_PATH=$PWD/collections ansible-playbook unsafe_plugin_name.yml "$@"
diff --git a/test/integration/targets/plugin_loader/unsafe_plugin_name.yml b/test/integration/targets/plugin_loader/unsafe_plugin_name.yml
new file mode 100644
index 0000000..73cd439
--- /dev/null
+++ b/test/integration/targets/plugin_loader/unsafe_plugin_name.yml
@@ -0,0 +1,9 @@
+- hosts: localhost
+ gather_facts: false
+ tasks:
+ - action: !unsafe n.c.a
+ register: r
+
+ - assert:
+ that:
+ - r.nca_executed
diff --git a/test/integration/targets/rel_plugin_loading/subdir/inventory_plugins/notyaml.py b/test/integration/targets/rel_plugin_loading/subdir/inventory_plugins/notyaml.py
index e542913..41a76d9 100644
--- a/test/integration/targets/rel_plugin_loading/subdir/inventory_plugins/notyaml.py
+++ b/test/integration/targets/rel_plugin_loading/subdir/inventory_plugins/notyaml.py
@@ -64,7 +64,7 @@ from collections.abc import MutableMapping
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.module_utils.six import string_types
-from ansible.module_utils._text import to_native, to_text
+from ansible.module_utils.common.text.converters import to_native, to_text
from ansible.plugins.inventory import BaseFileInventoryPlugin
NoneType = type(None)
diff --git a/test/integration/targets/remote_tmp/playbook.yml b/test/integration/targets/remote_tmp/playbook.yml
index 5adef62..2d0db4e 100644
--- a/test/integration/targets/remote_tmp/playbook.yml
+++ b/test/integration/targets/remote_tmp/playbook.yml
@@ -30,30 +30,43 @@
- name: Test tempdir is removed
hosts: testhost
gather_facts: false
+ vars:
+ # These tests cannot be run with pipelining as it defeats the purpose of
+ # ensuring remote_tmp is cleaned up. Pipelining is enabled in the test
+ # inventory
+ ansible_pipelining: false
+ # Ensure that the remote_tmp_dir we create allows the unpriv connection user
+ # to create the remote_tmp
+ ansible_become: false
tasks:
- import_role:
name: ../setup_remote_tmp_dir
- - file:
- state: touch
- path: "{{ remote_tmp_dir }}/65393"
+ - vars:
+ # Isolate the remote_tmp used by these tests
+ ansible_remote_tmp: "{{ remote_tmp_dir }}/remote_tmp"
+ block:
+ - file:
+ state: touch
+ path: "{{ remote_tmp_dir }}/65393"
- - copy:
- src: "{{ remote_tmp_dir }}/65393"
- dest: "{{ remote_tmp_dir }}/65393.2"
- remote_src: true
+ - copy:
+ src: "{{ remote_tmp_dir }}/65393"
+ dest: "{{ remote_tmp_dir }}/65393.2"
+ remote_src: true
- - find:
- path: "~/.ansible/tmp"
- use_regex: yes
- patterns: 'AnsiballZ_.+\.py'
- recurse: true
- register: result
+ - find:
+ path: "{{ ansible_remote_tmp }}"
+ use_regex: yes
+ patterns: 'AnsiballZ_.+\.py'
+ recurse: true
+ register: result
- debug:
var: result
- assert:
that:
- # Should find nothing since pipelining is used
- - result.files|length == 0
+ # Should only be AnsiballZ_find.py because find is actively running
+ - result.files|length == 1
+ - result.files[0].path.endswith('/AnsiballZ_find.py')
diff --git a/test/integration/targets/replace/tasks/main.yml b/test/integration/targets/replace/tasks/main.yml
index d267b78..ca8b4ec 100644
--- a/test/integration/targets/replace/tasks/main.yml
+++ b/test/integration/targets/replace/tasks/main.yml
@@ -263,3 +263,22 @@
- replace_cat8.stdout_lines[1] == "9.9.9.9"
- replace_cat8.stdout_lines[7] == "0.0.0.0"
- replace_cat8.stdout_lines[13] == "0.0.0.0"
+
+# For Python 3.6 or greater - https://github.com/ansible/ansible/issues/79364
+- name: Handle bad escape character in regular expression
+ replace:
+ path: /dev/null
+ after: ^
+ before: $
+ regexp: \.
+ replace: '\D'
+ ignore_errors: true
+ register: replace_test9
+ when: ansible_python.version.major == 3 and ansible_python.version.minor > 6
+
+- name: Validate the failure
+ assert:
+ that:
+ - replace_test9 is failure
+ - replace_test9.msg.startswith("Unable to process replace")
+ when: ansible_python.version.major == 3 and ansible_python.version.minor > 6
diff --git a/test/integration/targets/result_pickle_error/action_plugins/result_pickle_error.py b/test/integration/targets/result_pickle_error/action_plugins/result_pickle_error.py
new file mode 100644
index 0000000..e8d712a
--- /dev/null
+++ b/test/integration/targets/result_pickle_error/action_plugins/result_pickle_error.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Copyright: Contributors to the Ansible project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.plugins.action import ActionBase
+from jinja2 import Undefined
+
+
+class ActionModule(ActionBase):
+
+ def run(self, tmp=None, task_vars=None):
+ return {'obj': Undefined('obj')}
diff --git a/test/integration/targets/result_pickle_error/aliases b/test/integration/targets/result_pickle_error/aliases
new file mode 100644
index 0000000..70fbe57
--- /dev/null
+++ b/test/integration/targets/result_pickle_error/aliases
@@ -0,0 +1,3 @@
+shippable/posix/group5
+context/controller
+needs/target/test_utils
diff --git a/test/integration/targets/result_pickle_error/runme.sh b/test/integration/targets/result_pickle_error/runme.sh
new file mode 100755
index 0000000..e2ec37b
--- /dev/null
+++ b/test/integration/targets/result_pickle_error/runme.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+set -ux
+export ANSIBLE_ROLES_PATH=../
+
+is_timeout() {
+ rv=$?
+ if [ "$rv" == "124" ]; then
+ echo "***hang detected, this likely means the strategy never received a result for the task***"
+ fi
+ exit $rv
+}
+
+trap "is_timeout" EXIT
+
+../test_utils/scripts/timeout.py -- 10 ansible-playbook -i ../../inventory runme.yml -v "$@"
diff --git a/test/integration/targets/result_pickle_error/runme.yml b/test/integration/targets/result_pickle_error/runme.yml
new file mode 100644
index 0000000..6050849
--- /dev/null
+++ b/test/integration/targets/result_pickle_error/runme.yml
@@ -0,0 +1,7 @@
+- hosts: all
+ gather_facts: false
+ tasks:
+ - include_role:
+ name: result_pickle_error
+ # Just for caution loop 3 times to ensure no issues
+ loop: '{{ range(3) }}'
diff --git a/test/integration/targets/result_pickle_error/tasks/main.yml b/test/integration/targets/result_pickle_error/tasks/main.yml
new file mode 100644
index 0000000..895475d
--- /dev/null
+++ b/test/integration/targets/result_pickle_error/tasks/main.yml
@@ -0,0 +1,14 @@
+- name: Ensure pickling error doesn't cause a hang
+ result_pickle_error:
+ ignore_errors: true
+ register: result
+
+- assert:
+ that:
+ - result.msg == expected_msg
+ - result is failed
+ vars:
+ expected_msg: "cannot pickle 'Undefined' object"
+
+- debug:
+ msg: Success, no hang
diff --git a/test/integration/targets/roles/47023.yml b/test/integration/targets/roles/47023.yml
new file mode 100644
index 0000000..6b41b52
--- /dev/null
+++ b/test/integration/targets/roles/47023.yml
@@ -0,0 +1,5 @@
+---
+- hosts: all
+ gather_facts: no
+ tasks:
+ - include_role: name=47023_role1
diff --git a/test/integration/targets/roles/dupe_inheritance.yml b/test/integration/targets/roles/dupe_inheritance.yml
new file mode 100644
index 0000000..6fda5ba
--- /dev/null
+++ b/test/integration/targets/roles/dupe_inheritance.yml
@@ -0,0 +1,10 @@
+- name: Test
+ hosts: testhost
+ gather_facts: false
+ roles:
+ - role: top
+ info: First definition
+ testvar: abc
+
+ - role: top
+ info: Second definition
diff --git a/test/integration/targets/roles/privacy.yml b/test/integration/targets/roles/privacy.yml
new file mode 100644
index 0000000..2f671c0
--- /dev/null
+++ b/test/integration/targets/roles/privacy.yml
@@ -0,0 +1,60 @@
+# use this to debug issues
+#- debug: msg={{ is_private ~ ', ' ~ is_default ~ ', ' ~ privacy|default('nope')}}
+
+- hosts: localhost
+ name: test global privacy setting
+ gather_facts: false
+ roles:
+ - a
+ pre_tasks:
+
+ - name: 'test roles: privacy'
+ assert:
+ that:
+ - is_private and privacy is undefined or not is_private and privacy is defined
+ - not is_default or is_default and privacy is defined
+
+- hosts: localhost
+ name: test import_role privacy
+ gather_facts: false
+ tasks:
+ - import_role: name=a
+
+ - name: role is private, var should be undefined
+ assert:
+ that:
+ - is_private and privacy is undefined or not is_private and privacy is defined
+ - not is_default or is_default and privacy is defined
+
+- hosts: localhost
+ name: test global privacy setting on includes
+ gather_facts: false
+ tasks:
+ - include_role: name=a
+
+ - name: test include_role privacy
+ assert:
+ that:
+ - not is_default and (is_private and privacy is undefined or not is_private and privacy is defined) or is_default and privacy is undefined
+
+- hosts: localhost
+ name: test public yes always overrides global privacy setting on includes
+ gather_facts: false
+ tasks:
+ - include_role: name=a public=yes
+
+ - name: test include_role privacy
+ assert:
+ that:
+ - privacy is defined
+
+- hosts: localhost
+ name: test public no always overrides global privacy setting on includes
+ gather_facts: false
+ tasks:
+ - include_role: name=a public=no
+
+ - name: test include_role privacy
+ assert:
+ that:
+ - privacy is undefined
diff --git a/test/integration/targets/roles/role_complete.yml b/test/integration/targets/roles/role_complete.yml
new file mode 100644
index 0000000..86cae77
--- /dev/null
+++ b/test/integration/targets/roles/role_complete.yml
@@ -0,0 +1,47 @@
+- name: test deduping allows for 1 successful execution of role after it is skipped
+ hosts: testhost
+ gather_facts: false
+ tags: [ 'conditional_skipped' ]
+ roles:
+ # Skipped the first time it executes
+ - role: a
+ when: role_set_var is defined
+
+ - role: set_var
+
+ # No longer skipped
+ - role: a
+ when: role_set_var is defined
+ # Deduplicated with the previous success
+ - role: a
+ when: role_set_var is defined
+
+- name: test deduping allows for successful execution of role after host is unreachable
+ hosts: fake,testhost
+ gather_facts: false
+ tags: [ 'unreachable' ]
+ ignore_unreachable: yes
+ roles:
+ # unreachable by the first host
+ - role: test_connectivity
+
+ # unreachable host will try again,
+ # the successful host will not because it's deduplicated
+ - role: test_connectivity
+
+- name: test deduping role for failed host
+ hosts: testhost,localhost
+ gather_facts: false
+ tags: [ 'conditional_failed' ]
+ ignore_errors: yes
+ roles:
+ # Uses run_once to fail on the first host the first time it executes
+ - role: failed_when
+
+ - role: set_var
+ - role: recover
+
+ # Deduplicated after the failure, ONLY runs for localhost
+ - role: failed_when
+ # Deduplicated with the previous success
+ - role: failed_when
diff --git a/test/integration/targets/roles/role_dep_chain.yml b/test/integration/targets/roles/role_dep_chain.yml
new file mode 100644
index 0000000..cf99a25
--- /dev/null
+++ b/test/integration/targets/roles/role_dep_chain.yml
@@ -0,0 +1,6 @@
+---
+- hosts: all
+ tasks:
+ - name: static import inside dynamic include inherits defaults/vars
+ include_role:
+ name: include_import_dep_chain
diff --git a/test/integration/targets/roles/roles/47023_role1/defaults/main.yml b/test/integration/targets/roles/roles/47023_role1/defaults/main.yml
new file mode 100644
index 0000000..166caa3
--- /dev/null
+++ b/test/integration/targets/roles/roles/47023_role1/defaults/main.yml
@@ -0,0 +1 @@
+my_default: defined
diff --git a/test/integration/targets/roles/roles/47023_role1/tasks/main.yml b/test/integration/targets/roles/roles/47023_role1/tasks/main.yml
new file mode 100644
index 0000000..9c408ba
--- /dev/null
+++ b/test/integration/targets/roles/roles/47023_role1/tasks/main.yml
@@ -0,0 +1 @@
+- include_role: name=47023_role2
diff --git a/test/integration/targets/roles/roles/47023_role1/vars/main.yml b/test/integration/targets/roles/roles/47023_role1/vars/main.yml
new file mode 100644
index 0000000..bfda56b
--- /dev/null
+++ b/test/integration/targets/roles/roles/47023_role1/vars/main.yml
@@ -0,0 +1 @@
+my_var: defined
diff --git a/test/integration/targets/roles/roles/47023_role2/tasks/main.yml b/test/integration/targets/roles/roles/47023_role2/tasks/main.yml
new file mode 100644
index 0000000..4544215
--- /dev/null
+++ b/test/integration/targets/roles/roles/47023_role2/tasks/main.yml
@@ -0,0 +1 @@
+- include_role: name=47023_role3
diff --git a/test/integration/targets/roles/roles/47023_role3/tasks/main.yml b/test/integration/targets/roles/roles/47023_role3/tasks/main.yml
new file mode 100644
index 0000000..9479fe3
--- /dev/null
+++ b/test/integration/targets/roles/roles/47023_role3/tasks/main.yml
@@ -0,0 +1 @@
+- include_role: name=47023_role4
diff --git a/test/integration/targets/roles/roles/47023_role4/tasks/main.yml b/test/integration/targets/roles/roles/47023_role4/tasks/main.yml
new file mode 100644
index 0000000..64c96e9
--- /dev/null
+++ b/test/integration/targets/roles/roles/47023_role4/tasks/main.yml
@@ -0,0 +1,5 @@
+- debug:
+ msg: "Var is {{ my_var | default('undefined') }}"
+
+- debug:
+ msg: "Default is {{ my_default | default('undefined') }}"
diff --git a/test/integration/targets/roles/roles/a/vars/main.yml b/test/integration/targets/roles/roles/a/vars/main.yml
new file mode 100644
index 0000000..7812aa7
--- /dev/null
+++ b/test/integration/targets/roles/roles/a/vars/main.yml
@@ -0,0 +1 @@
+privacy: in role a
diff --git a/test/integration/targets/roles/roles/bottom/tasks/main.yml b/test/integration/targets/roles/roles/bottom/tasks/main.yml
new file mode 100644
index 0000000..3f37597
--- /dev/null
+++ b/test/integration/targets/roles/roles/bottom/tasks/main.yml
@@ -0,0 +1,3 @@
+- name: "{{ info }} - {{ role_name }}: testvar content"
+ debug:
+ msg: '{{ testvar | default("Not specified") }}'
diff --git a/test/integration/targets/roles/roles/failed_when/tasks/main.yml b/test/integration/targets/roles/roles/failed_when/tasks/main.yml
new file mode 100644
index 0000000..6ca4d8c
--- /dev/null
+++ b/test/integration/targets/roles/roles/failed_when/tasks/main.yml
@@ -0,0 +1,4 @@
+- debug:
+ msg: "{{ role_set_var is undefined | ternary('failed_when task failed', 'failed_when task succeeded') }}"
+ failed_when: role_set_var is undefined
+ run_once: true
diff --git a/test/integration/targets/roles/roles/imported_from_include/tasks/main.yml b/test/integration/targets/roles/roles/imported_from_include/tasks/main.yml
new file mode 100644
index 0000000..32126f8
--- /dev/null
+++ b/test/integration/targets/roles/roles/imported_from_include/tasks/main.yml
@@ -0,0 +1,4 @@
+- assert:
+ that:
+ - inherit_var is defined
+ - inherit_default is defined
diff --git a/test/integration/targets/roles/roles/include_import_dep_chain/defaults/main.yml b/test/integration/targets/roles/roles/include_import_dep_chain/defaults/main.yml
new file mode 100644
index 0000000..5b8a643
--- /dev/null
+++ b/test/integration/targets/roles/roles/include_import_dep_chain/defaults/main.yml
@@ -0,0 +1 @@
+inherit_default: default
diff --git a/test/integration/targets/roles/roles/include_import_dep_chain/tasks/main.yml b/test/integration/targets/roles/roles/include_import_dep_chain/tasks/main.yml
new file mode 100644
index 0000000..84884a8
--- /dev/null
+++ b/test/integration/targets/roles/roles/include_import_dep_chain/tasks/main.yml
@@ -0,0 +1,2 @@
+- import_role:
+ name: imported_from_include
diff --git a/test/integration/targets/roles/roles/include_import_dep_chain/vars/main.yml b/test/integration/targets/roles/roles/include_import_dep_chain/vars/main.yml
new file mode 100644
index 0000000..0d4aaa9
--- /dev/null
+++ b/test/integration/targets/roles/roles/include_import_dep_chain/vars/main.yml
@@ -0,0 +1 @@
+inherit_var: var
diff --git a/test/integration/targets/roles/roles/middle/tasks/main.yml b/test/integration/targets/roles/roles/middle/tasks/main.yml
new file mode 100644
index 0000000..bd2b529
--- /dev/null
+++ b/test/integration/targets/roles/roles/middle/tasks/main.yml
@@ -0,0 +1,6 @@
+- name: "{{ info }} - {{ role_name }}: testvar content"
+ debug:
+ msg: '{{ testvar | default("Not specified") }}'
+
+- include_role:
+ name: bottom
diff --git a/test/integration/targets/roles/roles/recover/tasks/main.yml b/test/integration/targets/roles/roles/recover/tasks/main.yml
new file mode 100644
index 0000000..72ea3ac
--- /dev/null
+++ b/test/integration/targets/roles/roles/recover/tasks/main.yml
@@ -0,0 +1 @@
+- meta: clear_host_errors
diff --git a/test/integration/targets/roles/roles/set_var/tasks/main.yml b/test/integration/targets/roles/roles/set_var/tasks/main.yml
new file mode 100644
index 0000000..45f83eb
--- /dev/null
+++ b/test/integration/targets/roles/roles/set_var/tasks/main.yml
@@ -0,0 +1,2 @@
+- set_fact:
+ role_set_var: true
diff --git a/test/integration/targets/roles/roles/test_connectivity/tasks/main.yml b/test/integration/targets/roles/roles/test_connectivity/tasks/main.yml
new file mode 100644
index 0000000..22fac6e
--- /dev/null
+++ b/test/integration/targets/roles/roles/test_connectivity/tasks/main.yml
@@ -0,0 +1,2 @@
+- ping:
+ data: 'reachable'
diff --git a/test/integration/targets/roles/roles/top/tasks/main.yml b/test/integration/targets/roles/roles/top/tasks/main.yml
new file mode 100644
index 0000000..a7a5b52
--- /dev/null
+++ b/test/integration/targets/roles/roles/top/tasks/main.yml
@@ -0,0 +1,6 @@
+- name: "{{ info }} - {{ role_name }}: testvar content"
+ debug:
+ msg: '{{ testvar | default("Not specified") }}'
+
+- include_role:
+ name: middle
diff --git a/test/integration/targets/roles/roles/vars_scope/defaults/main.yml b/test/integration/targets/roles/roles/vars_scope/defaults/main.yml
new file mode 100644
index 0000000..27f3e91
--- /dev/null
+++ b/test/integration/targets/roles/roles/vars_scope/defaults/main.yml
@@ -0,0 +1,10 @@
+default_only: default
+role_vars_only: default
+play_and_role_vars: default
+play_and_roles_and_role_vars: default
+play_and_import_and_role_vars: default
+play_and_include_and_role_vars: default
+play_and_role_vars_and_role_vars: default
+roles_and_role_vars: default
+import_and_role_vars: default
+include_and_role_vars: default
diff --git a/test/integration/targets/roles/roles/vars_scope/tasks/check_vars.yml b/test/integration/targets/roles/roles/vars_scope/tasks/check_vars.yml
new file mode 100644
index 0000000..083415d
--- /dev/null
+++ b/test/integration/targets/roles/roles/vars_scope/tasks/check_vars.yml
@@ -0,0 +1,7 @@
+- debug: var={{item}}
+ loop: '{{possible_vars}}'
+
+- assert:
+ that:
+ - (item in vars and item in defined and vars[item] == defined[item]) or (item not in vars and item not in defined)
+ loop: '{{possible_vars}}'
diff --git a/test/integration/targets/roles/roles/vars_scope/tasks/main.yml b/test/integration/targets/roles/roles/vars_scope/tasks/main.yml
new file mode 100644
index 0000000..155f362
--- /dev/null
+++ b/test/integration/targets/roles/roles/vars_scope/tasks/main.yml
@@ -0,0 +1 @@
+- include_tasks: check_vars.yml
diff --git a/test/integration/targets/roles/roles/vars_scope/vars/main.yml b/test/integration/targets/roles/roles/vars_scope/vars/main.yml
new file mode 100644
index 0000000..079353f
--- /dev/null
+++ b/test/integration/targets/roles/roles/vars_scope/vars/main.yml
@@ -0,0 +1,9 @@
+role_vars_only: role_vars
+play_and_role_vars: role_vars
+play_and_roles_and_role_vars: role_vars
+play_and_import_and_role_vars: role_vars
+play_and_include_and_role_vars: role_vars
+play_and_role_vars_and_role_vars: role_vars
+roles_and_role_vars: role_vars
+import_and_role_vars: role_vars
+include_and_role_vars: role_vars
diff --git a/test/integration/targets/roles/runme.sh b/test/integration/targets/roles/runme.sh
index bb98a93..bf3aaf5 100755
--- a/test/integration/targets/roles/runme.sh
+++ b/test/integration/targets/roles/runme.sh
@@ -3,26 +3,47 @@
set -eux
# test no dupes when dependencies in b and c point to a in roles:
-[ "$(ansible-playbook no_dupes.yml -i ../../inventory --tags inroles "$@" | grep -c '"msg": "A"')" = "1" ]
-[ "$(ansible-playbook no_dupes.yml -i ../../inventory --tags acrossroles "$@" | grep -c '"msg": "A"')" = "1" ]
-[ "$(ansible-playbook no_dupes.yml -i ../../inventory --tags intasks "$@" | grep -c '"msg": "A"')" = "1" ]
+[ "$(ansible-playbook no_dupes.yml -i ../../inventory --tags inroles | grep -c '"msg": "A"')" = "1" ]
+[ "$(ansible-playbook no_dupes.yml -i ../../inventory --tags acrossroles | grep -c '"msg": "A"')" = "1" ]
+[ "$(ansible-playbook no_dupes.yml -i ../../inventory --tags intasks | grep -c '"msg": "A"')" = "1" ]
# but still dupe across plays
-[ "$(ansible-playbook no_dupes.yml -i ../../inventory "$@" | grep -c '"msg": "A"')" = "3" ]
+[ "$(ansible-playbook no_dupes.yml -i ../../inventory | grep -c '"msg": "A"')" = "3" ]
+
+# and don't dedupe before the role successfully completes
+[ "$(ansible-playbook role_complete.yml -i ../../inventory -i fake, --tags conditional_skipped | grep -c '"msg": "A"')" = "1" ]
+[ "$(ansible-playbook role_complete.yml -i ../../inventory -i fake, --tags conditional_failed | grep -c '"msg": "failed_when task succeeded"')" = "1" ]
+[ "$(ansible-playbook role_complete.yml -i ../../inventory -i fake, --tags unreachable -vvv | grep -c '"data": "reachable"')" = "1" ]
+ansible-playbook role_complete.yml -i ../../inventory -i fake, --tags unreachable | grep -e 'ignored=2'
# include/import can execute another instance of role
-[ "$(ansible-playbook allowed_dupes.yml -i ../../inventory --tags importrole "$@" | grep -c '"msg": "A"')" = "2" ]
-[ "$(ansible-playbook allowed_dupes.yml -i ../../inventory --tags includerole "$@" | grep -c '"msg": "A"')" = "2" ]
+[ "$(ansible-playbook allowed_dupes.yml -i ../../inventory --tags importrole | grep -c '"msg": "A"')" = "2" ]
+[ "$(ansible-playbook allowed_dupes.yml -i ../../inventory --tags includerole | grep -c '"msg": "A"')" = "2" ]
+[ "$(ansible-playbook dupe_inheritance.yml -i ../../inventory | grep -c '"msg": "abc"')" = "3" ]
# ensure role data is merged correctly
ansible-playbook data_integrity.yml -i ../../inventory "$@"
# ensure role fails when trying to load 'non role' in _from
-ansible-playbook no_outside.yml -i ../../inventory "$@" > role_outside_output.log 2>&1 || true
+ansible-playbook no_outside.yml -i ../../inventory > role_outside_output.log 2>&1 || true
if grep "as it is not inside the expected role path" role_outside_output.log >/dev/null; then
echo "Test passed (playbook failed with expected output, output not shown)."
else
echo "Test failed, expected output from playbook failure is missing, output not shown)."
exit 1
fi
+
+# ensure vars scope is correct
+ansible-playbook vars_scope.yml -i ../../inventory "$@"
+
+# test nested includes get parent roles greater than a depth of 3
+[ "$(ansible-playbook 47023.yml -i ../../inventory | grep '\<\(Default\|Var\)\>' | grep -c 'is defined')" = "2" ]
+
+# ensure import_role called from include_role has the include_role in the dep chain
+ansible-playbook role_dep_chain.yml -i ../../inventory "$@"
+
+# global role privacy setting test, set to private, set to not private, default
+ANSIBLE_PRIVATE_ROLE_VARS=1 ansible-playbook privacy.yml -e @vars/privacy_vars.yml "$@"
+ANSIBLE_PRIVATE_ROLE_VARS=0 ansible-playbook privacy.yml -e @vars/privacy_vars.yml "$@"
+ansible-playbook privacy.yml -e @vars/privacy_vars.yml "$@"
diff --git a/test/integration/targets/roles/tasks/check_vars.yml b/test/integration/targets/roles/tasks/check_vars.yml
new file mode 100644
index 0000000..083415d
--- /dev/null
+++ b/test/integration/targets/roles/tasks/check_vars.yml
@@ -0,0 +1,7 @@
+- debug: var={{item}}
+ loop: '{{possible_vars}}'
+
+- assert:
+ that:
+ - (item in vars and item in defined and vars[item] == defined[item]) or (item not in vars and item not in defined)
+ loop: '{{possible_vars}}'
diff --git a/test/integration/targets/roles/vars/play.yml b/test/integration/targets/roles/vars/play.yml
new file mode 100644
index 0000000..dd84ae2
--- /dev/null
+++ b/test/integration/targets/roles/vars/play.yml
@@ -0,0 +1,26 @@
+play_only: play
+play_and_roles: play
+play_and_import: play
+play_and_include: play
+play_and_role_vars: play
+play_and_roles_and_role_vars: play
+play_and_import_and_role_vars: play
+play_and_include_and_role_vars: play
+possible_vars:
+ - default_only
+ - import_and_role_vars
+ - import_only
+ - include_and_role_vars
+ - include_only
+ - play_and_import
+ - play_and_import_and_role_vars
+ - play_and_include
+ - play_and_include_and_role_vars
+ - play_and_roles
+ - play_and_roles_and_role_vars
+ - play_and_role_vars
+ - play_and_role_vars_and_role_vars
+ - play_only
+ - roles_and_role_vars
+ - roles_only
+ - role_vars_only
diff --git a/test/integration/targets/roles/vars/privacy_vars.yml b/test/integration/targets/roles/vars/privacy_vars.yml
new file mode 100644
index 0000000..9539ed0
--- /dev/null
+++ b/test/integration/targets/roles/vars/privacy_vars.yml
@@ -0,0 +1,2 @@
+is_private: "{{lookup('config', 'DEFAULT_PRIVATE_ROLE_VARS')}}"
+is_default: "{{lookup('env', 'ANSIBLE_PRIVATE_ROLE_VARS') == ''}}"
diff --git a/test/integration/targets/roles/vars_scope.yml b/test/integration/targets/roles/vars_scope.yml
new file mode 100644
index 0000000..3e6b16a
--- /dev/null
+++ b/test/integration/targets/roles/vars_scope.yml
@@ -0,0 +1,358 @@
+- name: play and roles
+ hosts: localhost
+ gather_facts: false
+ vars_files:
+ - vars/play.yml
+ roles:
+ - name: vars_scope
+ vars:
+ roles_only: roles
+ roles_and_role_vars: roles
+ play_and_roles: roles
+ play_and_roles_and_role_vars: roles
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: roles
+ play_and_roles_and_role_vars: roles
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: roles
+ roles_only: roles
+ role_vars_only: role_vars
+
+ pre_tasks:
+ - include_tasks: tasks/check_vars.yml
+ vars:
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+ tasks:
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+- name: play baseline (no roles)
+ hosts: localhost
+ gather_facts: false
+ vars_files:
+ - vars/play.yml
+ tasks:
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ play_and_import: play
+ play_and_import_and_role_vars: play
+ play_and_include: play
+ play_and_include_and_role_vars: play
+ play_and_roles: play
+ play_and_roles_and_role_vars: play
+ play_and_role_vars: play
+ play_only: play
+
+- name: play and import
+ hosts: localhost
+ gather_facts: false
+ vars_files:
+ - vars/play.yml
+ tasks:
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ play_and_import: play
+ play_and_include: play
+ play_and_roles: play
+ play_only: play
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_and_include_and_role_vars: role_vars
+ play_and_roles_and_role_vars: role_vars
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ - name: static import
+ import_role:
+ name: vars_scope
+ vars:
+ import_only: import
+ import_and_role_vars: import
+ play_and_import: import
+ play_and_import_and_role_vars: import
+ defined:
+ default_only: default
+ import_and_role_vars: import
+ import_only: import
+ include_and_role_vars: role_vars
+ play_and_import: import
+ play_and_import_and_role_vars: import
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+- name: play and include
+ hosts: localhost
+ gather_facts: false
+ vars_files:
+ - vars/play.yml
+ tasks:
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ play_and_import: play
+ play_and_import_and_role_vars: play
+ play_and_include: play
+ play_and_include_and_role_vars: play
+ play_and_roles: play
+ play_and_roles_and_role_vars: play
+ play_and_role_vars: play
+ play_only: play
+
+ - name: dynamic include
+ include_role:
+ name: vars_scope
+ vars:
+ include_only: include
+ include_and_role_vars: include
+ play_and_include: include
+ play_and_include_and_role_vars: include
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: include
+ include_only: include
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: include
+ play_and_include_and_role_vars: include
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ play_and_import: play
+ play_and_import_and_role_vars: play
+ play_and_include: play
+ play_and_include_and_role_vars: play
+ play_and_roles: play
+ play_and_roles_and_role_vars: play
+ play_and_role_vars: play
+ play_only: play
+
+- name: play and roles and import and include
+ hosts: localhost
+ gather_facts: false
+ vars:
+ vars_files:
+ - vars/play.yml
+ roles:
+ - name: vars_scope
+ vars:
+ roles_only: roles
+ roles_and_role_vars: roles
+ play_and_roles: roles
+ play_and_roles_and_role_vars: roles
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: roles
+ play_and_roles_and_role_vars: roles
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: roles
+ roles_only: roles
+ role_vars_only: role_vars
+
+ pre_tasks:
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ tasks:
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ - name: static import
+ import_role:
+ name: vars_scope
+ vars:
+ import_only: import
+ import_and_role_vars: import
+ play_and_import: import
+ play_and_import_and_role_vars: import
+ defined:
+ default_only: default
+ import_and_role_vars: import
+ import_only: import
+ include_and_role_vars: role_vars
+ play_and_import: import
+ play_and_import_and_role_vars: import
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ - name: dynamic include
+ include_role:
+ name: vars_scope
+ vars:
+ include_only: include
+ include_and_role_vars: include
+ play_and_include: include
+ play_and_include_and_role_vars: include
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: include
+ include_only: include
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: include
+ play_and_include_and_role_vars: include
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
+
+ - include_tasks: roles/vars_scope/tasks/check_vars.yml
+ vars:
+ defined:
+ default_only: default
+ import_and_role_vars: role_vars
+ include_and_role_vars: role_vars
+ play_and_import: play
+ play_and_import_and_role_vars: role_vars
+ play_and_include: play
+ play_and_include_and_role_vars: role_vars
+ play_and_roles: play
+ play_and_roles_and_role_vars: role_vars
+ play_and_role_vars: role_vars
+ play_and_role_vars_and_role_vars: role_vars
+ play_only: play
+ roles_and_role_vars: role_vars
+ role_vars_only: role_vars
diff --git a/test/integration/targets/roles_arg_spec/roles/c/meta/main.yml b/test/integration/targets/roles_arg_spec/roles/c/meta/main.yml
index 1a1ccbe..10dce6d 100644
--- a/test/integration/targets/roles_arg_spec/roles/c/meta/main.yml
+++ b/test/integration/targets/roles_arg_spec/roles/c/meta/main.yml
@@ -2,6 +2,15 @@ argument_specs:
main:
short_description: Main entry point for role C.
options:
+ c_dict:
+ type: "dict"
+ required: true
c_int:
type: "int"
required: true
+ c_list:
+ type: "list"
+ required: true
+ c_raw:
+ type: "raw"
+ required: true
diff --git a/test/integration/targets/roles_arg_spec/test.yml b/test/integration/targets/roles_arg_spec/test.yml
index 5eca7c7..b88d2e1 100644
--- a/test/integration/targets/roles_arg_spec/test.yml
+++ b/test/integration/targets/roles_arg_spec/test.yml
@@ -48,6 +48,7 @@
name: a
vars:
a_int: "{{ INT_VALUE }}"
+ a_str: "import_role"
- name: "Call role entry point that is defined, but has no spec data"
import_role:
@@ -144,7 +145,10 @@
hosts: localhost
gather_facts: false
vars:
+ c_dict: {}
c_int: 1
+ c_list: []
+ c_raw: ~
a_str: "some string"
a_int: 42
tasks:
@@ -156,6 +160,125 @@
include_role:
name: c
+- name: "New play to reset vars: Test nested role including/importing role fails with null required options"
+ hosts: localhost
+ gather_facts: false
+ vars:
+ a_main_spec:
+ a_str:
+ required: true
+ type: "str"
+ c_main_spec:
+ c_int:
+ required: true
+ type: "int"
+ c_list:
+ required: true
+ type: "list"
+ c_dict:
+ required: true
+ type: "dict"
+ c_raw:
+ required: true
+ type: "raw"
+ # role c calls a's main and alternate entrypoints
+ a_str: ''
+ c_dict: {}
+ c_int: 0
+ c_list: []
+ c_raw: ~
+ tasks:
+ - name: test type coercion fails on None for required str
+ block:
+ - name: "Test import_role of role C (missing a_str)"
+ import_role:
+ name: c
+ vars:
+ a_str: ~
+ - fail:
+ msg: "Should not get here"
+ rescue:
+ - debug:
+ var: ansible_failed_result
+ - name: "Validate import_role failure"
+ assert:
+ that:
+ # NOTE: a bug here that prevents us from getting ansible_failed_task
+ - ansible_failed_result.argument_errors == [error]
+ - ansible_failed_result.argument_spec_data == a_main_spec
+ vars:
+ error: >-
+ argument 'a_str' is of type <class 'NoneType'> and we were unable to convert to str:
+ 'None' is not a string and conversion is not allowed
+
+ - name: test type coercion fails on None for required int
+ block:
+ - name: "Test import_role of role C (missing c_int)"
+ import_role:
+ name: c
+ vars:
+ c_int: ~
+ - fail:
+ msg: "Should not get here"
+ rescue:
+ - debug:
+ var: ansible_failed_result
+ - name: "Validate import_role failure"
+ assert:
+ that:
+ # NOTE: a bug here that prevents us from getting ansible_failed_task
+ - ansible_failed_result.argument_errors == [error]
+ - ansible_failed_result.argument_spec_data == c_main_spec
+ vars:
+ error: >-
+ argument 'c_int' is of type <class 'NoneType'> and we were unable to convert to int:
+ <class 'NoneType'> cannot be converted to an int
+
+ - name: test type coercion fails on None for required list
+ block:
+ - name: "Test import_role of role C (missing c_list)"
+ import_role:
+ name: c
+ vars:
+ c_list: ~
+ - fail:
+ msg: "Should not get here"
+ rescue:
+ - debug:
+ var: ansible_failed_result
+ - name: "Validate import_role failure"
+ assert:
+ that:
+ # NOTE: a bug here that prevents us from getting ansible_failed_task
+ - ansible_failed_result.argument_errors == [error]
+ - ansible_failed_result.argument_spec_data == c_main_spec
+ vars:
+ error: >-
+ argument 'c_list' is of type <class 'NoneType'> and we were unable to convert to list:
+ <class 'NoneType'> cannot be converted to a list
+
+ - name: test type coercion fails on None for required dict
+ block:
+ - name: "Test import_role of role C (missing c_dict)"
+ import_role:
+ name: c
+ vars:
+ c_dict: ~
+ - fail:
+ msg: "Should not get here"
+ rescue:
+ - debug:
+ var: ansible_failed_result
+ - name: "Validate import_role failure"
+ assert:
+ that:
+ # NOTE: a bug here that prevents us from getting ansible_failed_task
+ - ansible_failed_result.argument_errors == [error]
+ - ansible_failed_result.argument_spec_data == c_main_spec
+ vars:
+ error: >-
+ argument 'c_dict' is of type <class 'NoneType'> and we were unable to convert to dict:
+ <class 'NoneType'> cannot be converted to a dict
- name: "New play to reset vars: Test nested role including/importing role fails"
hosts: localhost
@@ -170,13 +293,15 @@
required: true
type: "int"
+ c_int: 100
+ c_list: []
+ c_dict: {}
+ c_raw: ~
tasks:
- block:
- name: "Test import_role of role C (missing a_str)"
import_role:
name: c
- vars:
- c_int: 100
- fail:
msg: "Should not get here"
@@ -201,7 +326,6 @@
include_role:
name: c
vars:
- c_int: 200
a_str: "some string"
- fail:
diff --git a/test/integration/targets/rpm_key/tasks/rpm_key.yaml b/test/integration/targets/rpm_key/tasks/rpm_key.yaml
index 89ed236..204b42a 100644
--- a/test/integration/targets/rpm_key/tasks/rpm_key.yaml
+++ b/test/integration/targets/rpm_key/tasks/rpm_key.yaml
@@ -123,6 +123,32 @@
assert:
that: "'rsa sha1 (md5) pgp md5 OK' in sl_check.stdout or 'digests signatures OK' in sl_check.stdout"
+- name: get keyid
+ shell: "rpm -q gpg-pubkey | head -n 1 | xargs rpm -q --qf %{version}"
+ register: key_id
+
+- name: remove GPG key using keyid
+ rpm_key:
+ state: absent
+ key: "{{ key_id.stdout }}"
+ register: remove_keyid
+ failed_when: remove_keyid.changed == false
+
+- name: remove GPG key using keyid (idempotent)
+ rpm_key:
+ state: absent
+ key: "{{ key_id.stdout }}"
+ register: key_id_idempotence
+
+- name: verify idempotent (key_id)
+ assert:
+ that: "not key_id_idempotence.changed"
+
+- name: add very first key on system again
+ rpm_key:
+ state: present
+ key: https://ci-files.testing.ansible.com/test/integration/targets/rpm_key/RPM-GPG-KEY-EPEL-7
+
- name: Issue 20325 - Verify fingerprint of key, invalid fingerprint - EXPECTED FAILURE
rpm_key:
key: https://ci-files.testing.ansible.com/test/integration/targets/rpm_key/RPM-GPG-KEY.dag
diff --git a/test/integration/targets/script/tasks/main.yml b/test/integration/targets/script/tasks/main.yml
index 74189f8..668ec48 100644
--- a/test/integration/targets/script/tasks/main.yml
+++ b/test/integration/targets/script/tasks/main.yml
@@ -37,6 +37,17 @@
## script
##
+- name: Required one of free-form and cmd
+ script:
+ ignore_errors: yes
+ register: script_required
+
+- name: assert that the script fails if neither free-form nor cmd is given
+ assert:
+ that:
+ - script_required.failed
+ - "'one of the following' in script_required.msg"
+
- name: execute the test.sh script via command
script: test.sh
register: script_result0
diff --git a/test/integration/targets/service/aliases b/test/integration/targets/service/aliases
index f2f9ac9..f3703f8 100644
--- a/test/integration/targets/service/aliases
+++ b/test/integration/targets/service/aliases
@@ -1,4 +1,3 @@
destructive
shippable/posix/group1
-skip/osx
skip/macos
diff --git a/test/integration/targets/service/files/ansible_test_service.py b/test/integration/targets/service/files/ansible_test_service.py
index 522493f..6292272 100644
--- a/test/integration/targets/service/files/ansible_test_service.py
+++ b/test/integration/targets/service/files/ansible_test_service.py
@@ -9,7 +9,6 @@ __metaclass__ = type
import os
import resource
import signal
-import sys
import time
UMASK = 0
diff --git a/test/integration/targets/service_facts/aliases b/test/integration/targets/service_facts/aliases
index 17d3eb7..32e10b0 100644
--- a/test/integration/targets/service_facts/aliases
+++ b/test/integration/targets/service_facts/aliases
@@ -1,4 +1,3 @@
shippable/posix/group2
skip/freebsd
-skip/osx
skip/macos
diff --git a/test/integration/targets/setup_deb_repo/tasks/main.yml b/test/integration/targets/setup_deb_repo/tasks/main.yml
index 471fb2a..3e640f6 100644
--- a/test/integration/targets/setup_deb_repo/tasks/main.yml
+++ b/test/integration/targets/setup_deb_repo/tasks/main.yml
@@ -59,6 +59,7 @@
loop:
- stable
- testing
+ when: install_repo|default(True)|bool is true
# Need to uncomment the deb-src for the universe component for build-dep state
- name: Ensure deb-src for the universe component
diff --git a/test/integration/targets/setup_paramiko/install-Alpine-3-python-3.yml b/test/integration/targets/setup_paramiko/install-Alpine-3-python-3.yml
index f16d9b5..8c0b28b 100644
--- a/test/integration/targets/setup_paramiko/install-Alpine-3-python-3.yml
+++ b/test/integration/targets/setup_paramiko/install-Alpine-3-python-3.yml
@@ -1,9 +1,2 @@
-- name: Setup remote constraints
- include_tasks: setup-remote-constraints.yml
- name: Install Paramiko for Python 3 on Alpine
- pip: # no apk package manager in core, just use pip
- name: paramiko
- extra_args: "-c {{ remote_constraints }}"
- environment:
- # Not sure why this fixes the test, but it does.
- SETUPTOOLS_USE_DISTUTILS: stdlib
+ command: apk add py3-paramiko
diff --git a/test/integration/targets/setup_paramiko/install-CentOS-6-python-2.yml b/test/integration/targets/setup_paramiko/install-CentOS-6-python-2.yml
deleted file mode 100644
index 0c7b9e8..0000000
--- a/test/integration/targets/setup_paramiko/install-CentOS-6-python-2.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-- name: Install Paramiko for Python 2 on CentOS 6
- yum:
- name: python-paramiko
diff --git a/test/integration/targets/setup_paramiko/install-Fedora-35-python-3.yml b/test/integration/targets/setup_paramiko/install-Fedora-35-python-3.yml
deleted file mode 100644
index bbe97a9..0000000
--- a/test/integration/targets/setup_paramiko/install-Fedora-35-python-3.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-- name: Install Paramiko and crypto policies scripts
- dnf:
- name:
- - crypto-policies-scripts
- - python3-paramiko
- install_weak_deps: no
-
-- name: Drop the crypto-policy to LEGACY for these tests
- command: update-crypto-policies --set LEGACY
diff --git a/test/integration/targets/setup_paramiko/install-Ubuntu-16-python-2.yml b/test/integration/targets/setup_paramiko/install-Ubuntu-16-python-2.yml
deleted file mode 100644
index 8f76074..0000000
--- a/test/integration/targets/setup_paramiko/install-Ubuntu-16-python-2.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-- name: Install Paramiko for Python 2 on Ubuntu 16
- apt:
- name: python-paramiko
diff --git a/test/integration/targets/setup_paramiko/install-python-2.yml b/test/integration/targets/setup_paramiko/install-python-2.yml
deleted file mode 100644
index be337a1..0000000
--- a/test/integration/targets/setup_paramiko/install-python-2.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-- name: Install Paramiko for Python 2
- package:
- name: python2-paramiko
diff --git a/test/integration/targets/setup_paramiko/uninstall-Alpine-3-python-3.yml b/test/integration/targets/setup_paramiko/uninstall-Alpine-3-python-3.yml
index e9dcc62..edb504f 100644
--- a/test/integration/targets/setup_paramiko/uninstall-Alpine-3-python-3.yml
+++ b/test/integration/targets/setup_paramiko/uninstall-Alpine-3-python-3.yml
@@ -1,4 +1,2 @@
- name: Uninstall Paramiko for Python 3 on Alpine
- pip:
- name: paramiko
- state: absent
+ command: apk del py3-paramiko
diff --git a/test/integration/targets/setup_paramiko/uninstall-Fedora-35-python-3.yml b/test/integration/targets/setup_paramiko/uninstall-Fedora-35-python-3.yml
deleted file mode 100644
index 6d0e9a1..0000000
--- a/test/integration/targets/setup_paramiko/uninstall-Fedora-35-python-3.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-- name: Revert the crypto-policy back to DEFAULT
- command: update-crypto-policies --set DEFAULT
-
-- name: Uninstall Paramiko and crypto policies scripts using dnf history undo
- command: dnf history undo last --assumeyes
diff --git a/test/integration/targets/setup_paramiko/uninstall-apt-python-2.yml b/test/integration/targets/setup_paramiko/uninstall-apt-python-2.yml
deleted file mode 100644
index 507d94c..0000000
--- a/test/integration/targets/setup_paramiko/uninstall-apt-python-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-- name: Uninstall Paramiko for Python 2 using apt
- apt:
- name: python-paramiko
- state: absent
- autoremove: yes
diff --git a/test/integration/targets/setup_paramiko/uninstall-zypper-python-2.yml b/test/integration/targets/setup_paramiko/uninstall-zypper-python-2.yml
deleted file mode 100644
index adb26e5..0000000
--- a/test/integration/targets/setup_paramiko/uninstall-zypper-python-2.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-- name: Uninstall Paramiko for Python 2 using zypper
- command: zypper --quiet --non-interactive remove --clean-deps python2-paramiko
diff --git a/test/integration/targets/setup_rpm_repo/tasks/main.yml b/test/integration/targets/setup_rpm_repo/tasks/main.yml
index be20078..bf5af10 100644
--- a/test/integration/targets/setup_rpm_repo/tasks/main.yml
+++ b/test/integration/targets/setup_rpm_repo/tasks/main.yml
@@ -24,9 +24,18 @@
args:
name: "{{ rpm_repo_packages }}"
- - name: Install rpmfluff via pip
- pip:
- name: rpmfluff
+ - name: Install rpmfluff via pip, ensure it is installed with default python as python3-rpm may not exist for other versions
+ block:
+ - action: "{{ ansible_facts.pkg_mgr }}"
+ args:
+ name:
+ - python3-pip
+ - python3
+ state: latest
+
+ - pip:
+ name: rpmfluff
+ executable: pip3
when: ansible_facts.os_family == 'RedHat' and ansible_distribution_major_version is version('9', '==')
- set_fact:
diff --git a/test/integration/targets/strategy_linear/runme.sh b/test/integration/targets/strategy_linear/runme.sh
index cbb6aea..a2734f9 100755
--- a/test/integration/targets/strategy_linear/runme.sh
+++ b/test/integration/targets/strategy_linear/runme.sh
@@ -5,3 +5,5 @@ set -eux
ansible-playbook test_include_file_noop.yml -i inventory "$@"
ansible-playbook task_action_templating.yml -i inventory "$@"
+
+ansible-playbook task_templated_run_once.yml -i inventory "$@"
diff --git a/test/integration/targets/strategy_linear/task_templated_run_once.yml b/test/integration/targets/strategy_linear/task_templated_run_once.yml
new file mode 100644
index 0000000..bacf06a
--- /dev/null
+++ b/test/integration/targets/strategy_linear/task_templated_run_once.yml
@@ -0,0 +1,20 @@
+- hosts: testhost,testhost2
+ gather_facts: no
+ vars:
+ run_once_flag: false
+ tasks:
+ - debug:
+ msg: "I am {{ item }}"
+ run_once: "{{ run_once_flag }}"
+ register: reg1
+ loop:
+ - "{{ inventory_hostname }}"
+
+ - assert:
+ that:
+ - "reg1.results[0].msg == 'I am testhost'"
+ when: inventory_hostname == 'testhost'
+ - assert:
+ that:
+ - "reg1.results[0].msg == 'I am testhost2'"
+ when: inventory_hostname == 'testhost2'
diff --git a/test/integration/targets/subversion/aliases b/test/integration/targets/subversion/aliases
index 3cc41e4..03b9643 100644
--- a/test/integration/targets/subversion/aliases
+++ b/test/integration/targets/subversion/aliases
@@ -1,6 +1,4 @@
shippable/posix/group2
-skip/osx
skip/macos
-skip/rhel/9.0b # svn checkout hangs
destructive
needs/root
diff --git a/test/integration/targets/support-callback_plugins/aliases b/test/integration/targets/support-callback_plugins/aliases
new file mode 100644
index 0000000..136c05e
--- /dev/null
+++ b/test/integration/targets/support-callback_plugins/aliases
@@ -0,0 +1 @@
+hidden
diff --git a/test/integration/targets/ansible/callback_plugins/callback_debug.py b/test/integration/targets/support-callback_plugins/callback_plugins/callback_debug.py
index 2462c1f..2462c1f 100644
--- a/test/integration/targets/ansible/callback_plugins/callback_debug.py
+++ b/test/integration/targets/support-callback_plugins/callback_plugins/callback_debug.py
diff --git a/test/integration/targets/systemd/tasks/test_indirect_service.yml b/test/integration/targets/systemd/tasks/test_indirect_service.yml
index fc11343..0df6048 100644
--- a/test/integration/targets/systemd/tasks/test_indirect_service.yml
+++ b/test/integration/targets/systemd/tasks/test_indirect_service.yml
@@ -34,4 +34,4 @@
- assert:
that:
- systemd_enable_dummy_indirect_1 is changed
- - systemd_enable_dummy_indirect_2 is not changed \ No newline at end of file
+ - systemd_enable_dummy_indirect_2 is not changed
diff --git a/test/integration/targets/systemd/vars/Debian.yml b/test/integration/targets/systemd/vars/Debian.yml
index 613410f..2dd0aff 100644
--- a/test/integration/targets/systemd/vars/Debian.yml
+++ b/test/integration/targets/systemd/vars/Debian.yml
@@ -1,3 +1,3 @@
ssh_service: ssh
sleep_bin_path: /bin/sleep
-indirect_service: dummy \ No newline at end of file
+indirect_service: dummy
diff --git a/test/integration/targets/tags/runme.sh b/test/integration/targets/tags/runme.sh
index 9da0b30..7dcb998 100755
--- a/test/integration/targets/tags/runme.sh
+++ b/test/integration/targets/tags/runme.sh
@@ -73,3 +73,12 @@ ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=list --tags t
ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged --tags untagged "$@"
ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged_list --tags untagged,tag3 "$@"
ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=tagged --tags tagged "$@"
+
+ansible-playbook test_template_parent_tags.yml "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'Tagged_task')" = "1" ]; rm out.txt
+
+ansible-playbook test_template_parent_tags.yml --tags tag1 "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'Tagged_task')" = "1" ]; rm out.txt
+
+ansible-playbook test_template_parent_tags.yml --skip-tags tag1 "$@" 2>&1 | tee out.txt
+[ "$(grep out.txt -ce 'Tagged_task')" = "0" ]; rm out.txt
diff --git a/test/integration/targets/tags/test_template_parent_tags.yml b/test/integration/targets/tags/test_template_parent_tags.yml
new file mode 100644
index 0000000..ea1c828
--- /dev/null
+++ b/test/integration/targets/tags/test_template_parent_tags.yml
@@ -0,0 +1,10 @@
+- hosts: localhost
+ gather_facts: false
+ vars:
+ tags_in_var:
+ - tag1
+ tasks:
+ - block:
+ - name: Tagged_task
+ debug:
+ tags: "{{ tags_in_var }}"
diff --git a/test/integration/targets/tasks/playbook.yml b/test/integration/targets/tasks/playbook.yml
index 80d9f8b..10bd859 100644
--- a/test/integration/targets/tasks/playbook.yml
+++ b/test/integration/targets/tasks/playbook.yml
@@ -6,6 +6,11 @@
debug:
msg: Hello
+ # ensure we properly test for an action name, not a task name when cheking for a meta task
+ - name: "meta"
+ debug:
+ msg: Hello
+
- name: ensure malformed raw_params on arbitrary actions are not ignored
debug:
garbage {{"with a template"}}
diff --git a/test/integration/targets/tasks/runme.sh b/test/integration/targets/tasks/runme.sh
index 594447b..57cbf28 100755
--- a/test/integration/targets/tasks/runme.sh
+++ b/test/integration/targets/tasks/runme.sh
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-ansible-playbook playbook.yml "$@"
+ansible-playbook playbook.yml \ No newline at end of file
diff --git a/test/integration/targets/template/ansible_managed_79129.yml b/test/integration/targets/template/ansible_managed_79129.yml
new file mode 100644
index 0000000..e00ada8
--- /dev/null
+++ b/test/integration/targets/template/ansible_managed_79129.yml
@@ -0,0 +1,29 @@
+---
+- hosts: testhost
+ gather_facts: false
+ tasks:
+ - set_fact:
+ output_dir: "{{ lookup('env', 'OUTPUT_DIR') }}"
+
+ - name: check strftime
+ block:
+ - template:
+ src: "templates/%necho Onii-chan help Im stuck;exit 1%n.j2"
+ dest: "{{ output_dir }}/79129-strftime.sh"
+ mode: '0755'
+
+ - shell: "exec {{ output_dir | quote }}/79129-strftime.sh"
+
+ - name: check jinja template
+ block:
+ - template:
+ src: !unsafe "templates/completely{{ 1 % 0 }} safe template.j2"
+ dest: "{{ output_dir }}/79129-jinja.sh"
+ mode: '0755'
+
+ - shell: "exec {{ output_dir | quote }}/79129-jinja.sh"
+ register: result
+
+ - assert:
+ that:
+ - "'Hello' in result.stdout"
diff --git a/test/integration/targets/template/arg_template_overrides.j2 b/test/integration/targets/template/arg_template_overrides.j2
new file mode 100644
index 0000000..17a79b9
--- /dev/null
+++ b/test/integration/targets/template/arg_template_overrides.j2
@@ -0,0 +1,4 @@
+var_a: << var_a >>
+var_b: << var_b >>
+var_c: << var_c >>
+var_d: << var_d >>
diff --git a/test/integration/targets/template/in_template_overrides.yml b/test/integration/targets/template/in_template_overrides.yml
deleted file mode 100644
index 3c2d4d9..0000000
--- a/test/integration/targets/template/in_template_overrides.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-- hosts: localhost
- gather_facts: false
- vars:
- var_a: "value"
- var_b: "{{ var_a }}"
- var_c: "<< var_a >>"
- tasks:
- - set_fact:
- var_d: "{{ var_a }}"
-
- - block:
- - template:
- src: in_template_overrides.j2
- dest: out.txt
-
- - command: cat out.txt
- register: out
-
- - assert:
- that:
- - "'var_a: value' in out.stdout"
- - "'var_b: value' in out.stdout"
- - "'var_c: << var_a >>' in out.stdout"
- - "'var_d: value' in out.stdout"
- always:
- - file:
- path: out.txt
- state: absent
diff --git a/test/integration/targets/template/runme.sh b/test/integration/targets/template/runme.sh
index 30163af..d3913d9 100755
--- a/test/integration/targets/template/runme.sh
+++ b/test/integration/targets/template/runme.sh
@@ -8,7 +8,10 @@ ANSIBLE_ROLES_PATH=../ ansible-playbook template.yml -i ../../inventory -v "$@"
ansible testhost -i testhost, -m debug -a 'msg={{ hostvars["localhost"] }}' -e "vars1={{ undef() }}" -e "vars2={{ vars1 }}"
# Test for https://github.com/ansible/ansible/issues/27262
-ansible-playbook ansible_managed.yml -c ansible_managed.cfg -i ../../inventory -v "$@"
+ANSIBLE_CONFIG=ansible_managed.cfg ansible-playbook ansible_managed.yml -i ../../inventory -v "$@"
+
+# Test for https://github.com/ansible/ansible/pull/79129
+ANSIBLE_CONFIG=ansible_managed.cfg ansible-playbook ansible_managed_79129.yml -i ../../inventory -v "$@"
# Test for #42585
ANSIBLE_ROLES_PATH=../ ansible-playbook custom_template.yml -i ../../inventory -v "$@"
@@ -39,7 +42,7 @@ ansible-playbook 72262.yml -v "$@"
ansible-playbook unsafe.yml -v "$@"
# ensure Jinja2 overrides from a template are used
-ansible-playbook in_template_overrides.yml -v "$@"
+ansible-playbook template_overrides.yml -v "$@"
ansible-playbook lazy_eval.yml -i ../../inventory -v "$@"
diff --git a/test/integration/targets/template/tasks/main.yml b/test/integration/targets/template/tasks/main.yml
index 3c91734..34e8828 100644
--- a/test/integration/targets/template/tasks/main.yml
+++ b/test/integration/targets/template/tasks/main.yml
@@ -25,7 +25,7 @@
- name: show jinja2 version
debug:
- msg: "{{ lookup('pipe', '{{ ansible_python[\"executable\"] }} -c \"import jinja2; print(jinja2.__version__)\"') }}"
+ msg: "{{ lookup('pipe', ansible_python.executable ~ ' -c \"import jinja2; print(jinja2.__version__)\"') }}"
- name: get default group
shell: id -gn
@@ -760,7 +760,7 @@
that:
- test
vars:
- test: "{{ lookup('file', '{{ output_dir }}/empty_template.templated')|length == 0 }}"
+ test: "{{ lookup('file', output_dir ~ '/empty_template.templated')|length == 0 }}"
- name: test jinja2 override without colon throws proper error
block:
diff --git a/test/integration/targets/template/template_overrides.yml b/test/integration/targets/template/template_overrides.yml
new file mode 100644
index 0000000..50cfb8f
--- /dev/null
+++ b/test/integration/targets/template/template_overrides.yml
@@ -0,0 +1,38 @@
+- hosts: localhost
+ gather_facts: false
+ vars:
+ output_dir: "{{ lookup('env', 'OUTPUT_DIR') }}"
+ var_a: "value"
+ var_b: "{{ var_a }}"
+ var_c: "<< var_a >>"
+ tasks:
+ - set_fact:
+ var_d: "{{ var_a }}"
+
+ - template:
+ src: in_template_overrides.j2
+ dest: '{{ output_dir }}/in_template_overrides.out'
+
+ - template:
+ src: arg_template_overrides.j2
+ dest: '{{ output_dir }}/arg_template_overrides.out'
+ variable_start_string: '<<'
+ variable_end_string: '>>'
+
+ - command: cat '{{ output_dir }}/in_template_overrides.out'
+ register: in_template_overrides_out
+
+ - command: cat '{{ output_dir }}/arg_template_overrides.out'
+ register: arg_template_overrides_out
+
+ - assert:
+ that:
+ - "'var_a: value' in in_template_overrides_out.stdout"
+ - "'var_b: value' in in_template_overrides_out.stdout"
+ - "'var_c: << var_a >>' in in_template_overrides_out.stdout"
+ - "'var_d: value' in in_template_overrides_out.stdout"
+
+ - "'var_a: value' in arg_template_overrides_out.stdout"
+ - "'var_b: value' in arg_template_overrides_out.stdout"
+ - "'var_c: << var_a >>' in arg_template_overrides_out.stdout"
+ - "'var_d: value' in arg_template_overrides_out.stdout"
diff --git a/test/integration/targets/template/templates/%necho Onii-chan help Im stuck;exit 1%n.j2 b/test/integration/targets/template/templates/%necho Onii-chan help Im stuck;exit 1%n.j2
new file mode 100644
index 0000000..2d63c15
--- /dev/null
+++ b/test/integration/targets/template/templates/%necho Onii-chan help Im stuck;exit 1%n.j2
@@ -0,0 +1,3 @@
+# {{ ansible_managed }}
+echo 79129 test passed
+exit 0
diff --git a/test/integration/targets/template/templates/completely{{ 1 % 0 }} safe template.j2 b/test/integration/targets/template/templates/completely{{ 1 % 0 }} safe template.j2
new file mode 100644
index 0000000..c9a04b4
--- /dev/null
+++ b/test/integration/targets/template/templates/completely{{ 1 % 0 }} safe template.j2
@@ -0,0 +1,3 @@
+# {{ ansible_managed }}
+echo Hello
+exit 0
diff --git a/test/integration/targets/template/unsafe.yml b/test/integration/targets/template/unsafe.yml
index bef9a4b..6f16388 100644
--- a/test/integration/targets/template/unsafe.yml
+++ b/test/integration/targets/template/unsafe.yml
@@ -3,6 +3,7 @@
vars:
nottemplated: this should not be seen
imunsafe: !unsafe '{{ nottemplated }}'
+ unsafe_set: !unsafe '{{ "test" }}'
tasks:
- set_fact:
@@ -12,11 +13,15 @@
- set_fact:
this_always_safe: '{{ imunsafe }}'
+ - set_fact:
+ this_unsafe_set: "{{ unsafe_set }}"
+
- name: ensure nothing was templated
assert:
that:
- this_always_safe == imunsafe
- imunsafe == this_was_unsafe.strip()
+ - unsafe_set == this_unsafe_set.strip()
- hosts: localhost
diff --git a/test/integration/targets/template_jinja2_non_native/macro_override.yml b/test/integration/targets/template_jinja2_non_native/macro_override.yml
index 8a1cabd..c3f9ab6 100644
--- a/test/integration/targets/template_jinja2_non_native/macro_override.yml
+++ b/test/integration/targets/template_jinja2_non_native/macro_override.yml
@@ -12,4 +12,4 @@
- "'foobar' not in data"
- "'\"foo\" \"bar\"' in data"
vars:
- data: "{{ lookup('file', '{{ output_dir }}/macro_override.out') }}"
+ data: "{{ lookup('file', output_dir ~ '/macro_override.out') }}"
diff --git a/test/integration/targets/templating/tasks/main.yml b/test/integration/targets/templating/tasks/main.yml
index 312e171..edbf012 100644
--- a/test/integration/targets/templating/tasks/main.yml
+++ b/test/integration/targets/templating/tasks/main.yml
@@ -33,3 +33,14 @@
- result is failed
- >-
"TemplateSyntaxError: Could not load \"asdf \": 'invalid plugin name: ansible.builtin.asdf '" in result.msg
+
+- name: Make sure syntax errors originating from a template being compiled into Python code object result in a failure
+ debug:
+ msg: "{{ lookup('vars', 'v1', default='', default='') }}"
+ ignore_errors: true
+ register: r
+
+- assert:
+ that:
+ - r is failed
+ - "'keyword argument repeated' in r.msg"
diff --git a/test/integration/targets/test_core/tasks/main.yml b/test/integration/targets/test_core/tasks/main.yml
index 8c2decb..ac06d67 100644
--- a/test/integration/targets/test_core/tasks/main.yml
+++ b/test/integration/targets/test_core/tasks/main.yml
@@ -126,6 +126,16 @@
hello: world
register: executed_task
+- name: Skip me with multiple conditions
+ set_fact:
+ hello: world
+ when:
+ - True == True
+ - foo == 'bar'
+ vars:
+ foo: foo
+ register: skipped_task_multi_condition
+
- name: Try skipped test on non-dictionary
set_fact:
hello: "{{ 'nope' is skipped }}"
@@ -136,8 +146,11 @@
assert:
that:
- skipped_task is skipped
+ - skipped_task.false_condition == False
- executed_task is not skipped
- misuse_of_skipped is failure
+ - skipped_task_multi_condition is skipped
+ - skipped_task_multi_condition.false_condition == "foo == 'bar'"
- name: Not an async task
set_fact:
diff --git a/test/integration/targets/test_utils/aliases b/test/integration/targets/test_utils/aliases
new file mode 100644
index 0000000..136c05e
--- /dev/null
+++ b/test/integration/targets/test_utils/aliases
@@ -0,0 +1 @@
+hidden
diff --git a/test/integration/targets/test_utils/scripts/timeout.py b/test/integration/targets/test_utils/scripts/timeout.py
new file mode 100755
index 0000000..f88f3e4
--- /dev/null
+++ b/test/integration/targets/test_utils/scripts/timeout.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import argparse
+import subprocess
+import sys
+
+parser = argparse.ArgumentParser()
+parser.add_argument('duration', type=int)
+parser.add_argument('command', nargs='+')
+args = parser.parse_args()
+
+try:
+ p = subprocess.run(
+ ' '.join(args.command),
+ shell=True,
+ timeout=args.duration,
+ check=False,
+ )
+ sys.exit(p.returncode)
+except subprocess.TimeoutExpired:
+ sys.exit(124)
diff --git a/test/integration/targets/unarchive/runme.sh b/test/integration/targets/unarchive/runme.sh
new file mode 100755
index 0000000..5351a0c
--- /dev/null
+++ b/test/integration/targets/unarchive/runme.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -eux
+
+ansible-playbook -i ../../inventory runme.yml -v "$@"
+
+# https://github.com/ansible/ansible/issues/80710
+ANSIBLE_REMOTE_TMP=./ansible ansible-playbook -i ../../inventory test_relative_tmp_dir.yml -v "$@"
diff --git a/test/integration/targets/unarchive/runme.yml b/test/integration/targets/unarchive/runme.yml
new file mode 100644
index 0000000..ddcd609
--- /dev/null
+++ b/test/integration/targets/unarchive/runme.yml
@@ -0,0 +1,4 @@
+- hosts: all
+ gather_facts: no
+ roles:
+ - { role: ../unarchive }
diff --git a/test/integration/targets/unarchive/tasks/main.yml b/test/integration/targets/unarchive/tasks/main.yml
index 148e583..b07c2fe 100644
--- a/test/integration/targets/unarchive/tasks/main.yml
+++ b/test/integration/targets/unarchive/tasks/main.yml
@@ -20,3 +20,4 @@
- import_tasks: test_different_language_var.yml
- import_tasks: test_invalid_options.yml
- import_tasks: test_ownership_top_folder.yml
+- import_tasks: test_relative_dest.yml
diff --git a/test/integration/targets/unarchive/tasks/test_different_language_var.yml b/test/integration/targets/unarchive/tasks/test_different_language_var.yml
index 9eec658..32c84f4 100644
--- a/test/integration/targets/unarchive/tasks/test_different_language_var.yml
+++ b/test/integration/targets/unarchive/tasks/test_different_language_var.yml
@@ -2,10 +2,10 @@
when: ansible_os_family == 'Debian'
block:
- name: install fr language pack
- apt:
+ apt:
name: language-pack-fr
state: present
-
+
- name: create our unarchive destination
file:
path: "{{ remote_tmp_dir }}/test-unarchive-nonascii-くらとみ-tar-gz"
diff --git a/test/integration/targets/unarchive/tasks/test_mode.yml b/test/integration/targets/unarchive/tasks/test_mode.yml
index 06fbc7b..efd428e 100644
--- a/test/integration/targets/unarchive/tasks/test_mode.yml
+++ b/test/integration/targets/unarchive/tasks/test_mode.yml
@@ -3,6 +3,29 @@
path: '{{remote_tmp_dir}}/test-unarchive-tar-gz'
state: directory
+- name: test invalid modes
+ unarchive:
+ src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
+ dest: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
+ remote_src: yes
+ mode: "{{ item }}"
+ list_files: True
+ register: unarchive_mode_errors
+ ignore_errors: yes
+ loop:
+ - u=foo
+ - foo=r
+ - ufoo=r
+ - abc=r
+ - ao=r
+ - oa=r
+
+- assert:
+ that:
+ - item.failed
+ - "'bad symbolic permission for mode: ' + item.item == item.details"
+ loop: "{{ unarchive_mode_errors.results }}"
+
- name: unarchive and set mode to 0600, directories 0700
unarchive:
src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
diff --git a/test/integration/targets/unarchive/tasks/test_relative_dest.yml b/test/integration/targets/unarchive/tasks/test_relative_dest.yml
new file mode 100644
index 0000000..aae31fb
--- /dev/null
+++ b/test/integration/targets/unarchive/tasks/test_relative_dest.yml
@@ -0,0 +1,26 @@
+- name: Create relative test directory
+ file:
+ path: test-unarchive-relative
+ state: directory
+
+- name: Unarchive a file using a relative destination path
+ unarchive:
+ src: "{{ remote_tmp_dir }}/test-unarchive.tar"
+ dest: test-unarchive-relative
+ remote_src: yes
+ register: relative_dest_1
+
+- name: Unarchive a file using a relative destination path again
+ unarchive:
+ src: "{{ remote_tmp_dir }}/test-unarchive.tar"
+ dest: test-unarchive-relative
+ remote_src: yes
+ register: relative_dest_2
+
+- name: Ensure changes were made correctly
+ assert:
+ that:
+ - relative_dest_1 is changed
+ - relative_dest_1.warnings | length > 0
+ - relative_dest_1.warnings[0] is search('absolute path')
+ - relative_dest_2 is not changed
diff --git a/test/integration/targets/unarchive/test_relative_tmp_dir.yml b/test/integration/targets/unarchive/test_relative_tmp_dir.yml
new file mode 100644
index 0000000..f368f7a
--- /dev/null
+++ b/test/integration/targets/unarchive/test_relative_tmp_dir.yml
@@ -0,0 +1,10 @@
+- hosts: all
+ gather_facts: no
+ tasks:
+ - include_role:
+ name: ../setup_remote_tmp_dir
+ - include_role:
+ name: ../setup_gnutar
+ - include_tasks: tasks/prepare_tests.yml
+
+ - include_tasks: tasks/test_tar.yml
diff --git a/test/integration/targets/unsafe_writes/aliases b/test/integration/targets/unsafe_writes/aliases
index da1b554..3560af2 100644
--- a/test/integration/targets/unsafe_writes/aliases
+++ b/test/integration/targets/unsafe_writes/aliases
@@ -1,7 +1,6 @@
context/target
needs/root
skip/freebsd
-skip/osx
skip/macos
shippable/posix/group2
needs/target/setup_remote_tmp_dir
diff --git a/test/integration/targets/until/tasks/main.yml b/test/integration/targets/until/tasks/main.yml
index 2b2ac94..42ce9c8 100644
--- a/test/integration/targets/until/tasks/main.yml
+++ b/test/integration/targets/until/tasks/main.yml
@@ -82,3 +82,37 @@
register: counter
delay: 0.5
until: counter.rc == 0
+
+- name: test retries without explicit until, defaults to "until task succeeds"
+ block:
+ - name: EXPECTED FAILURE
+ fail:
+ retries: 3
+ delay: 0.1
+ register: r
+ ignore_errors: true
+
+ - assert:
+ that:
+ - r.attempts == 3
+
+ - vars:
+ test_file: "{{ lookup('env', 'OUTPUT_DIR') }}/until_success_test_file"
+ block:
+ - file:
+ name: "{{ test_file }}"
+ state: absent
+
+ - name: fail on the first invocation, succeed on the second
+ shell: "[ -f {{ test_file }} ] || (touch {{ test_file }} && false)"
+ retries: 5
+ delay: 0.1
+ register: r
+ always:
+ - file:
+ name: "{{ test_file }}"
+ state: absent
+
+ - assert:
+ that:
+ - r.attempts == 2
diff --git a/test/integration/targets/unvault/main.yml b/test/integration/targets/unvault/main.yml
index a0f97b4..8f0adc7 100644
--- a/test/integration/targets/unvault/main.yml
+++ b/test/integration/targets/unvault/main.yml
@@ -1,4 +1,5 @@
- hosts: localhost
+ gather_facts: false
tasks:
- set_fact:
unvaulted: "{{ lookup('unvault', 'vault') }}"
diff --git a/test/integration/targets/unvault/runme.sh b/test/integration/targets/unvault/runme.sh
index df4585e..054a14d 100755
--- a/test/integration/targets/unvault/runme.sh
+++ b/test/integration/targets/unvault/runme.sh
@@ -2,5 +2,5 @@
set -eux
-
+# simple run
ansible-playbook --vault-password-file password main.yml
diff --git a/test/integration/targets/uri/tasks/main.yml b/test/integration/targets/uri/tasks/main.yml
index 9ba09ec..ddae83a 100644
--- a/test/integration/targets/uri/tasks/main.yml
+++ b/test/integration/targets/uri/tasks/main.yml
@@ -132,7 +132,7 @@
- "result.changed == true"
- name: "get ca certificate {{ self_signed_host }}"
- get_url:
+ uri:
url: "http://{{ httpbin_host }}/ca2cert.pem"
dest: "{{ remote_tmp_dir }}/ca2cert.pem"
@@ -638,9 +638,18 @@
- assert:
that:
- result['set_cookie'] == 'Foo=bar, Baz=qux'
- # Python sorts cookies in order of most specific (ie. longest) path first
+ # Python 3.10 and earlier sorts cookies in order of most specific (ie. longest) path first
# items with the same path are reversed from response order
- result['cookies_string'] == 'Baz=qux; Foo=bar'
+ when: ansible_python_version is version('3.11', '<')
+
+- assert:
+ that:
+ - result['set_cookie'] == 'Foo=bar, Baz=qux'
+ # Python 3.11 no longer sorts cookies.
+ # See: https://github.com/python/cpython/issues/86232
+ - result['cookies_string'] == 'Foo=bar; Baz=qux'
+ when: ansible_python_version is version('3.11', '>=')
- name: Write out netrc template
template:
@@ -757,6 +766,30 @@
dest: "{{ remote_tmp_dir }}/output"
state: absent
+- name: Test download root to dir without content-disposition
+ uri:
+ url: "https://{{ httpbin_host }}/"
+ dest: "{{ remote_tmp_dir }}"
+ register: get_root_no_filename
+
+- name: Test downloading to dir without content-disposition
+ uri:
+ url: "https://{{ httpbin_host }}/response-headers"
+ dest: "{{ remote_tmp_dir }}"
+ register: get_dir_no_filename
+
+- name: Test downloading to dir with content-disposition
+ uri:
+ url: 'https://{{ httpbin_host }}/response-headers?Content-Disposition=attachment%3B%20filename%3D%22filename.json%22'
+ dest: "{{ remote_tmp_dir }}"
+ register: get_dir_filename
+
+- assert:
+ that:
+ - get_root_no_filename.path == remote_tmp_dir ~ "/index.html"
+ - get_dir_no_filename.path == remote_tmp_dir ~ "/response-headers"
+ - get_dir_filename.path == remote_tmp_dir ~ "/filename.json"
+
- name: Test follow_redirects=none
import_tasks: redirect-none.yml
diff --git a/test/integration/targets/uri/tasks/redirect-none.yml b/test/integration/targets/uri/tasks/redirect-none.yml
index 0d1b2b3..060950d 100644
--- a/test/integration/targets/uri/tasks/redirect-none.yml
+++ b/test/integration/targets/uri/tasks/redirect-none.yml
@@ -240,7 +240,7 @@
url: https://{{ httpbin_host }}/redirect-to?status_code=308&url=https://{{ httpbin_host }}/anything
follow_redirects: none
return_content: yes
- method: GET
+ method: HEAD
ignore_errors: yes
register: http_308_head
diff --git a/test/integration/targets/uri/tasks/redirect-urllib2.yml b/test/integration/targets/uri/tasks/redirect-urllib2.yml
index 6cdafdb..73e8796 100644
--- a/test/integration/targets/uri/tasks/redirect-urllib2.yml
+++ b/test/integration/targets/uri/tasks/redirect-urllib2.yml
@@ -237,7 +237,7 @@
url: https://{{ httpbin_host }}/redirect-to?status_code=308&url=https://{{ httpbin_host }}/anything
follow_redirects: urllib2
return_content: yes
- method: GET
+ method: HEAD
ignore_errors: yes
register: http_308_head
@@ -250,6 +250,23 @@
- http_308_head.redirected == false
- http_308_head.status == 308
- http_308_head.url == 'https://{{ httpbin_host }}/redirect-to?status_code=308&url=https://{{ httpbin_host }}/anything'
+ # Python 3.10 and earlier do not support HTTP 308 responses.
+ # See: https://github.com/python/cpython/issues/84501
+ when: ansible_python_version is version('3.11', '<')
+
+# NOTE: The HTTP HEAD turns into an HTTP GET
+- assert:
+ that:
+ - http_308_head is successful
+ - http_308_head.json.data == ''
+ - http_308_head.json.method == 'GET'
+ - http_308_head.json.url == 'https://{{ httpbin_host }}/anything'
+ - http_308_head.redirected == true
+ - http_308_head.status == 200
+ - http_308_head.url == 'https://{{ httpbin_host }}/anything'
+ # Python 3.11 introduced support for HTTP 308 responses.
+ # See: https://github.com/python/cpython/issues/84501
+ when: ansible_python_version is version('3.11', '>=')
# FIXME: This is fixed in https://github.com/ansible/ansible/pull/36809
- name: Test HTTP 308 using GET
@@ -270,6 +287,22 @@
- http_308_get.redirected == false
- http_308_get.status == 308
- http_308_get.url == 'https://{{ httpbin_host }}/redirect-to?status_code=308&url=https://{{ httpbin_host }}/anything'
+ # Python 3.10 and earlier do not support HTTP 308 responses.
+ # See: https://github.com/python/cpython/issues/84501
+ when: ansible_python_version is version('3.11', '<')
+
+- assert:
+ that:
+ - http_308_get is successful
+ - http_308_get.json.data == ''
+ - http_308_get.json.method == 'GET'
+ - http_308_get.json.url == 'https://{{ httpbin_host }}/anything'
+ - http_308_get.redirected == true
+ - http_308_get.status == 200
+ - http_308_get.url == 'https://{{ httpbin_host }}/anything'
+ # Python 3.11 introduced support for HTTP 308 responses.
+ # See: https://github.com/python/cpython/issues/84501
+ when: ansible_python_version is version('3.11', '>=')
# FIXME: This is fixed in https://github.com/ansible/ansible/pull/36809
- name: Test HTTP 308 using POST
diff --git a/test/integration/targets/uri/tasks/return-content.yml b/test/integration/targets/uri/tasks/return-content.yml
index 5a9b97e..cb8aeea 100644
--- a/test/integration/targets/uri/tasks/return-content.yml
+++ b/test/integration/targets/uri/tasks/return-content.yml
@@ -46,4 +46,4 @@
assert:
that:
- result is failed
- - "'content' not in result" \ No newline at end of file
+ - "'content' not in result"
diff --git a/test/integration/targets/uri/tasks/use_netrc.yml b/test/integration/targets/uri/tasks/use_netrc.yml
index da745b8..521f8eb 100644
--- a/test/integration/targets/uri/tasks/use_netrc.yml
+++ b/test/integration/targets/uri/tasks/use_netrc.yml
@@ -48,4 +48,4 @@
- name: Clean up
file:
dest: "{{ remote_tmp_dir }}/netrc"
- state: absent \ No newline at end of file
+ state: absent
diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml
index 9d36bfc..be4c4d6 100644
--- a/test/integration/targets/user/tasks/main.yml
+++ b/test/integration/targets/user/tasks/main.yml
@@ -31,7 +31,9 @@
- import_tasks: test_expires.yml
- import_tasks: test_expires_new_account.yml
- import_tasks: test_expires_new_account_epoch_negative.yml
+- import_tasks: test_expires_no_shadow.yml
- import_tasks: test_expires_min_max.yml
+- import_tasks: test_expires_warn.yml
- import_tasks: test_shadow_backup.yml
- import_tasks: test_ssh_key_passphrase.yml
- import_tasks: test_password_lock.yml
diff --git a/test/integration/targets/user/tasks/test_create_user.yml b/test/integration/targets/user/tasks/test_create_user.yml
index bced790..644dbeb 100644
--- a/test/integration/targets/user/tasks/test_create_user.yml
+++ b/test/integration/targets/user/tasks/test_create_user.yml
@@ -65,3 +65,15 @@
- "user_test1.results[2]['state'] == 'present'"
- "user_test1.results[3]['state'] == 'present'"
- "user_test1.results[4]['state'] == 'present'"
+
+- name: register user informations
+ when: ansible_facts.system == 'Darwin'
+ command: dscl . -read /Users/ansibulluser
+ register: user_test2
+
+- name: validate user defaults for MacOS
+ when: ansible_facts.system == 'Darwin'
+ assert:
+ that:
+ - "'RealName: ansibulluser' in user_test2.stdout_lines "
+ - "'PrimaryGroupID: 20' in user_test2.stdout_lines "
diff --git a/test/integration/targets/user/tasks/test_create_user_home.yml b/test/integration/targets/user/tasks/test_create_user_home.yml
index 1b529f7..5561a2f 100644
--- a/test/integration/targets/user/tasks/test_create_user_home.yml
+++ b/test/integration/targets/user/tasks/test_create_user_home.yml
@@ -134,3 +134,21 @@
name: randomuser
state: absent
remove: yes
+
+- name: Create user home directory with /dev/null as skeleton, https://github.com/ansible/ansible/issues/75063
+ # create_homedir is mostly used by linux, rest of OSs take care of it themselves via -k option (which fails this task)
+ when: ansible_system == 'Linux'
+ block:
+ - name: "Create user home directory with /dev/null as skeleton"
+ user:
+ name: withskeleton
+ state: present
+ skeleton: "/dev/null"
+ createhome: yes
+ register: create_user_with_skeleton_dev_null
+ always:
+ - name: "Remove test user"
+ user:
+ name: withskeleton
+ state: absent
+ remove: yes
diff --git a/test/integration/targets/user/tasks/test_expires_no_shadow.yml b/test/integration/targets/user/tasks/test_expires_no_shadow.yml
new file mode 100644
index 0000000..4629c6f
--- /dev/null
+++ b/test/integration/targets/user/tasks/test_expires_no_shadow.yml
@@ -0,0 +1,47 @@
+# https://github.com/ansible/ansible/issues/71916
+- name: Test setting expiration for a user account that does not have an /etc/shadow entry
+ when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse']
+ block:
+ - name: Remove ansibulluser
+ user:
+ name: ansibulluser
+ state: absent
+ remove: yes
+
+ - name: Create user account entry in /etc/passwd
+ lineinfile:
+ path: /etc/passwd
+ line: "ansibulluser::575:575::/home/dummy:/bin/bash"
+ regexp: "^ansibulluser.*"
+ state: present
+
+ - name: Create user with negative expiration
+ user:
+ name: ansibulluser
+ uid: 575
+ expires: -1
+ register: user_test_expires_no_shadow_1
+
+ - name: Create user with negative expiration again
+ user:
+ name: ansibulluser
+ uid: 575
+ expires: -1
+ register: user_test_expires_no_shadow_2
+
+ - name: Ensure changes were made appropriately
+ assert:
+ that:
+ - user_test_expires_no_shadow_1 is changed
+ - user_test_expires_no_shadow_2 is not changed
+
+ - name: Get expiration date for ansibulluser
+ getent:
+ database: shadow
+ key: ansibulluser
+
+ - name: LINUX | Ensure proper expiration date was set
+ assert:
+ msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}"
+ that:
+ - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0
diff --git a/test/integration/targets/user/tasks/test_expires_warn.yml b/test/integration/targets/user/tasks/test_expires_warn.yml
new file mode 100644
index 0000000..afe033c
--- /dev/null
+++ b/test/integration/targets/user/tasks/test_expires_warn.yml
@@ -0,0 +1,36 @@
+# https://github.com/ansible/ansible/issues/79882
+- name: Test setting warning days
+ when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse']
+ block:
+ - name: create user
+ user:
+ name: ansibulluser
+ state: present
+
+ - name: add warning days for password
+ user:
+ name: ansibulluser
+ password_expire_warn: 28
+ register: pass_warn_1_0
+
+ - name: again add warning days for password
+ user:
+ name: ansibulluser
+ password_expire_warn: 28
+ register: pass_warn_1_1
+
+ - name: validate result for warning days
+ assert:
+ that:
+ - pass_warn_1_0 is changed
+ - pass_warn_1_1 is not changed
+
+ - name: Get shadow data for ansibulluser
+ getent:
+ database: shadow
+ key: ansibulluser
+
+ - name: Ensure number of warning days was set properly
+ assert:
+ that:
+ - ansible_facts.getent_shadow['ansibulluser'][4] == '28'
diff --git a/test/integration/targets/user/tasks/test_local.yml b/test/integration/targets/user/tasks/test_local.yml
index 67c24a2..217d476 100644
--- a/test/integration/targets/user/tasks/test_local.yml
+++ b/test/integration/targets/user/tasks/test_local.yml
@@ -86,9 +86,11 @@
- testgroup3
- testgroup4
- testgroup5
+ - testgroup6
- local_ansibulluser
tags:
- user_test_local_mode
+ register: test_groups
- name: Create local_ansibulluser with groups
user:
@@ -113,6 +115,18 @@
tags:
- user_test_local_mode
+- name: Append groups for local_ansibulluser (again)
+ user:
+ name: local_ansibulluser
+ state: present
+ local: yes
+ groups: ['testgroup3', 'testgroup4']
+ append: yes
+ register: local_user_test_4_again
+ ignore_errors: yes
+ tags:
+ - user_test_local_mode
+
- name: Test append without groups for local_ansibulluser
user:
name: local_ansibulluser
@@ -133,6 +147,28 @@
tags:
- user_test_local_mode
+- name: Append groups for local_ansibulluser using group id
+ user:
+ name: local_ansibulluser
+ state: present
+ append: yes
+ groups: "{{ test_groups.results[5]['gid'] }}"
+ register: local_user_test_7
+ ignore_errors: yes
+ tags:
+ - user_test_local_mode
+
+- name: Append groups for local_ansibulluser using gid (again)
+ user:
+ name: local_ansibulluser
+ state: present
+ append: yes
+ groups: "{{ test_groups.results[5]['gid'] }}"
+ register: local_user_test_7_again
+ ignore_errors: yes
+ tags:
+ - user_test_local_mode
+
# If we don't re-assign, then "Set user expiration" will
# fail.
- name: Re-assign named group for local_ansibulluser
@@ -164,6 +200,7 @@
- testgroup3
- testgroup4
- testgroup5
+ - testgroup6
- local_ansibulluser
tags:
- user_test_local_mode
@@ -175,7 +212,10 @@
- local_user_test_2 is not changed
- local_user_test_3 is changed
- local_user_test_4 is changed
+ - local_user_test_4_again is not changed
- local_user_test_6 is changed
+ - local_user_test_7 is changed
+ - local_user_test_7_again is not changed
- local_user_test_remove_1 is changed
- local_user_test_remove_2 is not changed
tags:
diff --git a/test/integration/targets/user/vars/main.yml b/test/integration/targets/user/vars/main.yml
index 4b328f7..2acd1e1 100644
--- a/test/integration/targets/user/vars/main.yml
+++ b/test/integration/targets/user/vars/main.yml
@@ -10,4 +10,4 @@ status_command:
default_user_group:
openSUSE Leap: users
- MacOSX: admin
+ MacOSX: staff
diff --git a/test/integration/targets/var_blending/roles/test_var_blending/tasks/main.yml b/test/integration/targets/var_blending/roles/test_var_blending/tasks/main.yml
index f2b2e54..ef2a06e 100644
--- a/test/integration/targets/var_blending/roles/test_var_blending/tasks/main.yml
+++ b/test/integration/targets/var_blending/roles/test_var_blending/tasks/main.yml
@@ -1,4 +1,4 @@
-# test code
+# test code
# (c) 2014, Michael DeHaan <michael.dehaan@gmail.com>
# This file is part of Ansible
@@ -22,7 +22,7 @@
output_dir: "{{ lookup('env', 'OUTPUT_DIR') }}"
- name: deploy a template that will use variables at various levels
- template: src=foo.j2 dest={{output_dir}}/foo.templated
+ template: src=foo.j2 dest={{output_dir}}/foo.templated
register: template_result
- name: copy known good into place
@@ -33,9 +33,9 @@
register: diff_result
- name: verify templated file matches known good
- assert:
- that:
- - 'diff_result.stdout == ""'
+ assert:
+ that:
+ - 'diff_result.stdout == ""'
- name: check debug variable with same name as var content
debug: var=same_value_as_var_name_var
diff --git a/test/integration/targets/var_precedence/ansible-var-precedence-check.py b/test/integration/targets/var_precedence/ansible-var-precedence-check.py
index fc31688..b03c87b 100755
--- a/test/integration/targets/var_precedence/ansible-var-precedence-check.py
+++ b/test/integration/targets/var_precedence/ansible-var-precedence-check.py
@@ -14,7 +14,6 @@ import stat
import subprocess
import tempfile
import yaml
-from pprint import pprint
from optparse import OptionParser
from jinja2 import Environment
@@ -364,9 +363,9 @@ class VarTestMaker(object):
block_wrapper = [debug_task, test_task]
if 'include_params' in self.features:
- self.tasks.append(dict(name='including tasks', include='included_tasks.yml', vars=dict(findme='include_params')))
+ self.tasks.append(dict(name='including tasks', include_tasks='included_tasks.yml', vars=dict(findme='include_params')))
else:
- self.tasks.append(dict(include='included_tasks.yml'))
+ self.tasks.append(dict(include_tasks='included_tasks.yml'))
fname = os.path.join(TESTDIR, 'included_tasks.yml')
with open(fname, 'w') as f:
diff --git a/test/integration/targets/var_precedence/test_var_precedence.yml b/test/integration/targets/var_precedence/test_var_precedence.yml
index 58584bf..bba661d 100644
--- a/test/integration/targets/var_precedence/test_var_precedence.yml
+++ b/test/integration/targets/var_precedence/test_var_precedence.yml
@@ -1,14 +1,18 @@
---
- hosts: testhost
vars:
- - ansible_hostname: "BAD!"
- - vars_var: "vars_var"
- - param_var: "BAD!"
- - vars_files_var: "BAD!"
- - extra_var_override_once_removed: "{{ extra_var_override }}"
- - from_inventory_once_removed: "{{ inven_var | default('BAD!') }}"
+ ansible_hostname: "BAD!"
+ vars_var: "vars_var"
+ param_var: "BAD!"
+ vars_files_var: "BAD!"
+ extra_var_override_once_removed: "{{ extra_var_override }}"
+ from_inventory_once_removed: "{{ inven_var | default('BAD!') }}"
vars_files:
- vars/test_var_precedence.yml
+ pre_tasks:
+ - name: param vars should also override set_fact
+ set_fact:
+ param_var: "BAD!"
roles:
- { role: test_var_precedence, param_var: "param_var" }
tasks:
diff --git a/test/integration/targets/vars_files/aliases b/test/integration/targets/vars_files/aliases
new file mode 100644
index 0000000..8278ec8
--- /dev/null
+++ b/test/integration/targets/vars_files/aliases
@@ -0,0 +1,2 @@
+shippable/posix/group3
+context/controller
diff --git a/test/integration/targets/vars_files/inventory b/test/integration/targets/vars_files/inventory
new file mode 100644
index 0000000..88dae26
--- /dev/null
+++ b/test/integration/targets/vars_files/inventory
@@ -0,0 +1,3 @@
+[testgroup]
+testhost foo=bar
+testhost2 foo=baz
diff --git a/test/integration/targets/vars_files/runme.sh b/test/integration/targets/vars_files/runme.sh
new file mode 100755
index 0000000..127536f
--- /dev/null
+++ b/test/integration/targets/vars_files/runme.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -eux
+
+ansible-playbook runme.yml -i inventory -v "$@"
diff --git a/test/integration/targets/vars_files/runme.yml b/test/integration/targets/vars_files/runme.yml
new file mode 100644
index 0000000..257f929
--- /dev/null
+++ b/test/integration/targets/vars_files/runme.yml
@@ -0,0 +1,22 @@
+---
+- hosts: testgroup
+ gather_facts: no
+ vars_files:
+ - "vars/common.yml"
+ -
+ - "vars/{{ foo }}.yml"
+ - "vars/defaults.yml"
+ tasks:
+ - import_tasks: validate.yml
+
+- hosts: testgroup
+ gather_facts: no
+ vars:
+ _vars_files:
+ - 'vars/{{ foo }}.yml'
+ - 'vars/defaults.yml'
+ vars_files:
+ - "vars/common.yml"
+ - "{{ lookup('first_found', _vars_files) }}"
+ tasks:
+ - import_tasks: validate.yml
diff --git a/test/integration/targets/vars_files/validate.yml b/test/integration/targets/vars_files/validate.yml
new file mode 100644
index 0000000..dc889c5
--- /dev/null
+++ b/test/integration/targets/vars_files/validate.yml
@@ -0,0 +1,11 @@
+- assert:
+ that:
+ - common is true
+- assert:
+ that:
+ - is_bar is true
+ when: inventory_hostname == 'testhost'
+- assert:
+ that:
+ - is_bar is false
+ when: inventory_hostname == 'testhost2'
diff --git a/test/integration/targets/vars_files/vars/bar.yml b/test/integration/targets/vars_files/vars/bar.yml
new file mode 100644
index 0000000..d6f3c5b
--- /dev/null
+++ b/test/integration/targets/vars_files/vars/bar.yml
@@ -0,0 +1 @@
+is_bar: yes
diff --git a/test/integration/targets/vars_files/vars/common.yml b/test/integration/targets/vars_files/vars/common.yml
new file mode 100644
index 0000000..a8cd808
--- /dev/null
+++ b/test/integration/targets/vars_files/vars/common.yml
@@ -0,0 +1 @@
+common: yes
diff --git a/test/integration/targets/vars_files/vars/defaults.yml b/test/integration/targets/vars_files/vars/defaults.yml
new file mode 100644
index 0000000..4a7bfac
--- /dev/null
+++ b/test/integration/targets/vars_files/vars/defaults.yml
@@ -0,0 +1 @@
+is_bar: no
diff --git a/test/integration/targets/wait_for/tasks/main.yml b/test/integration/targets/wait_for/tasks/main.yml
index f81fd0f..74b8e9a 100644
--- a/test/integration/targets/wait_for/tasks/main.yml
+++ b/test/integration/targets/wait_for/tasks/main.yml
@@ -91,7 +91,7 @@
wait_for:
path: "{{remote_tmp_dir}}/wait_for_keyword"
search_regex: completed (?P<foo>\w+) ([0-9]+)
- timeout: 5
+ timeout: 25
register: waitfor
- name: verify test wait for keyword in file with match groups
@@ -114,6 +114,15 @@
path: "{{remote_tmp_dir}}/utf16.txt"
search_regex: completed
+- name: test non mmapable file
+ wait_for:
+ path: "/sys/class/net/lo/carrier"
+ search_regex: "1"
+ timeout: 30
+ when:
+ - ansible_facts['os_family'] not in ['FreeBSD', 'Darwin']
+ - not (ansible_facts['os_family'] in ['RedHat', 'CentOS'] and ansible_facts['distribution_major_version'] is version('7', '<='))
+
- name: test wait for port timeout
wait_for:
port: 12121
diff --git a/test/integration/targets/win_exec_wrapper/action_plugins/test_rc_1.py b/test/integration/targets/win_exec_wrapper/action_plugins/test_rc_1.py
new file mode 100644
index 0000000..60cffde
--- /dev/null
+++ b/test/integration/targets/win_exec_wrapper/action_plugins/test_rc_1.py
@@ -0,0 +1,35 @@
+# Copyright: (c) 2023, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+import json
+
+from ansible.plugins.action import ActionBase
+
+
+class ActionModule(ActionBase):
+
+ def run(self, tmp=None, task_vars=None):
+ super().run(tmp, task_vars)
+ del tmp
+
+ exec_command = self._connection.exec_command
+
+ def patched_exec_command(*args, **kwargs):
+ rc, stdout, stderr = exec_command(*args, **kwargs)
+
+ new_stdout = json.dumps({
+ "rc": rc,
+ "stdout": stdout.decode(),
+ "stderr": stderr.decode(),
+ "failed": False,
+ "changed": False,
+ }).encode()
+
+ return (0, new_stdout, b"")
+
+ try:
+ # This is done to capture the raw rc/stdio from the module exec
+ self._connection.exec_command = patched_exec_command
+ return self._execute_module(task_vars=task_vars)
+ finally:
+ self._connection.exec_command = exec_command
diff --git a/test/integration/targets/win_exec_wrapper/library/test_rc_1.ps1 b/test/integration/targets/win_exec_wrapper/library/test_rc_1.ps1
new file mode 100644
index 0000000..a987954
--- /dev/null
+++ b/test/integration/targets/win_exec_wrapper/library/test_rc_1.ps1
@@ -0,0 +1,17 @@
+#!powershell
+
+# This scenario needs to use Legacy, the same HadErrors won't be set if using
+# Ansible.Basic
+#Requires -Module Ansible.ModuleUtils.Legacy
+
+# This will set `$ps.HadErrors` in the running pipeline but with no error
+# record written. We are testing that it won't set the rc to 1 for this
+# scenario.
+try {
+ Write-Error -Message err -ErrorAction Stop
+}
+catch {
+ Exit-Json @{}
+}
+
+Fail-Json @{} "This should not be reached"
diff --git a/test/integration/targets/win_exec_wrapper/tasks/main.yml b/test/integration/targets/win_exec_wrapper/tasks/main.yml
index 8fc54f7..f1342c4 100644
--- a/test/integration/targets/win_exec_wrapper/tasks/main.yml
+++ b/test/integration/targets/win_exec_wrapper/tasks/main.yml
@@ -272,3 +272,12 @@
assert:
that:
- ps_log_count.stdout | int == 0
+
+- name: test module that sets HadErrors with no error records
+ test_rc_1:
+ register: module_had_errors
+
+- name: assert test module that sets HadErrors with no error records
+ assert:
+ that:
+ - module_had_errors.rc == 0
diff --git a/test/integration/targets/win_fetch/tasks/main.yml b/test/integration/targets/win_fetch/tasks/main.yml
index b581835..16a2876 100644
--- a/test/integration/targets/win_fetch/tasks/main.yml
+++ b/test/integration/targets/win_fetch/tasks/main.yml
@@ -215,3 +215,17 @@
- fetch_special_file.checksum == '34d4150adc3347f1dd8ce19fdf65b74d971ab602'
- fetch_special_file.dest == host_output_dir + "/abc$not var'quote‘"
- fetch_special_file_actual.stdout == 'abc'
+
+- name: create file with wildcard characters
+ raw: Set-Content -LiteralPath '{{ remote_tmp_dir }}\abc[].txt' -Value 'abc'
+
+- name: fetch file with wildcard characters
+ fetch:
+ src: '{{ remote_tmp_dir }}\abc[].txt'
+ dest: '{{ host_output_dir }}/'
+ register: fetch_wildcard_file_nofail
+
+- name: assert fetch file with wildcard characters
+ assert:
+ that:
+ - "fetch_wildcard_file_nofail is not failed"
diff --git a/test/integration/targets/win_script/files/test_script_with_args.ps1 b/test/integration/targets/win_script/files/test_script_with_args.ps1
index 01bb37f..669c641 100644
--- a/test/integration/targets/win_script/files/test_script_with_args.ps1
+++ b/test/integration/targets/win_script/files/test_script_with_args.ps1
@@ -2,5 +2,5 @@
# passed to the script.
foreach ($i in $args) {
- Write-Host $i;
+ Write-Host $i
}
diff --git a/test/integration/targets/win_script/files/test_script_with_errors.ps1 b/test/integration/targets/win_script/files/test_script_with_errors.ps1
index 56f9773..bdf7ee4 100644
--- a/test/integration/targets/win_script/files/test_script_with_errors.ps1
+++ b/test/integration/targets/win_script/files/test_script_with_errors.ps1
@@ -2,7 +2,7 @@
trap {
Write-Error -ErrorRecord $_
- exit 1;
+ exit 1
}
throw "Oh noes I has an error"
diff --git a/test/integration/targets/windows-minimal/library/win_ping_set_attr.ps1 b/test/integration/targets/windows-minimal/library/win_ping_set_attr.ps1
index f170496..d23bbc7 100644
--- a/test/integration/targets/windows-minimal/library/win_ping_set_attr.ps1
+++ b/test/integration/targets/windows-minimal/library/win_ping_set_attr.ps1
@@ -16,16 +16,16 @@
# POWERSHELL_COMMON
-$params = Parse-Args $args $true;
+$params = Parse-Args $args $true
-$data = Get-Attr $params "data" "pong";
+$data = Get-Attr $params "data" "pong"
$result = @{
changed = $false
ping = "pong"
-};
+}
# Test that Set-Attr will replace an existing attribute.
Set-Attr $result "ping" $data
-Exit-Json $result;
+Exit-Json $result
diff --git a/test/integration/targets/windows-minimal/library/win_ping_strict_mode_error.ps1 b/test/integration/targets/windows-minimal/library/win_ping_strict_mode_error.ps1
index 508174a..09400d0 100644
--- a/test/integration/targets/windows-minimal/library/win_ping_strict_mode_error.ps1
+++ b/test/integration/targets/windows-minimal/library/win_ping_strict_mode_error.ps1
@@ -16,15 +16,15 @@
# POWERSHELL_COMMON
-$params = Parse-Args $args $true;
+$params = Parse-Args $args $true
$params.thisPropertyDoesNotExist
-$data = Get-Attr $params "data" "pong";
+$data = Get-Attr $params "data" "pong"
$result = @{
changed = $false
ping = $data
-};
+}
-Exit-Json $result;
+Exit-Json $result
diff --git a/test/integration/targets/windows-minimal/library/win_ping_syntax_error.ps1 b/test/integration/targets/windows-minimal/library/win_ping_syntax_error.ps1
index d4c9f07..6932d53 100644
--- a/test/integration/targets/windows-minimal/library/win_ping_syntax_error.ps1
+++ b/test/integration/targets/windows-minimal/library/win_ping_syntax_error.ps1
@@ -18,13 +18,13 @@
$blah = 'I can't quote my strings correctly.'
-$params = Parse-Args $args $true;
+$params = Parse-Args $args $true
-$data = Get-Attr $params "data" "pong";
+$data = Get-Attr $params "data" "pong"
$result = @{
changed = $false
ping = $data
-};
+}
-Exit-Json $result;
+Exit-Json $result
diff --git a/test/integration/targets/windows-minimal/library/win_ping_throw.ps1 b/test/integration/targets/windows-minimal/library/win_ping_throw.ps1
index 7306f4d..2fba209 100644
--- a/test/integration/targets/windows-minimal/library/win_ping_throw.ps1
+++ b/test/integration/targets/windows-minimal/library/win_ping_throw.ps1
@@ -18,13 +18,13 @@
throw
-$params = Parse-Args $args $true;
+$params = Parse-Args $args $true
-$data = Get-Attr $params "data" "pong";
+$data = Get-Attr $params "data" "pong"
$result = @{
changed = $false
ping = $data
-};
+}
-Exit-Json $result;
+Exit-Json $result
diff --git a/test/integration/targets/windows-minimal/library/win_ping_throw_string.ps1 b/test/integration/targets/windows-minimal/library/win_ping_throw_string.ps1
index 09e3b7c..62de826 100644
--- a/test/integration/targets/windows-minimal/library/win_ping_throw_string.ps1
+++ b/test/integration/targets/windows-minimal/library/win_ping_throw_string.ps1
@@ -18,13 +18,13 @@
throw "no ping for you"
-$params = Parse-Args $args $true;
+$params = Parse-Args $args $true
-$data = Get-Attr $params "data" "pong";
+$data = Get-Attr $params "data" "pong"
$result = @{
changed = $false
ping = $data
-};
+}
-Exit-Json $result;
+Exit-Json $result
diff --git a/test/integration/targets/yum/aliases b/test/integration/targets/yum/aliases
index 1d49133..b12f354 100644
--- a/test/integration/targets/yum/aliases
+++ b/test/integration/targets/yum/aliases
@@ -1,5 +1,4 @@
destructive
shippable/posix/group1
skip/freebsd
-skip/osx
skip/macos
diff --git a/test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py b/test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py
index 27f38ce..306ccd9 100644
--- a/test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py
+++ b/test/integration/targets/yum/filter_plugins/filter_list_of_tuples_by_first_param.py
@@ -1,8 +1,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-from ansible.errors import AnsibleError, AnsibleFilterError
-
def filter_list_of_tuples_by_first_param(lst, search, startswith=False):
out = []