diff options
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 = [] |