diff options
author | cos <cos> | 2024-04-20 09:15:12 +0200 |
---|---|---|
committer | cos <cos> | 2024-04-20 09:44:47 +0200 |
commit | bbed591285e1882d05198e518f75873af58939f5 (patch) | |
tree | c25b33652187f7070d0c2467663c11d6cd4e2326 /test/integration | |
parent | c4f015da4ac75017b97c24ef6601bdd98872e60f (diff) | |
download | debian-ansible-core-bbed591285e1882d05198e518f75873af58939f5.zip |
Attempt to recreate upstream branch state from tar filesupstream/failed-recreation-attempt
Unfortunately this was a too naive approach, and the result fails to
build.
Work around that version control is behind the actual package version in
trixie. As is obvious from the lacking commits in the salsa repository
and also visible on https://tracker.debian.org/pkg/ansible-core with the
report from vcswatch stating: VCS repository is not up to date.
This commit contains all changes from ansible-core_2.14.13.orig.tar.gz
to ansible-core_2.16.5.orig.tar.gz, which should hopefully be a squashed
representation on the same set of changes on the uploader's unpushed git
tree.
Diffstat (limited to 'test/integration')
275 files changed, 3107 insertions, 960 deletions
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 243a5e43..36f402fc 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 caec2ed6..dfc12710 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 d4569869..639d3c6b 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 243a5e43..36f402fc 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 cbb8f0fb..1870b8ea 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 79b7a704..aaaecb80 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 cc60945e..ebfea2af 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 02ec289f..e930d7d8 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/randommodule-text.output b/test/integration/targets/ansible-doc/randommodule-text.output index 602d66ec..ca361346 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 cf036000..f40202a8 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 f51fa8a4..b525766c 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-galaxy-collection-cli/files/expected.txt b/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt index 110009e3..69218290 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 8f0ada0b..140bf2a7 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 913a6f79..60c43cc7 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 dab599b1..f0e78ca0 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 f22f9844..91ed9124 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 dd307d72..520dbe5c 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 53c29f77..c1f5e1d7 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 f4a51c4b..423edd9e 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 8140d468..83e9acc9 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 b651a73e..a554c277 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 eb471f8e..d861cb4d 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 17a000db..46198fef 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 cca83c7b..92378266 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 74c99838..f3b9777c 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 b8d63492..1c93d54b 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 724c861e..e17d6aa1 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/publish.yml b/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml index 241eae60..1be16ae9 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 763c5a19..bff36892 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 893ea803..debd70bc 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 dfe3d0f7..0fe2f82d 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/templates/ansible.cfg.j2 b/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2 index 9bff527b..a242979d 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 175d6696..066d2678 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 cfd908c1..48766638 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 c70e8998..1c17daf7 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 b39df11c..5f88a557 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/files/testserver.py b/test/integration/targets/ansible-galaxy/files/testserver.py index 13598507..8cca6a83 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 7d966e29..fcd826c3 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/valid_sample.yml b/test/integration/targets/ansible-inventory/files/valid_sample.yml index 477f82f2..b8e7b882 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/tasks/main.yml b/test/integration/targets/ansible-inventory/tasks/main.yml index 84ac2c3c..c3459c12 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-pull/runme.sh b/test/integration/targets/ansible-pull/runme.sh index 582e8099..b591b283 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 13e7d785..f4caffd1 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 ab24bcad..fe7f9446 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-openshift/aliases b/test/integration/targets/ansible-test-cloud-openshift/aliases index 6e32db7b..b714e82c 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 c3b51904..6acb67dc 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-container/runme.py b/test/integration/targets/ansible-test-container/runme.py index 8ff48e0d..3c86b6dd 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 f59b9091..f662b97d 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 22b4236a..38860b03 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/runme.sh b/test/integration/targets/ansible-test-sanity-import/runme.sh index a12e3e3f..a49a71a0 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-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 c2575422..4ca20efb 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 bf1003fa..d158a987 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/ps_only/README.md b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md index bbdd5138..9c1c1c34 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 95f12f39..ca6e52a3 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 e0299969..5e2365ab 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 d8138d3b..67b8a83b 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 fee22ad8..76ead137 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 580f9d87..16e0bc88 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 dbb479a7..5cdd0966 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 e79613bb..8780e356 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/random_directory/bad.py b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/random_directory/bad.py index 2e35cf85..e34d1c37 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 82215438..a5d896f7 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 e1b3f4ca..dcbe827c 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 233db741..92584958 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/venv-pythons.py b/test/integration/targets/ansible-test/venv-pythons.py index b380f147..97998bcd 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 71dbacc0..2365d47d 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 50720ea9..98399eca 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 7f8ed115..c21d49a6 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 b495211d..6a16ec86 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 8278ec8b..c7f2050a 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 09af947f..a0e4e8d7 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 e9e72a9f..d6780214 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 5f892f9c..20c87093 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 d273eda7..a0bc1992 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 d4cce78a..b1d08afa 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_key/aliases b/test/integration/targets/apt_key/aliases index a820ec90..97f534a8 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 ffb89b22..7aee56a7 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 34e2b540..b4fe8dba 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 9c15e647..2ddf4140 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 726de111..62960ccd 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 b6d6d110..2d86d77b 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 4a2ce64b..c05824d7 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/main.yml b/test/integration/targets/blockinfile/tasks/main.yml index 054e5549..f26cb165 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/blocks/unsafe_failed_task.yml b/test/integration/targets/blocks/unsafe_failed_task.yml index adfa492a..e74327b9 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 71a4ef9e..ed455756 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 7a99cc74..3a121a5f 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 a5777506..ebf1c5b5 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 f926d144..ce9ecbf4 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 fc19a99d..77f80502 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 b945eb68..6f3a19d7 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 59cb3c5e..6f2320d3 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 31ffd17c..de5c2e58 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 ae6941f3..92696481 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 23cce104..f1242e14 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 d21856e7..e54ecb3f 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/tasks/main.yml b/test/integration/targets/command_shell/tasks/main.yml index 1f4aa5d7..2cc365db 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 455818c9..56ec8438 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 6c965663..0ce76011 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 a3f34ab7..cd17c090 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 dabbf407..08832b14 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 78f92a49..b086a3ad 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 b86c56ac..601312fa 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 d6c8e63c..40ea9de3 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 f2f9ac9d..f3703f85 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/debconf/tasks/main.yml b/test/integration/targets/debconf/tasks/main.yml index d3d63cdf..f9236268 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 c9be4ff2..b44f83bd 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 1bdf27cf..e0dcc746 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 dcfa9d03..eb601e02 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/dnf/aliases b/test/integration/targets/dnf/aliases index d6f27b8e..b12f3547 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 ec1c36f8..9845f3db 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 65b77ceb..4941e2c3 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 503cb4c3..f54c0a83 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 0d70cf78..5e161dbb 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/dpkg_selections/aliases b/test/integration/targets/dpkg_selections/aliases index c0d5684b..9c44d752 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 080db262..016d7716 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 c0c5ccd5..28227fce 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 43f9c74e..f295cf3c 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 59a81a1b..d322fe0d 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 7bf18c5e..2aef5957 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 100ce23a..c9e1dc55 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 8a6b5b7b..d0bf9bdc 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 b0e1af3e..2416c2ca 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 a5bd68d7..c1b4c791 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 2d084191..9d287a18 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 8bf25f77..1479f734 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 019f00e4..33fcae82 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 89c62b9b..9c4a960f 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/fork_safe_stdio/aliases b/test/integration/targets/fork_safe_stdio/aliases index e968db72..7761837e 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 4438c3fe..863582f3 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/file_utils.py b/test/integration/targets/gathering_facts/library/file_utils.py index 58538029..38fa9265 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/runme.sh b/test/integration/targets/gathering_facts/runme.sh index c1df560c..a90de0f0 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/main.yml b/test/integration/targets/get_url/tasks/main.yml index 09814c70..c26cc08b 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 e1852a81..234c904a 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 e0585ca3..20f1b4e9 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 47c37478..db35e048 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 8c8834a9..bd57ed1d 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 0e0cf684..409bbae2 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 ed06eab5..c990251f 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 136c5d5d..d8a2a818 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 3fd19067..338ae081 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 8fddd4b1..cb497c44 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 584a1693..4626f102 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 06511053..982c03ff 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 5cfb4d5b..ca8457ac 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 26fa7cf3..f1fe41d5 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 b38531f3..55c7c438 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/tasks/main.yml b/test/integration/targets/group/tasks/main.yml index eb8126dd..21235240 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 f9a81220..eb92cd1d 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/runme.sh b/test/integration/targets/handlers/runme.sh index 76fc99d8..368ca44d 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/include_vars/tasks/main.yml b/test/integration/targets/include_vars/tasks/main.yml index 6fc4e85a..97636d9d 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 96b05d6c..bcac7646 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 2c04fee5..cd82eca5 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 6831245c..d500f0df 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 a234a3dd..50dd2341 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 953459dc..c9b1e81a 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 7d3e625f..453fa96d 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 83ca468b..2ba1ae63 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 5ae7882f..d7bcf8eb 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 7bc19faa..c06d3feb 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 e619feaf..8622cf66 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 a32e8513..da6b914f 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 0b4c6312..f3c4964e 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 7ca445a3..43cad4fc 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 a0c99ade..a5de4211 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 81bf1475..919e1884 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 7d66ecf8..73df8aad 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 03551228..dae4103a 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 b1b56ac7..cd438430 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 dc00dedd..d5ffec4d 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_config/tasks/main.yml b/test/integration/targets/lookup_config/tasks/main.yml index 356d2f80..e5699d34 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 218ee58d..92d93d45 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 9aeaf1d1..ba248bd5 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_sequence/tasks/main.yml b/test/integration/targets/lookup_sequence/tasks/main.yml index bd0a4d80..e64801d3 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 71365a15..115c9e52 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 ef37fce1..19b7d98f 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 374b5fdf..6853708f 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 2fb227ad..83fd5db6 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 68dc8934..b90d05dc 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 09322a9d..bd892de9 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 fbffe309..ba60e649 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 480ea001..8c7ba884 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 2584fd3d..0c43201c 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 0d39f26d..174f3725 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 20284fd1..7ba24348 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 b0e1904b..67050fbd 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 9e84f636..afa1c9c3 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/tasks/main.yml b/test/integration/targets/module_no_log/tasks/main.yml index cf9e5802..bf024105 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 fb6c8a81..857d3d8e 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 efb3ddae..ab80ceae 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_test.yml b/test/integration/targets/module_utils/module_utils_test.yml index 4e948bd6..352bc582 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 6170f046..9644df93 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 d18c42d7..5cb1a72d 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 = # + + 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 = # + + 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/runme.sh b/test/integration/targets/no_log/runme.sh index bb5c048f..bf764bf9 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 37773831..163129e2 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 44b6cf93..23c7789b 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 8aad37a3..b7cd4831 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/vars.py b/test/integration/targets/old_style_vars_plugins/deprecation_warning/vars.py index d5c9a422..f554be04 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/runme.sh b/test/integration/targets/old_style_vars_plugins/runme.sh index 4cd19168..9f416235 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 b4000c97..5ba8a2df 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 c17525d8..37267aa6 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 5a5e4646..f5edf4b1 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/roles/test_good_parsing/tasks/main.yml b/test/integration/targets/parsing/roles/test_good_parsing/tasks/main.yml index d225c0f9..25e91f28 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 070888da..a1d8b7ce 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 022ce4cf..2d550082 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/testplay.yml b/test/integration/targets/path_lookups/testplay.yml index 8bf45532..bc05c7e5 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/test-pause.py b/test/integration/targets/pause/test-pause.py index 3703470d..ab771fa0 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 66992fd0..a3770702 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/pip.yml b/test/integration/targets/pip/tasks/pip.yml index 39480614..9f1034d2 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 9f1c5c0b..44412f22 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 694ebfcb..5f183e9f 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 6cffa676..bef7d6d8 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 08e56f24..8604716e 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 c1ce42ef..132bf03f 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/runme.sh b/test/integration/targets/plugin_filtering/runme.sh index aa0e2b0c..03d78abc 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/override/filters.yml b/test/integration/targets/plugin_loader/override/filters.yml index e51ab4e9..569a4479 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 e30f6241..e68f06ad 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/rel_plugin_loading/subdir/inventory_plugins/notyaml.py b/test/integration/targets/rel_plugin_loading/subdir/inventory_plugins/notyaml.py index e542913d..41a76d9b 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 5adef626..2d0db4e8 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 d267b783..ca8b4ec1 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/roles/runme.sh b/test/integration/targets/roles/runme.sh index bb98a932..bf3aaf58 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_arg_spec/roles/c/meta/main.yml b/test/integration/targets/roles_arg_spec/roles/c/meta/main.yml index 1a1ccbe4..10dce6d2 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 5eca7c73..b88d2e18 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 89ed2361..204b42ac 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 74189f81..668ec48e 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 f2f9ac9d..f3703f85 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 522493fc..6292272e 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 17d3eb75..32e10b03 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 471fb2a2..3e640f69 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 f16d9b53..8c0b28bf 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/uninstall-Alpine-3-python-3.yml b/test/integration/targets/setup_paramiko/uninstall-Alpine-3-python-3.yml index e9dcc62c..edb504ff 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_rpm_repo/tasks/main.yml b/test/integration/targets/setup_rpm_repo/tasks/main.yml index be20078f..bf5af101 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 cbb6aea3..a2734f97 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/subversion/aliases b/test/integration/targets/subversion/aliases index 3cc41e4c..03b96434 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/systemd/tasks/test_indirect_service.yml b/test/integration/targets/systemd/tasks/test_indirect_service.yml index fc11343e..0df60486 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 613410f0..2dd0affb 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 9da0b301..7dcb9985 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/tasks/playbook.yml b/test/integration/targets/tasks/playbook.yml index 80d9f8b1..10bd8591 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 594447bd..57cbf28a 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/runme.sh b/test/integration/targets/template/runme.sh index 30163af7..d3913d97 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 3c91734b..34e88287 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/unsafe.yml b/test/integration/targets/template/unsafe.yml index bef9a4b4..6f163881 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 8a1cabd2..c3f9ab69 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 312e171d..edbf012e 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 8c2decbd..ac06d67e 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/unarchive/tasks/main.yml b/test/integration/targets/unarchive/tasks/main.yml index 148e583f..b07c2fe7 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 9eec658e..32c84f4b 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 06fbc7b8..efd428eb 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/unsafe_writes/aliases b/test/integration/targets/unsafe_writes/aliases index da1b554e..3560af2f 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 2b2ac94e..42ce9c8f 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 a0f97b4b..8f0adc75 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 df4585e3..054a14df 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 9ba09ece..ddae83a0 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 0d1b2b34..060950d2 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 6cdafdb2..73e87960 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 5a9b97e6..cb8aeea2 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 da745b89..521f8ebf 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 9d36bfca..be4c4d6f 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 bced7905..644dbebb 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 1b529f76..5561a2f5 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_local.yml b/test/integration/targets/user/tasks/test_local.yml index 67c24a21..217d4769 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 4b328f71..2acd1e12 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 f2b2e54a..ef2a06e1 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 fc31688b..b03c87b8 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 58584bfb..bba661db 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/wait_for/tasks/main.yml b/test/integration/targets/wait_for/tasks/main.yml index f81fd0f2..74b8e9aa 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/tasks/main.yml b/test/integration/targets/win_exec_wrapper/tasks/main.yml index 8fc54f7c..f1342c48 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 b5818352..16a28761 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 01bb37f5..669c6410 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 56f97735..bdf7ee48 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 f1704964..d23bbc74 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 508174af..09400d08 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 d4c9f07a..6932d538 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 7306f4d2..2fba2092 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 09e3b7cb..62de8263 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 1d491339..b12f3547 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 27f38ce5..306ccd9a 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 = [] |