diff options
author | Lee Garrett <lgarrett@rocketjump.eu> | 2021-09-29 20:41:09 +0200 |
---|---|---|
committer | Lee Garrett <lgarrett@rocketjump.eu> | 2021-09-29 20:41:09 +0200 |
commit | 64aab4bd2d3ded02da538beb94a4a7fbf7781699 (patch) | |
tree | ba8539c275acd0c9cf9d9472e46e08e7768bce60 /test/integration | |
parent | ddff31825e92941ec9bfa2aa3ef36e98a4b3b4a7 (diff) | |
download | debian-ansible-core-64aab4bd2d3ded02da538beb94a4a7fbf7781699.zip |
New upstream version 2.11.5
Diffstat (limited to 'test/integration')
664 files changed, 11005 insertions, 8035 deletions
diff --git a/test/integration/targets/adhoc/aliases b/test/integration/targets/adhoc/aliases new file mode 100644 index 00000000..765b70da --- /dev/null +++ b/test/integration/targets/adhoc/aliases @@ -0,0 +1 @@ +shippable/posix/group2 diff --git a/test/integration/targets/adhoc/runme.sh b/test/integration/targets/adhoc/runme.sh new file mode 100755 index 00000000..4ef076eb --- /dev/null +++ b/test/integration/targets/adhoc/runme.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux + +# run type tests +ansible -a 'sleep 20' --task-timeout 5 localhost |grep 'The command action failed to execute in the expected time frame (5) and was terminated' diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/cache/notjsonfile.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/cache/notjsonfile.py index ee56f6ee..ea4a7229 100644 --- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/cache/notjsonfile.py +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/cache/notjsonfile.py @@ -11,6 +11,7 @@ DOCUMENTATION = ''' description: - This cache uses JSON formatted, per host, files saved to the filesystem. author: Ansible Core (@ansible-core) + version_added: 0.7.0 options: _uri: required: True @@ -18,16 +19,26 @@ DOCUMENTATION = ''' - Path in which the cache plugin will save the JSON files env: - name: ANSIBLE_CACHE_PLUGIN_CONNECTION + version_added: 1.2.0 ini: - key: fact_caching_connection section: defaults + deprecated: + alternative: none + why: Test deprecation + version: '2.0.0' _prefix: description: User defined prefix to use when creating the JSON files env: - name: ANSIBLE_CACHE_PLUGIN_PREFIX + version_added: 1.1.0 ini: - key: fact_caching_prefix section: defaults + deprecated: + alternative: none + why: Another test deprecation + removed_at_date: '2050-01-01' _timeout: default: 86400 description: Expiration timeout for the cache plugin data @@ -36,7 +47,16 @@ DOCUMENTATION = ''' ini: - key: fact_caching_timeout section: defaults + vars: + - name: notsjonfile_fact_caching_timeout + version_added: 1.5.0 + deprecated: + alternative: do not use a variable + why: Test deprecation + version: '3.0.0' type: integer + extends_documentation_fragment: + - testns.testcol2.plugin ''' from ansible.plugins.cache import BaseFileCacheModule diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py index daecac5d..9eee46ed 100644 --- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/lookup/noop.py @@ -12,6 +12,12 @@ DOCUMENTATION = """ short_description: returns input description: - this is a noop + deprecated: + alternative: Use some other lookup + why: Test deprecation + removed_in: '3.0.0' + extends_documentation_fragment: + - testns.testcol2.version_added """ EXAMPLES = """ @@ -22,6 +28,7 @@ EXAMPLES = """ RETURN = """ _list: description: input given + version_added: 1.0.0 """ from ansible.module_utils.common._collections_compat import Sequence diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/fakemodule.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/fakemodule.py index decdbef4..6d18c087 100644 --- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/fakemodule.py +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/fakemodule.py @@ -5,9 +5,10 @@ __metaclass__ = type DOCUMENTATION = """ module: fakemodule - short_desciptoin: fake module + short_desciption: fake module description: - this is a fake module + version_added: 1.0.0 options: _notreal: description: really not a real option 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 new file mode 100644 index 00000000..f251a69f --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py @@ -0,0 +1,95 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: randommodule +short_description: A random module +description: + - A random module. +author: + - Ansible Core Team +version_added: 1.0.0 +deprecated: + alternative: Use some other module + why: Test deprecation + removed_in: '3.0.0' +options: + test: + description: Some text. + type: str + version_added: 1.2.0 + sub: + description: Suboptions. + type: dict + suboptions: + subtest: + description: A suboption. + 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. + type: float + version_added: 1.1.0 + # The following is not supported in modules, and should not get processed + # by add_collection_to_versions_and_dates() + env: + - name: TEST_ENV + version_added: 1.0.0 + deprecated: + alternative: none + why: Test deprecation + removed_in: '2.0.0' + version: '2.0.0' +extends_documentation_fragment: + - testns.testcol2.module +''' + +EXAMPLES = ''' +''' + +RETURN = ''' +z_last: + description: A last result. + type: str + returned: success + version_added: 1.3.0 + +m_middle: + description: + - This should be in the middle. + - Has some more data + type: dict + returned: success and 1st of month + contains: + suboption: + description: A suboption. + type: str + choices: [ARF, BARN, c_without_capital_first_letter] + version_added: 1.4.0 + +a_first: + description: A first result. + type: str + returned: success +''' + + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict(), + ) + + module.exit_json() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/vars/noop_vars_plugin.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/vars/noop_vars_plugin.py index ccb33b04..94e7feb0 100644 --- a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/vars/noop_vars_plugin.py +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/plugins/vars/noop_vars_plugin.py @@ -15,6 +15,8 @@ DOCUMENTATION = ''' section: testns.testcol.noop_vars_plugin env: - name: ANSIBLE_VARS_PLUGIN_STAGE + extends_documentation_fragment: + - testns.testcol2.deprecation ''' from ansible.plugins.vars import BaseVarsPlugin diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/roles/testrole/meta/main.yml b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/roles/testrole/meta/main.yml new file mode 100644 index 00000000..bc6af698 --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/roles/testrole/meta/main.yml @@ -0,0 +1,26 @@ +--- +dependencies: +galaxy_info: + +argument_specs: + main: + short_description: testns.testcol.testrole short description for main entry point + description: + - Longer description for testns.testcol.testrole main entry point. + author: Ansible Core (@ansible) + options: + opt1: + description: opt1 description + type: "str" + required: true + + alternate: + short_description: testns.testcol.testrole short description for alternate entry point + description: + - Longer description for testns.testcol.testrole alternate entry point. + author: Ansible Core (@ansible) + options: + altopt1: + description: altopt1 description + type: "int" + required: true diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/roles/testrole_with_no_argspecs/meta/empty b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/roles/testrole_with_no_argspecs/meta/empty new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol/roles/testrole_with_no_argspecs/meta/empty 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 new file mode 100644 index 00000000..02ec289f --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/MANIFEST.json @@ -0,0 +1,30 @@ +{ + "collection_info": { + "description": null, + "repository": "", + "tags": [], + "dependencies": {}, + "authors": [ + "Ansible (https://ansible.com)" + ], + "issues": "", + "name": "testcol2", + "license": [ + "GPL-3.0-or-later" + ], + "documentation": "", + "namespace": "testns", + "version": "1.2.0", + "readme": "README.md", + "license_file": "COPYING", + "homepage": "", + }, + "file_manifest_file": { + "format": 1, + "ftype": "file", + "chksum_sha256": "4c15a867ceba8ba1eaf2f4a58844bb5dbb82fec00645fc7eb74a3d31964900f6", + "name": "FILES.json", + "chksum_type": "sha256" + }, + "format": 1 +} diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/deprecation.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/deprecation.py new file mode 100644 index 00000000..3942d722 --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/deprecation.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + DOCUMENTATION = r''' +options: {} +deprecated: + alternative: Use some other module + why: Test deprecation + removed_in: '3.0.0' +''' diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/module.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/module.py new file mode 100644 index 00000000..a5723632 --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/module.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + DOCUMENTATION = r''' +options: + testcol2option: + description: + - An option taken from testcol2 + type: str + version_added: 1.0.0 + testcol2option2: + description: + - Another option taken from testcol2 + type: str +''' diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/plugin.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/plugin.py new file mode 100644 index 00000000..2fe4e4a6 --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/plugin.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + DOCUMENTATION = r''' +options: + testcol2option: + description: + - A plugin option taken from testcol2 + type: str + version_added: 1.0.0 + ini: + - key: foo + section: bar + version_added: 1.1.0 + deprecated: + alternative: none + why: Test deprecation + version: '3.0.0' + env: + - name: FOO_BAR + version_added: 1.2.0 + deprecated: + alternative: none + why: Test deprecation + removed_at_date: 2020-01-31 + vars: + - name: foobar + version_added: 1.3.0 + deprecated: + alternative: none + why: Test deprecation + removed_at_date: 2040-12-31 + testcol2depr: + description: + - A plugin option taken from testcol2 that is deprecated + type: str + deprecated: + alternative: none + why: Test option deprecation + version: '2.0.0' +''' diff --git a/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/version_added.py b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/version_added.py new file mode 100644 index 00000000..73e5f2f8 --- /dev/null +++ b/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol2/plugins/doc_fragments/version_added.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + DOCUMENTATION = r''' +options: {} +version_added: 1.0.0 +''' diff --git a/test/integration/targets/ansible-doc/fakecollrole.output b/test/integration/targets/ansible-doc/fakecollrole.output new file mode 100644 index 00000000..fdd6a2dd --- /dev/null +++ b/test/integration/targets/ansible-doc/fakecollrole.output @@ -0,0 +1,16 @@ +> TESTNS.TESTCOL.TESTROLE (/ansible/test/integration/targets/ansible-doc/collections/ansible_collections/testns/testcol) + +ENTRY POINT: alternate - testns.testcol.testrole short description for alternate entry point + + Longer description for testns.testcol.testrole alternate entry + point. + +OPTIONS (= is mandatory): + += altopt1 + altopt1 description + + type: int + + +AUTHOR: Ansible Core (@ansible) diff --git a/test/integration/targets/ansible-doc/fakemodule.output b/test/integration/targets/ansible-doc/fakemodule.output index adc27e08..01070fd5 100644 --- a/test/integration/targets/ansible-doc/fakemodule.output +++ b/test/integration/targets/ansible-doc/fakemodule.output @@ -11,5 +11,6 @@ OPTIONS (= is mandatory): AUTHOR: me -SHORT_DESCIPTOIN: fake module +SHORT_DESCIPTION: fake module +VERSION_ADDED_COLLECTION: testns.testcol diff --git a/test/integration/targets/ansible-doc/fakerole.output b/test/integration/targets/ansible-doc/fakerole.output new file mode 100644 index 00000000..81db9a63 --- /dev/null +++ b/test/integration/targets/ansible-doc/fakerole.output @@ -0,0 +1,32 @@ +> TEST_ROLE1 (/ansible/test/integration/targets/ansible-doc/roles/normal_role1) + +ENTRY POINT: main - test_role1 from roles subdir + + In to am attended desirous raptures *declared* diverted + confined at. Collected instantly remaining up certainly to + `necessary' as. Over walk dull into son boy door went new. At + or happiness commanded daughters as. Is `handsome' an declared + at received in extended vicinity subjects. Into miss on he + over been late pain an. Only week bore boy what fat case left + use. Match round scale now style far times. Your me past an + much. + +OPTIONS (= is mandatory): + += myopt1 + First option. + + type: str + +- myopt2 + Second option + [Default: 8000] + type: int + +- myopt3 + Third option. + (Choices: choice1, choice2)[Default: (null)] + type: str + + +AUTHOR: John Doe (@john), Jane Doe (@jane) diff --git a/test/integration/targets/ansible-doc/library/test_docs_yaml_anchors.py b/test/integration/targets/ansible-doc/library/test_docs_yaml_anchors.py new file mode 100644 index 00000000..bec02922 --- /dev/null +++ b/test/integration/targets/ansible-doc/library/test_docs_yaml_anchors.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: test_docs_yaml_anchors +short_description: Test module with YAML anchors in docs +description: + - Test module +author: + - Ansible Core Team +options: + at_the_top: &toplevel_anchor + description: + - Short desc + default: some string + type: str + + last_one: *toplevel_anchor + + egress: + description: + - Egress firewall rules + type: list + elements: dict + suboptions: &sub_anchor + port: + description: + - Rule port + type: int + required: true + + ingress: + description: + - Ingress firewall rules + type: list + elements: dict + suboptions: *sub_anchor +''' + +EXAMPLES = ''' +''' + +RETURN = ''' +''' + + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + at_the_top=dict(type='str', default='some string'), + last_one=dict(type='str', default='some string'), + egress=dict(type='list', elements='dict', options=dict( + port=dict(type='int', required=True), + )), + ingress=dict(type='list', elements='dict', options=dict( + port=dict(type='int', required=True), + )), + ), + ) + + module.exit_json() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/ansible-doc/noop.output b/test/integration/targets/ansible-doc/noop.output new file mode 100644 index 00000000..567150ad --- /dev/null +++ b/test/integration/targets/ansible-doc/noop.output @@ -0,0 +1,32 @@ +{ + "testns.testcol.noop": { + "doc": { + "author": "Ansible core team", + "collection": "testns.testcol", + "deprecated": { + "alternative": "Use some other lookup", + "removed_from_collection": "testns.testcol", + "removed_in": "3.0.0", + "why": "Test deprecation" + }, + "description": [ + "this is a noop" + ], + "filename": "./collections/ansible_collections/testns/testcol/plugins/lookup/noop.py", + "lookup": "noop", + "options": {}, + "short_description": "returns input", + "version_added": "1.0.0", + "version_added_collection": "testns.testcol2" + }, + "examples": "\n- name: do nothing\n debug: msg=\"{{ lookup('testns.testcol.noop', [1,2,3,4] }}\"\n", + "metadata": null, + "return": { + "_list": { + "description": "input given", + "version_added": "1.0.0", + "version_added_collection": "testns.testcol" + } + } + } +} diff --git a/test/integration/targets/ansible-doc/noop_vars_plugin.output b/test/integration/targets/ansible-doc/noop_vars_plugin.output new file mode 100644 index 00000000..5c42af35 --- /dev/null +++ b/test/integration/targets/ansible-doc/noop_vars_plugin.output @@ -0,0 +1,42 @@ +{ + "testns.testcol.noop_vars_plugin": { + "doc": { + "collection": "testns.testcol", + "deprecated": { + "alternative": "Use some other module", + "removed_from_collection": "testns.testcol2", + "removed_in": "3.0.0", + "why": "Test deprecation" + }, + "description": "don't test loading host and group vars from a collection", + "filename": "./collections/ansible_collections/testns/testcol/plugins/vars/noop_vars_plugin.py", + "options": { + "stage": { + "choices": [ + "all", + "inventory", + "task" + ], + "default": "all", + "env": [ + { + "name": "ANSIBLE_VARS_PLUGIN_STAGE" + } + ], + "ini": [ + { + "key": "stage", + "section": "testns.testcol.noop_vars_plugin" + } + ], + "type": "str" + } + }, + "short_description": "Do NOT load host and group vars", + "vars": "noop_vars_plugin" + }, + "examples": null, + "metadata": null, + "return": null + } +} diff --git a/test/integration/targets/ansible-doc/notjsonfile.output b/test/integration/targets/ansible-doc/notjsonfile.output new file mode 100644 index 00000000..a73b1a98 --- /dev/null +++ b/test/integration/targets/ansible-doc/notjsonfile.output @@ -0,0 +1,157 @@ +{ + "testns.testcol.notjsonfile": { + "doc": { + "author": "Ansible Core (@ansible-core)", + "cache": "notjsonfile", + "collection": "testns.testcol", + "description": [ + "This cache uses JSON formatted, per host, files saved to the filesystem." + ], + "filename": "./collections/ansible_collections/testns/testcol/plugins/cache/notjsonfile.py", + "options": { + "_prefix": { + "deprecated": { + "alternative": "none", + "collection_name": "testns.testcol", + "removed_at_date": "2050-01-01", + "why": "Another test deprecation" + }, + "description": "User defined prefix to use when creating the JSON files", + "env": [ + { + "name": "ANSIBLE_CACHE_PLUGIN_PREFIX", + "version_added": "1.1.0", + "version_added_collection": "testns.testcol" + } + ], + "ini": [ + { + "key": "fact_caching_prefix", + "section": "defaults" + } + ] + }, + "_timeout": { + "default": 86400, + "description": "Expiration timeout for the cache plugin data", + "env": [ + { + "name": "ANSIBLE_CACHE_PLUGIN_TIMEOUT" + } + ], + "ini": [ + { + "key": "fact_caching_timeout", + "section": "defaults" + } + ], + "type": "integer", + "vars": [ + { + "deprecated": { + "alternative": "do not use a variable", + "collection_name": "testns.testcol", + "version": "3.0.0", + "why": "Test deprecation" + }, + "name": "notsjonfile_fact_caching_timeout", + "version_added": "1.5.0", + "version_added_collection": "testns.testcol" + } + ] + }, + "_uri": { + "description": [ + "Path in which the cache plugin will save the JSON files" + ], + "env": [ + { + "name": "ANSIBLE_CACHE_PLUGIN_CONNECTION", + "version_added": "1.2.0", + "version_added_collection": "testns.testcol" + } + ], + "ini": [ + { + "deprecated": { + "alternative": "none", + "collection_name": "testns.testcol", + "version": "2.0.0", + "why": "Test deprecation" + }, + "key": "fact_caching_connection", + "section": "defaults" + } + ], + "required": true + }, + "testcol2depr": { + "deprecated": { + "alternative": "none", + "collection_name": "testns.testcol2", + "version": "2.0.0", + "why": "Test option deprecation" + }, + "description": [ + "A plugin option taken from testcol2 that is deprecated" + ], + "type": "str" + }, + "testcol2option": { + "description": [ + "A plugin option taken from testcol2" + ], + "env": [ + { + "deprecated": { + "alternative": "none", + "collection_name": "testns.testcol2", + "removed_at_date": "2020-01-31", + "why": "Test deprecation" + }, + "name": "FOO_BAR", + "version_added": "1.2.0", + "version_added_collection": "testns.testcol2" + } + ], + "ini": [ + { + "deprecated": { + "alternative": "none", + "collection_name": "testns.testcol2", + "version": "3.0.0", + "why": "Test deprecation" + }, + "key": "foo", + "section": "bar", + "version_added": "1.1.0", + "version_added_collection": "testns.testcol2" + } + ], + "type": "str", + "vars": [ + { + "deprecated": { + "alternative": "none", + "collection_name": "testns.testcol2", + "removed_at_date": "2040-12-31", + "why": "Test deprecation" + }, + "name": "foobar", + "version_added": "1.3.0", + "version_added_collection": "testns.testcol2" + } + ], + "version_added": "1.0.0", + "version_added_collection": "testns.testcol2" + } + }, + "short_description": "JSON formatted files.", + "version_added": "0.7.0", + "version_added_collection": "testns.testcol" + }, + "examples": null, + "metadata": null, + "return": null + } +} diff --git a/test/integration/targets/ansible-doc/randommodule.output b/test/integration/targets/ansible-doc/randommodule.output new file mode 100644 index 00000000..25f46c36 --- /dev/null +++ b/test/integration/targets/ansible-doc/randommodule.output @@ -0,0 +1,115 @@ +{ + "testns.testcol.randommodule": { + "doc": { + "author": [ + "Ansible Core Team" + ], + "collection": "testns.testcol", + "deprecated": { + "alternative": "Use some other module", + "removed_from_collection": "testns.testcol", + "removed_in": "3.0.0", + "why": "Test deprecation" + }, + "description": [ + "A random module." + ], + "filename": "./collections/ansible_collections/testns/testcol/plugins/modules/randommodule.py", + "has_action": false, + "module": "randommodule", + "options": { + "sub": { + "description": "Suboptions.", + "env": [ + { + "deprecated": { + "alternative": "none", + "removed_in": "2.0.0", + "version": "2.0.0", + "why": "Test deprecation" + }, + "name": "TEST_ENV", + "version_added": "1.0.0" + } + ], + "options": { + "subtest2": { + "description": "Another suboption.", + "type": "float", + "version_added": "1.1.0" + } + }, + "suboptions": { + "subtest": { + "description": "A suboption.", + "type": "int", + "version_added": "1.1.0", + "version_added_collection": "testns.testcol" + } + }, + "type": "dict" + }, + "test": { + "description": "Some text.", + "type": "str", + "version_added": "1.2.0", + "version_added_collection": "testns.testcol" + }, + "testcol2option": { + "description": [ + "An option taken from testcol2" + ], + "type": "str", + "version_added": "1.0.0", + "version_added_collection": "testns.testcol2" + }, + "testcol2option2": { + "description": [ + "Another option taken from testcol2" + ], + "type": "str" + } + }, + "short_description": "A random module", + "version_added": "1.0.0", + "version_added_collection": "testns.testcol" + }, + "examples": "\n", + "metadata": null, + "return": { + "a_first": { + "description": "A first result.", + "returned": "success", + "type": "str" + }, + "m_middle": { + "contains": { + "suboption": { + "choices": [ + "ARF", + "BARN", + "c_without_capital_first_letter" + ], + "description": "A suboption.", + "type": "str", + "version_added": "1.4.0", + "version_added_collection": "testns.testcol" + } + }, + "description": [ + "This should be in the middle.", + "Has some more data" + ], + "returned": "success and 1st of month", + "type": "dict" + }, + "z_last": { + "description": "A last result.", + "returned": "success", + "type": "str", + "version_added": "1.3.0", + "version_added_collection": "testns.testcol" + } + } + } +} diff --git a/test/integration/targets/ansible-doc/roles/test_role1/meta/argument_specs.yml b/test/integration/targets/ansible-doc/roles/test_role1/meta/argument_specs.yml new file mode 100644 index 00000000..0315a1fd --- /dev/null +++ b/test/integration/targets/ansible-doc/roles/test_role1/meta/argument_specs.yml @@ -0,0 +1,34 @@ +--- +argument_specs: + main: + short_description: test_role1 from roles subdir + description: + - In to am attended desirous raptures B(declared) diverted confined at. Collected instantly remaining + up certainly to C(necessary) as. Over walk dull into son boy door went new. + - At or happiness commanded daughters as. Is I(handsome) an declared at received in extended vicinity + subjects. Into miss on he over been late pain an. Only week bore boy what fat case left use. Match round + scale now style far times. Your me past an much. + author: + - John Doe (@john) + - Jane Doe (@jane) + + options: + myopt1: + description: + - First option. + type: "str" + required: true + + myopt2: + description: + - Second option + type: "int" + default: 8000 + + myopt3: + description: + - Third option. + type: "str" + choices: + - choice1 + - choice2 diff --git a/test/integration/targets/ansible-doc/roles/test_role1/meta/main.yml b/test/integration/targets/ansible-doc/roles/test_role1/meta/main.yml new file mode 100644 index 00000000..19f6162b --- /dev/null +++ b/test/integration/targets/ansible-doc/roles/test_role1/meta/main.yml @@ -0,0 +1,13 @@ +# This meta/main.yml exists to test that it is NOT read, with preference being +# given to the meta/argument_specs.yml file. This spec contains additional +# entry points (groot, foo) that the argument_specs.yml does not. If this file +# were read, the additional entrypoints would show up in --list output, breaking +# tests. +--- +argument_specs: + main: + short_description: test_role1 from roles subdir + groot: + short_description: I am Groot + foo: + short_description: I am Foo diff --git a/test/integration/targets/ansible-doc/roles/test_role2/meta/empty b/test/integration/targets/ansible-doc/roles/test_role2/meta/empty new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/integration/targets/ansible-doc/roles/test_role2/meta/empty diff --git a/test/integration/targets/ansible-doc/roles/test_role3/meta/main.yml b/test/integration/targets/ansible-doc/roles/test_role3/meta/main.yml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/integration/targets/ansible-doc/roles/test_role3/meta/main.yml diff --git a/test/integration/targets/ansible-doc/runme.sh b/test/integration/targets/ansible-doc/runme.sh index b5929f60..4f40d7c3 100755 --- a/test/integration/targets/ansible-doc/runme.sh +++ b/test/integration/targets/ansible-doc/runme.sh @@ -3,13 +3,20 @@ set -eux 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' + +# collections testing ( unset ANSIBLE_PLAYBOOK_DIR cd "$(dirname "$0")" # test module docs from collection -current_out="$(ansible-doc --playbook-dir ./ testns.testcol.fakemodule)" -expected_out="$(cat fakemodule.output)" +# 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" # ensure we do work with valid collection name for list @@ -39,4 +46,51 @@ do justcol=$(ansible-doc -l -t ${ptype} --playbook-dir ./ testns|wc -l) test "$justcol" -eq 1 done + +#### test role functionality + +# Test 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" + +# 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 + +# Include normal roles (no collection filter) +output=$(ansible-doc -t role -l --playbook-dir . | wc -l) +test "$output" -eq 3 + +# 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") +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 +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" + ) + +#### test add_collection_to_versions_and_dates() + +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" + +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" + +current_out="$(ansible-doc --json --playbook-dir ./ -t lookup testns.testcol.noop | sed 's/ *$//' | sed 's/ *"filename": "[^"]*",$//')" +expected_out="$(sed 's/ *"filename": "[^"]*",$//' noop.output)" +test "$current_out" == "$expected_out" + +current_out="$(ansible-doc --json --playbook-dir ./ -t vars testns.testcol.noop_vars_plugin | sed 's/ *$//' | sed 's/ *"filename": "[^"]*",$//')" +expected_out="$(sed 's/ *"filename": "[^"]*",$//' noop_vars_plugin.output)" +test "$current_out" == "$expected_out" diff --git a/test/integration/targets/ansible-doc/test.yml b/test/integration/targets/ansible-doc/test.yml index a077a994..3c380b3d 100644 --- a/test/integration/targets/ansible-doc/test.yml +++ b/test/integration/targets/ansible-doc/test.yml @@ -136,3 +136,14 @@ - '"Reason: Updated module released with more functionality" in result.stdout' - '"Will be removed in a release after 2022-06-01" in result.stdout' - '"Alternatives: new_module" in result.stdout' + + - name: documented module with YAML anchors + command: ansible-doc test_docs_yaml_anchors + register: result + - set_fact: + actual_output: >- + {{ result.stdout | regex_replace('^(> [A-Z_]+ +\().+library/([a-z_]+.py)\)$', '\1library/\2)', multiline=true) }} + expected_output: "{{ lookup('file', 'test_docs_yaml_anchors.output') }}" + - assert: + that: + - actual_output == expected_output diff --git a/test/integration/targets/ansible-doc/test_docs_yaml_anchors.output b/test/integration/targets/ansible-doc/test_docs_yaml_anchors.output new file mode 100644 index 00000000..e7d721e4 --- /dev/null +++ b/test/integration/targets/ansible-doc/test_docs_yaml_anchors.output @@ -0,0 +1,49 @@ +> TEST_DOCS_YAML_ANCHORS (library/test_docs_yaml_anchors.py) + + Test module + +OPTIONS (= is mandatory): + +- at_the_top + Short desc + [Default: some string] + type: str + +- egress + Egress firewall rules + [Default: (null)] + elements: dict + type: list + + SUBOPTIONS: + + = port + Rule port + + type: int + +- ingress + Ingress firewall rules + [Default: (null)] + elements: dict + type: list + + SUBOPTIONS: + + = port + Rule port + + type: int + +- last_one + Short desc + [Default: some string] + type: str + + +AUTHOR: Ansible Core Team + +EXAMPLES: + + + diff --git a/test/integration/targets/ansible-doc/test_role1/README.txt b/test/integration/targets/ansible-doc/test_role1/README.txt new file mode 100644 index 00000000..98983c8c --- /dev/null +++ b/test/integration/targets/ansible-doc/test_role1/README.txt @@ -0,0 +1,3 @@ +Test role that exists in the playbook directory so we can validate +that a role of the same name that exists in the 'roles' subdirectory +will take precedence over this one. diff --git a/test/integration/targets/ansible-doc/test_role1/meta/main.yml b/test/integration/targets/ansible-doc/test_role1/meta/main.yml new file mode 100644 index 00000000..4f7abcc8 --- /dev/null +++ b/test/integration/targets/ansible-doc/test_role1/meta/main.yml @@ -0,0 +1,8 @@ +--- +dependencies: +galaxy_info: + +argument_specs: + main: + short_description: test_role1 from playbook dir + description: This should not appear in `ansible-doc --list` output. diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/download.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/download.yml index 672b849c..b1017e78 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/download.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/download.yml @@ -24,8 +24,8 @@ - assert: that: - - '"Downloading collection ''amazon.aws'' to" in download_collection.stdout' - - '"Downloading collection ''awx.awx'' to" in download_collection.stdout' + - '"Downloading collection ''amazon.aws:1.0.0'' to" in download_collection.stdout' + - '"Downloading collection ''awx.awx:0.0.1-devel'' to" in download_collection.stdout' - download_collection_amazon_actual.stat.exists - download_collection_awx_actual.stat.exists diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/empty_installed_collections.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/empty_installed_collections.yml index f21a6f6b..241107c3 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/empty_installed_collections.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/empty_installed_collections.yml @@ -1,7 +1,7 @@ - name: delete installed collections file: - state: "{{ item }}" - path: "{{ galaxy_dir }}/ansible_collections" + state: absent + path: "{{ item }}" loop: - - absent - - directory + - "{{ install_path }}" + - "{{ alt_install_path }}" diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/individual_collection_repo.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/individual_collection_repo.yml index 1b761f60..9ca7c8f0 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/individual_collection_repo.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/individual_collection_repo.yml @@ -1,12 +1,10 @@ - name: Clone a git repository git: repo: https://github.com/ansible-collections/amazon.aws.git - dest: '{{ galaxy_dir }}/development/amazon.aws/' + dest: '{{ scm_path }}/amazon.aws/' - name: install - command: 'ansible-galaxy collection install git+file://{{galaxy_dir }}/development/amazon.aws/.git' - args: - chdir: '{{ galaxy_dir }}/development' + command: 'ansible-galaxy collection install git+file://{{ scm_path }}/amazon.aws/.git' - name: list installed collections command: 'ansible-galaxy collection list' 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 7db7e1d6..759226d2 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/main.yml @@ -5,13 +5,14 @@ - name: Test installing collections from git repositories environment: - ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}' + ANSIBLE_COLLECTIONS_PATHS: "{{ galaxy_dir }}/collections" vars: cleanup: True galaxy_dir: "{{ galaxy_dir }}" block: - include_tasks: ./setup.yml + - include_tasks: ./requirements.yml - include_tasks: ./individual_collection_repo.yml - include_tasks: ./setup_multi_collection_repo.yml - include_tasks: ./multi_collection_repo_all.yml @@ -31,11 +32,23 @@ path: '{{ item }}' state: absent loop: - - '{{ galaxy_dir }}/ansible_collections' - - '{{ galaxy_dir }}/development' + - "{{ install_path }}" + - "{{ alt_install_path }}" + - "{{ scm_path }}" - name: remove git package: name: git state: absent when: git_install is changed + + # This gets dragged in as a dependency of git on FreeBSD. + # We need to remove it too when done. + - name: remove python37 if necessary + package: + name: python37 + state: absent + when: + - git_install is changed + - ansible_distribution == 'FreeBSD' + - ansible_python.version.major == 2 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 2992062a..f22f9844 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 @@ -1,5 +1,5 @@ - name: Install all collections by default - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/ansible_test/.git' + command: 'ansible-galaxy collection install git+file://{{ test_repo_path }}/.git' - name: list installed collections command: 'ansible-galaxy collection list' @@ -10,5 +10,36 @@ - "'ansible_test.collection_1' in installed_collections.stdout" - "'ansible_test.collection_2' in installed_collections.stdout" +- name: install from artifact to another path to compare contents + 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" + +- name: check if the files and folders in build_ignore were respected + stat: + path: "{{ install_path }}/ansible_test/collection_1/{{ item }}" + register: result + loop: + - foo.txt + - foobar/baz.txt + - foobar/qux + +- assert: + that: result.results | map(attribute='stat') | map(attribute='exists') is not any + +- name: check that directory with ignored files exists and is empty + stat: + path: "{{ install_path }}/ansible_test/collection_1/foobar" + register: result + +- assert: + that: result.stat.exists + +- name: test that there are no diff installing from a repo vs artifact + command: "diff -ur {{ collection_from_artifact }} {{ collection_from_repo }} -x *.json" + vars: + collection_from_repo: "{{ install_path }}/ansible_test/collection_1" + collection_from_artifact: "{{ alt_install_path }}/ansible_test/collection_1" + - include_tasks: ./empty_installed_collections.yml when: cleanup diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_individual.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_individual.yml index 48f6407a..a5a2bdfc 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_individual.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/multi_collection_repo_individual.yml @@ -1,5 +1,5 @@ - name: test installing one collection - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/ansible_test/.git#collection_2' + command: 'ansible-galaxy collection install git+file://{{ test_repo_path }}/.git#collection_2' - name: list installed collections command: 'ansible-galaxy collection list' diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/reinstalling.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/reinstalling.yml index c0f6c910..90a70e2f 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/reinstalling.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/reinstalling.yml @@ -1,31 +1,31 @@ - name: Rerun installing a collection with a dep - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/ansible_test/.git#/collection_1/' + command: 'ansible-galaxy collection install git+file://{{ test_repo_path }}/.git#/collection_1/' register: installed -- assert: +- name: SCM collections don't have a concrete artifact version so the collection should always be reinstalled + assert: that: - - "'Skipping' in installed.stdout" - - "'Created' not in installed.stdout" + - "'Created collection for ansible_test.collection_1' in installed.stdout" + - "'Created collection for ansible_test.collection_2' in installed.stdout" -- name: Only reinstall the collection - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/ansible_test/.git#/collection_1/ --force' +- name: The collection should also be reinstalled when --force flag is used + command: 'ansible-galaxy collection install git+file://{{ test_repo_path }}/.git#/collection_1/ --force' register: installed - assert: that: - "'Created collection for ansible_test.collection_1' in installed.stdout" - - "'Created collection for ansible_test.collection_2' not in installed.stdout" - - "'Skipping' in installed.stdout" + # The dependency is also an SCM collection, so it should also be reinstalled + - "'Created collection for ansible_test.collection_2' in installed.stdout" -- name: Reinstall the collection and dependency - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/ansible_test/.git#/collection_1/ --force-with-deps' +- name: The collection should also be reinstalled when --force-with-deps is used + command: 'ansible-galaxy collection install git+file://{{ test_repo_path }}/.git#/collection_1/ --force-with-deps' register: installed - assert: that: - "'Created collection for ansible_test.collection_1' in installed.stdout" - "'Created collection for ansible_test.collection_2' in installed.stdout" - - "'Skipping' not in installed.stdout" - include_tasks: ./empty_installed_collections.yml when: cleanup diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/requirements.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/requirements.yml new file mode 100644 index 00000000..235ed126 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/requirements.yml @@ -0,0 +1,103 @@ +- name: make a requirements directory + file: + state: directory + path: '{{ galaxy_dir }}/requirements' + +- name: populate requirement templates + template: + src: "{{ item }}" + dest: "{{ galaxy_dir }}/requirements/{{ item }}" + loop: + - source_only.yml + - source_and_name.yml + - source_and_name_and_type.yml + - name_without_type.yml + - git_prefix_name.yml + - name_and_type.yml + +- name: test source is not a git repo + command: 'ansible-galaxy collection install -r source_only.yml' + register: result + ignore_errors: true + args: + chdir: '{{ galaxy_dir }}/requirements' + +- assert: + that: + - result.failed + - >- + "ERROR! Neither the collection requirement entry key 'name', + nor 'source' point to a concrete resolvable collection artifact. + Also 'name' is not an FQCN. A valid collection name must be in + the format <namespace>.<collection>. Please make sure that the + namespace and the collection name contain characters from + [a-zA-Z0-9_] only." in result.stderr + +- name: test source is not a git repo even if name is provided + command: 'ansible-galaxy collection install -r source_and_name.yml' + register: result + ignore_errors: true + args: + chdir: '{{ galaxy_dir }}/requirements' + +- assert: + that: + - result.failed + - >- + result.stderr is search("ERROR! Collections requirement 'source' + entry should contain a valid Galaxy API URL but it does not: + git\+file:///.*/amazon.aws/.git is not an HTTP URL.") + +- name: test source is not a git repo even if name and type is provided + command: 'ansible-galaxy collection install -r source_and_name_and_type.yml' + register: result + ignore_errors: true + args: + chdir: '{{ galaxy_dir }}/requirements' + +- assert: + that: + - result.failed + - >- + result.stderr is search("ERROR! Failed to clone a Git repository + from `file:///.*/.git`.") + - >- + result.stderr is search("fatal: '/.*/amazon.aws/.git' does not + appear to be a git repository") + +- name: test using name as a git repo without git+ prefix + command: 'ansible-galaxy collection install -r name_without_type.yml' + register: result + ignore_errors: true + args: + chdir: '{{ galaxy_dir }}/requirements' + +- assert: + that: + - result.failed + - '"name must be in the format <namespace>.<collection>" in result.stderr' + +- name: Clone a git repository + git: + repo: https://github.com/ansible-collections/amazon.aws.git + dest: '{{ scm_path }}/amazon.aws/' + +- name: test using name as a git repo + command: 'ansible-galaxy collection install -r git_prefix_name.yml' + register: result + args: + chdir: '{{ galaxy_dir }}/requirements' + +- name: test using name plus type as a git repo + command: 'ansible-galaxy collection install -r name_and_type.yml --force' + register: result + args: + chdir: '{{ galaxy_dir }}/requirements' + +- name: remove the test repo and requirements dir + file: + path: '{{ item }}' + state: absent + loop: + - '{{ scm_path }}/amazon.aws/' + - '{{ galaxy_dir }}/requirements' diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency.yml index 5a23663e..5645aec6 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency.yml @@ -1,5 +1,20 @@ +- name: test installing one collection that has a SCM dep with --no-deps + command: 'ansible-galaxy collection install git+file://{{ test_repo_path }}/.git#/collection_1/ --no-deps' + +- name: list installed collections + command: 'ansible-galaxy collection list' + register: installed_collections + +- assert: + that: + - "'ansible_test.collection_1' in installed_collections.stdout" + - "'ansible_test.collection_2' not in installed_collections.stdout" + +- name: remove collections to test installing with the dependency + include_tasks: ./empty_installed_collections.yml + - name: test installing one collection that has a SCM dep - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/ansible_test/.git#/collection_1/' + command: 'ansible-galaxy collection install git+file://{{ test_repo_path }}/.git#/collection_1/' - name: list installed collections command: 'ansible-galaxy collection list' diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml index bc10f24c..f200be18 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/scm_dependency_deduplication.yml @@ -1,19 +1,38 @@ - name: Install all collections in a repo, one of which has a recursive dependency - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/namespace_1/.git' + command: 'ansible-galaxy collection install git+file://{{ scm_path }}/namespace_1/.git' register: command - assert: that: - - command.stdout_lines | length == 9 - - command.stdout_lines[0] == "Starting galaxy collection install process" - - command.stdout_lines[1] == "Process install dependency map" - - command.stdout_lines[2] == "Starting collection install process" - - "'namespace_1.collection_1' in command.stdout_lines[3]" - - "'namespace_1.collection_1' in command.stdout_lines[4]" - - "'namespace_1.collection_1' in command.stdout_lines[5]" - - "'namespace_2.collection_2' in command.stdout_lines[6]" - - "'namespace_2.collection_2' in command.stdout_lines[7]" - - "'namespace_2.collection_2' in command.stdout_lines[8]" + - command.stdout_lines | length == 12 + - >- + 'Starting galaxy collection install process' + in command.stdout_lines + - >- + 'Starting collection install process' + in command.stdout_lines + - >- + "Installing 'namespace_1.collection_1:1.0.0' to + '{{ install_path }}/namespace_1/collection_1'" + in command.stdout_lines + - >- + 'Created collection for namespace_1.collection_1:1.0.0 at + {{ install_path }}/namespace_1/collection_1' + in command.stdout_lines + - >- + 'namespace_1.collection_1:1.0.0 was installed successfully' + in command.stdout_lines + - >- + "Installing 'namespace_2.collection_2:1.0.0' to + '{{ install_path }}/namespace_2/collection_2'" + in command.stdout_lines + - >- + 'Created collection for namespace_2.collection_2:1.0.0 at + {{ install_path }}/namespace_2/collection_2' + in command.stdout_lines + - >- + 'namespace_2.collection_2:1.0.0 was installed successfully' + in command.stdout_lines - name: list installed collections command: 'ansible-galaxy collection list' @@ -25,21 +44,40 @@ - "'namespace_2.collection_2' in installed_collections.stdout" - name: Install a specific collection in a repo with a recursive dependency - command: 'ansible-galaxy collection install git+file://{{ galaxy_dir }}/development/namespace_1/.git#/collection_1/ --force-with-deps' + command: 'ansible-galaxy collection install git+file://{{ scm_path }}/namespace_1/.git#/collection_1/ --force-with-deps' register: command - assert: that: - - command.stdout_lines | length == 9 - - command.stdout_lines[0] == "Starting galaxy collection install process" - - command.stdout_lines[1] == "Process install dependency map" - - command.stdout_lines[2] == "Starting collection install process" - - "'namespace_1.collection_1' in command.stdout_lines[3]" - - "'namespace_1.collection_1' in command.stdout_lines[4]" - - "'namespace_1.collection_1' in command.stdout_lines[5]" - - "'namespace_2.collection_2' in command.stdout_lines[6]" - - "'namespace_2.collection_2' in command.stdout_lines[7]" - - "'namespace_2.collection_2' in command.stdout_lines[8]" + - command.stdout_lines | length == 12 + - >- + 'Starting galaxy collection install process' + in command.stdout_lines + - >- + 'Starting collection install process' + in command.stdout_lines + - >- + "Installing 'namespace_1.collection_1:1.0.0' to + '{{ install_path }}/namespace_1/collection_1'" + in command.stdout_lines + - >- + 'Created collection for namespace_1.collection_1:1.0.0 at + {{ install_path }}/namespace_1/collection_1' + in command.stdout_lines + - >- + 'namespace_1.collection_1:1.0.0 was installed successfully' + in command.stdout_lines + - >- + "Installing 'namespace_2.collection_2:1.0.0' to + '{{ install_path }}/namespace_2/collection_2'" + in command.stdout_lines + - >- + 'Created collection for namespace_2.collection_2:1.0.0 at + {{ install_path }}/namespace_2/collection_2' + in command.stdout_lines + - >- + 'namespace_2.collection_2:1.0.0 was installed successfully' + in command.stdout_lines - name: list installed collections command: 'ansible-galaxy collection list' diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup.yml index f4beb9d6..bb882c9d 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup.yml @@ -1,7 +1,7 @@ - name: ensure git is installed package: name: git - when: ansible_distribution != "MacOSX" + when: ansible_distribution not in ["MacOSX", "Alpine"] register: git_install - name: set git global user.email if not already set @@ -15,5 +15,5 @@ path: '{{ item }}' state: directory loop: - - '{{ galaxy_dir }}/ansible_collections' - - '{{ galaxy_dir }}/development/ansible_test' + - '{{ install_path }}' + - '{{ test_repo_path }}' diff --git a/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_multi_collection_repo.yml b/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_multi_collection_repo.yml index 4a662ca6..e733a845 100644 --- a/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_multi_collection_repo.yml +++ b/test/integration/targets/ansible-galaxy-collection-scm/tasks/setup_multi_collection_repo.yml @@ -1,27 +1,70 @@ - name: Initialize a git repo - command: 'git init {{ galaxy_dir }}/development/ansible_test' + command: 'git init {{ test_repo_path }}' - stat: - path: "{{ galaxy_dir }}/development/ansible_test" + path: "{{ test_repo_path }}" - name: Add a couple collections to the repository command: 'ansible-galaxy collection init {{ item }}' args: - chdir: '{{ galaxy_dir }}/development' + chdir: '{{ scm_path }}' loop: - 'ansible_test.collection_1' - 'ansible_test.collection_2' +- name: Preserve the (empty) docs directory for the SCM collection + file: + path: '{{ test_repo_path }}/{{ item }}/docs/README.md' + state: touch + loop: + - collection_1 + - collection_2 + +- name: Preserve the (empty) roles directory for the SCM collection + file: + path: '{{ test_repo_path }}/{{ item }}/roles/README.md' + state: touch + loop: + - collection_1 + - collection_2 + +- name: create extra files and folders to test build_ignore + file: + path: '{{ test_repo_path }}/collection_1/{{ item.name }}' + state: '{{ item.state }}' + loop: + - name: foo.txt + state: touch + - name: foobar + state: directory + - name: foobar/qux + state: directory + - name: foobar/qux/bar + state: touch + - name: foobar/baz.txt + state: touch + - name: Add collection_2 as a dependency of collection_1 lineinfile: - path: '{{ galaxy_dir }}/development/ansible_test/collection_1/galaxy.yml' + path: '{{ test_repo_path }}/collection_1/galaxy.yml' regexp: '^dependencies' - line: "dependencies: {'git+file://{{ galaxy_dir }}/development/ansible_test/.git#collection_2/': '*'}" + line: "dependencies: {'git+file://{{ test_repo_path }}/.git#collection_2/': '*'}" + +- name: Ignore a directory and files + lineinfile: + path: '{{ test_repo_path }}/collection_1/galaxy.yml' + regexp: '^build_ignore' + line: "build_ignore: ['foo.txt', 'foobar/*']" - name: Commit the changes command: '{{ item }}' args: - chdir: '{{ galaxy_dir }}/development/ansible_test' + chdir: '{{ test_repo_path }}' loop: - git add ./ - git commit -m 'add collections' + +- name: Build the actual artifact for ansible_test.collection_1 for comparison + command: "ansible-galaxy collection build ansible_test/collection_1 --output-path {{ galaxy_dir }}" + args: + chdir: "{{ scm_path }}" 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 df0af917..dd307d72 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 @@ -1,5 +1,5 @@ - name: Initialize git repositories - command: 'git init {{ galaxy_dir }}/development/{{ item }}' + command: 'git init {{ scm_path }}/{{ item }}' loop: - namespace_1 - namespace_2 @@ -7,27 +7,27 @@ - name: Add a couple collections to the repository command: 'ansible-galaxy collection init {{ item }}' args: - chdir: '{{ galaxy_dir }}/development' + chdir: '{{ scm_path }}' loop: - 'namespace_1.collection_1' - 'namespace_2.collection_2' - name: Add collection_2 as a dependency of collection_1 lineinfile: - path: '{{ galaxy_dir }}/development/namespace_1/collection_1/galaxy.yml' + path: '{{ scm_path }}/namespace_1/collection_1/galaxy.yml' regexp: '^dependencies' - line: "dependencies: {'git+file://{{ galaxy_dir }}/development/namespace_2/.git#collection_2/': '*'}" + line: "dependencies: {'git+file://{{ scm_path }}/namespace_2/.git#collection_2/': '*'}" - name: Add collection_1 as a dependency on collection_2 lineinfile: - path: '{{ galaxy_dir }}/development/namespace_2/collection_2/galaxy.yml' + path: '{{ scm_path }}/namespace_2/collection_2/galaxy.yml' regexp: '^dependencies' - line: "dependencies: {'git+file://{{ galaxy_dir }}/development/namespace_1/.git#collection_1/': 'master'}" + line: "dependencies: {'git+file://{{ scm_path }}/namespace_1/.git#collection_1/': 'master'}" - name: Commit the changes shell: git add ./; git commit -m 'add collection' args: - chdir: '{{ galaxy_dir }}/development/{{ item }}' + chdir: '{{ scm_path }}/{{ item }}' loop: - namespace_1 - namespace_2 diff --git a/test/integration/targets/ansible-galaxy-collection-scm/templates/git_prefix_name.yml b/test/integration/targets/ansible-galaxy-collection-scm/templates/git_prefix_name.yml new file mode 100644 index 00000000..8b0e7884 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/templates/git_prefix_name.yml @@ -0,0 +1,2 @@ +collections: + - name: git+file://{{ scm_path }}/amazon.aws/.git diff --git a/test/integration/targets/ansible-galaxy-collection-scm/templates/name_and_type.yml b/test/integration/targets/ansible-galaxy-collection-scm/templates/name_and_type.yml new file mode 100644 index 00000000..8f6be461 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/templates/name_and_type.yml @@ -0,0 +1,3 @@ +collections: + - name: file://{{ scm_path }}/amazon.aws/.git + type: git diff --git a/test/integration/targets/ansible-galaxy-collection-scm/templates/name_without_type.yml b/test/integration/targets/ansible-galaxy-collection-scm/templates/name_without_type.yml new file mode 100644 index 00000000..9d340b54 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/templates/name_without_type.yml @@ -0,0 +1,3 @@ +collections: + # should not work: git prefix or type is required + - name: file://{{ scm_path }}/amazon.aws/.git diff --git a/test/integration/targets/ansible-galaxy-collection-scm/templates/source_and_name.yml b/test/integration/targets/ansible-galaxy-collection-scm/templates/source_and_name.yml new file mode 100644 index 00000000..b7bdb277 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/templates/source_and_name.yml @@ -0,0 +1,4 @@ +collections: + # should not work: source is expected to be a galaxy server name or URL + - source: git+file://{{ scm_path }}/amazon.aws/.git + name: ansible.nope diff --git a/test/integration/targets/ansible-galaxy-collection-scm/templates/source_and_name_and_type.yml b/test/integration/targets/ansible-galaxy-collection-scm/templates/source_and_name_and_type.yml new file mode 100644 index 00000000..01a2ea2c --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/templates/source_and_name_and_type.yml @@ -0,0 +1,5 @@ +collections: + # should not work: source is expected to be a galaxy server name or URL + - source: git+file://{{ scm_path }}/amazon.aws/.git + name: ansible.nope + type: git diff --git a/test/integration/targets/ansible-galaxy-collection-scm/templates/source_only.yml b/test/integration/targets/ansible-galaxy-collection-scm/templates/source_only.yml new file mode 100644 index 00000000..74f87085 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/templates/source_only.yml @@ -0,0 +1,3 @@ +collections: + # should not work: source is expected to be a galaxy server name or URL + - source: git+file://{{ scm_path }}/amazon.aws/.git diff --git a/test/integration/targets/ansible-galaxy-collection-scm/vars/main.yml b/test/integration/targets/ansible-galaxy-collection-scm/vars/main.yml new file mode 100644 index 00000000..c8f50afd --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection-scm/vars/main.yml @@ -0,0 +1,4 @@ +install_path: "{{ galaxy_dir }}/collections/ansible_collections" +alt_install_path: "{{ galaxy_dir }}/other_collections/ansible_collections" +scm_path: "{{ galaxy_dir }}/development" +test_repo_path: "{{ galaxy_dir }}/development/ansible_test" diff --git a/test/integration/targets/ansible-galaxy-collection/aliases b/test/integration/targets/ansible-galaxy-collection/aliases index 4b3ebea3..e501bce5 100644 --- a/test/integration/targets/ansible-galaxy-collection/aliases +++ b/test/integration/targets/ansible-galaxy-collection/aliases @@ -1,3 +1,3 @@ -shippable/fallaxy/group1 -shippable/fallaxy/smoketest -cloud/fallaxy +shippable/galaxy/group1 +shippable/galaxy/smoketest +cloud/galaxy diff --git a/test/integration/targets/ansible-galaxy-collection/files/test_module.py b/test/integration/targets/ansible-galaxy-collection/files/test_module.py new file mode 100644 index 00000000..d7e48149 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/files/test_module.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com> +# (c) 2016, Toshio Kuratomi <tkuratomi@ansible.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ping +version_added: historical +short_description: Try to connect to host, verify a usable python and return C(pong) on success +description: + - A trivial test module, this module always returns C(pong) on successful + contact. It does not make sense in playbooks, but it is useful from + C(/usr/bin/ansible) to verify the ability to login and that a usable Python is configured. + - This is NOT ICMP ping, this is just a trivial test module that requires Python on the remote-node. + - For Windows targets, use the M(ansible.windows.win_ping) module instead. + - For Network targets, use the M(ansible.netcommon.net_ping) module instead. +options: + data: + description: + - Data to return for the C(ping) return value. + - If this parameter is set to C(crash), the module will cause an exception. + type: str + default: pong +seealso: +- module: ansible.netcommon.net_ping +- module: ansible.windows.win_ping +author: + - Ansible Core Team + - Michael DeHaan +''' + +EXAMPLES = ''' +# Test we can logon to 'webservers' and execute python with json lib. +# ansible webservers -m ping + +- name: Example from an Ansible Playbook + ping: + +- name: Induce an exception to see what happens + ping: + data: crash +''' + +RETURN = ''' +ping: + description: value provided with the data parameter + returned: success + type: str + sample: pong +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + data=dict(type='str', default='pong'), + ), + supports_check_mode=True + ) + + if module.params['data'] == 'crash': + raise Exception("boom") + + result = dict( + ping=module.params['data'], + ) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py b/test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py new file mode 100644 index 00000000..53c29f77 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/library/reset_pulp.py @@ -0,0 +1,211 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: reset_pulp +short_description: Resets pulp back to the initial state +description: +- See short_description +options: + pulp_api: + description: + - The Pulp API endpoint. + required: yes + type: str + galaxy_ng_server: + description: + - The Galaxy NG API endpoint. + required: yes + type: str + url_username: + description: + - The username to use when authenticating against Pulp. + required: yes + type: str + url_password: + description: + - The password to use when authenticating against Pulp. + required: yes + type: str + repositories: + description: + - A list of pulp repositories to create. + - Galaxy NG expects a repository that matches C(GALAXY_API_DEFAULT_DISTRIBUTION_BASE_PATH) in + C(/etc/pulp/settings.py) or the default of C(published). + required: yes + type: list + elements: str + namespaces: + description: + - A list of namespaces to create for Galaxy NG. + required: yes + type: list + elements: str +author: +- Jordan Borean (@jborean93) +''' + +EXAMPLES = ''' +- name: reset pulp content + reset_pulp: + pulp_api: http://galaxy:24817 + galaxy_ng_server: http://galaxy/api/galaxy/ + url_username: username + url_password: password + repository: published + namespaces: + - namespace1 + - namespace2 +''' + +RETURN = ''' +# +''' + +import json + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import fetch_url +from ansible.module_utils.common.text.converters import to_text + + +def invoke_api(module, url, method='GET', data=None, status_codes=None): + status_codes = status_codes or [200] + headers = {} + if data: + headers['Content-Type'] = 'application/json' + data = json.dumps(data) + + 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) + + data = to_text(resp.read()) + if data: + return json.loads(data) + + +def delete_galaxy_namespace(namespace, module): + """ Deletes the galaxy ng namespace specified. """ + ns_uri = '%sv3/namespaces/%s/' % (module.params['galaxy_ng_server'], namespace) + invoke_api(module, ns_uri, method='DELETE', status_codes=[204]) + + +def delete_pulp_distribution(distribution, module): + """ Deletes the pulp distribution at the URI specified. """ + task_info = invoke_api(module, distribution, method='DELETE', status_codes=[202]) + wait_pulp_task(task_info['task'], module) + + +def delete_pulp_orphans(module): + """ Deletes any orphaned pulp objects. """ + orphan_uri = module.params['pulp_api'] + '/pulp/api/v3/orphans/' + task_info = invoke_api(module, orphan_uri, method='DELETE', status_codes=[202]) + wait_pulp_task(task_info['task'], module) + + +def delete_pulp_repository(repository, module): + """ Deletes the pulp repository at the URI specified. """ + task_info = invoke_api(module, repository, method='DELETE', status_codes=[202]) + wait_pulp_task(task_info['task'], module) + + +def get_galaxy_namespaces(module): + """ Gets a list of galaxy namespaces. """ + # No pagination has been implemented, shouldn't need unless we ever exceed 100 namespaces. + namespace_uri = module.params['galaxy_ng_server'] + 'v3/namespaces/?limit=100&offset=0' + ns_info = invoke_api(module, namespace_uri) + + return [n['name'] for n in ns_info['data']] + + +def get_pulp_distributions(module): + """ 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) + return [module.params['pulp_api'] + r['pulp_href'] for r in distro_info['results']] + + +def get_pulp_repositories(module): + """ 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) + return [module.params['pulp_api'] + r['pulp_href'] for r in repo_info['results']] + + +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_info = invoke_api(module, ns_uri, method='POST', data=data, status_codes=[201]) + + return ns_info['id'] + + +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_info = invoke_api(module, repo_uri, method='POST', data=data, status_codes=[201]) + + return module.params['pulp_api'] + 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/' + 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) + + return module.params['pulp_api'] + task_info['created_resources'][0] + + +def wait_pulp_task(task, module): + """ Waits for a pulp import task to finish. """ + while True: + task_info = invoke_api(module, module.params['pulp_api'] + task) + if task_info['finished_at'] is not None: + break + + return task_info + + +def main(): + module_args = dict( + pulp_api=dict(type='str', required=True), + galaxy_ng_server=dict(type='str', required=True), + url_username=dict(type='str', required=True), + url_password=dict(type='str', required=True, no_log=True), + repositories=dict(type='list', elements='str', required=True), + namespaces=dict(type='list', elements='str', required=True), + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=False + ) + 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)] + delete_pulp_orphans(module) + [delete_galaxy_namespace(n, module) for n in get_galaxy_namespaces(module)] + + for repository in module.params['repositories']: + repo_href = new_pulp_repository(repository, module) + new_pulp_distribution(repository, repository, repo_href, module) + [new_galaxy_namespace(n, module) for n in module.params['namespaces']] + + module.exit_json(changed=True) + + +if __name__ == '__main__': + main() 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 b876a65f..6f1a17f9 100644 --- a/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py +++ b/test/integration/targets/ansible-galaxy-collection/library/setup_collections.py @@ -83,12 +83,82 @@ import yaml from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_bytes +from functools import partial +from multiprocessing import dummy as threading + + +def publish_collection(module, collection): + namespace = collection['namespace'] + name = collection['name'] + version = collection['version'] + dependencies = collection['dependencies'] + use_symlink = collection['use_symlink'] + + result = {} + 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) + + with open(os.path.join(b_collection_dir, b'README.md'), mode='wb') as fd: + fd.write(b"Collection readme") + + galaxy_meta = { + 'namespace': namespace, + 'name': name, + 'version': version, + 'readme': 'README.md', + 'authors': ['Collection author <name@email.com'], + 'dependencies': dependencies, + 'license': ['GPL-3.0-or-later'], + 'repository': 'https://ansible.com/', + } + 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 tempfile.NamedTemporaryFile(mode='wb') as temp_fd: + temp_fd.write(b"data") + + if use_symlink: + os.mkdir(os.path.join(b_collection_dir, b'docs')) + os.mkdir(os.path.join(b_collection_dir, b'plugins')) + b_target_file = b'RE\xc3\x85DM\xc3\x88.md' + with open(os.path.join(b_collection_dir, b_target_file), mode='wb') as fd: + fd.write(b'data') + + os.symlink(b_target_file, os.path.join(b_collection_dir, b_target_file + b'-link')) + os.symlink(temp_fd.name, os.path.join(b_collection_dir, b_target_file + b'-outside-link')) + os.symlink(os.path.join(b'..', b_target_file), os.path.join(b_collection_dir, b'docs', b_target_file)) + os.symlink(os.path.join(b_collection_dir, b_target_file), + os.path.join(b_collection_dir, b'plugins', b_target_file)) + os.symlink(b'docs', os.path.join(b_collection_dir, b'docs-link')) + + release_filename = '%s-%s-%s.tar.gz' % (namespace, name, version) + collection_path = os.path.join(collection_dir, release_filename) + rc, stdout, stderr = module.run_command(['ansible-galaxy', 'collection', 'build'], cwd=collection_dir) + result['build'] = { + 'rc': rc, + 'stdout': stdout, + 'stderr': stderr, + } + + publish_args = ['ansible-galaxy', 'collection', 'publish', collection_path, '--server', module.params['server']] + if module.params['token']: + publish_args.extend(['--token', module.params['token']]) + + rc, stdout, stderr = module.run_command(publish_args) + result['publish'] = { + 'rc': rc, + 'stdout': stdout, + 'stderr': stderr, + } + + return result def run_module(): module_args = dict( server=dict(type='str', required=True), - token=dict(type='str', required=True), + token=dict(type='str'), collections=dict( type='list', elements='dict', @@ -108,57 +178,17 @@ def run_module(): supports_check_mode=False ) - result = dict(changed=True) + result = dict(changed=True, results=[]) - for idx, collection in enumerate(module.params['collections']): - collection_dir = os.path.join(module.tmpdir, "%s-%s-%s" % (collection['namespace'], collection['name'], - collection['version'])) - b_collection_dir = to_bytes(collection_dir, errors='surrogate_or_strict') - os.mkdir(b_collection_dir) + pool = threading.Pool(4) + publish_func = partial(publish_collection, module) + result['results'] = pool.map(publish_func, module.params['collections']) - with open(os.path.join(b_collection_dir, b'README.md'), mode='wb') as fd: - fd.write(b"Collection readme") + failed = bool(sum( + r['build']['rc'] + r['publish']['rc'] for r in result['results'] + )) - galaxy_meta = { - 'namespace': collection['namespace'], - 'name': collection['name'], - 'version': collection['version'], - 'readme': 'README.md', - 'authors': ['Collection author <name@email.com'], - 'dependencies': collection['dependencies'], - } - 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 tempfile.NamedTemporaryFile(mode='wb') as temp_fd: - temp_fd.write(b"data") - - if collection['use_symlink']: - os.mkdir(os.path.join(b_collection_dir, b'docs')) - os.mkdir(os.path.join(b_collection_dir, b'plugins')) - b_target_file = b'RE\xc3\x85DM\xc3\x88.md' - with open(os.path.join(b_collection_dir, b_target_file), mode='wb') as fd: - fd.write(b'data') - - os.symlink(b_target_file, os.path.join(b_collection_dir, b_target_file + b'-link')) - os.symlink(temp_fd.name, os.path.join(b_collection_dir, b_target_file + b'-outside-link')) - os.symlink(os.path.join(b'..', b_target_file), os.path.join(b_collection_dir, b'docs', b_target_file)) - os.symlink(os.path.join(b_collection_dir, b_target_file), - os.path.join(b_collection_dir, b'plugins', b_target_file)) - os.symlink(b'docs', os.path.join(b_collection_dir, b'docs-link')) - - release_filename = '%s-%s-%s.tar.gz' % (collection['namespace'], collection['name'], collection['version']) - collection_path = os.path.join(collection_dir, release_filename) - module.run_command(['ansible-galaxy', 'collection', 'build'], cwd=collection_dir) - - # To save on time, skip the import wait until the last collection is being uploaded. - publish_args = ['ansible-galaxy', 'collection', 'publish', collection_path, '--server', - module.params['server'], '--token', module.params['token']] - if idx != (len(module.params['collections']) - 1): - publish_args.append('--no-wait') - module.run_command(publish_args) - - module.exit_json(**result) + module.exit_json(failed=failed, **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 a5ba1d47..7e4a5ed6 100644 --- a/test/integration/targets/ansible-galaxy-collection/tasks/build.yml +++ b/test/integration/targets/ansible-galaxy-collection/tasks/build.yml @@ -51,3 +51,26 @@ that: - '"use --force to re-create the collection artifact" in build_existing_no_force.stderr' - '"Created collection for ansible_test.my_collection" in build_existing_force.stdout' + +- name: build collection containing ignored files + command: ansible-galaxy collection build + args: + chdir: '{{ galaxy_dir }}/scratch/ansible_test/ignore' + +- name: list the created tar contents + command: tar -tf {{ galaxy_dir }}/scratch/ansible_test/ignore/ansible_test-ignore-1.0.0.tar.gz + args: + warn: false + register: tar_output + +- name: assert ignored files are not present in the archive + assert: + that: + - item not in tar_output.stdout_lines + loop: '{{ collection_ignored_files }}' + +- name: assert ignored directories are not present in the archive + assert: + that: + - item not in tar_output.stdout_lines + loop: '{{ collection_ignored_directories }}' diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/download.yml b/test/integration/targets/ansible-galaxy-collection/tasks/download.yml index bdd743b2..e00d0b83 100644 --- a/test/integration/targets/ansible-galaxy-collection/tasks/download.yml +++ b/test/integration/targets/ansible-galaxy-collection/tasks/download.yml @@ -4,8 +4,37 @@ path: '{{ galaxy_dir }}/download' 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 }} + register: download_collection + args: + chdir: '{{ galaxy_dir }}/download' + +- name: get result of download collection with multiple dependencies + find: + path: '{{ galaxy_dir }}/download/collections' + file_type: file + register: download_collection_actual + +- name: assert download collection with multiple dependencies --no-deps + assert: + that: + - >- + "Downloading collection 'parent_dep.parent_collection:1.0.0' to '/tmp/" + in download_collection.stdout + - >- + "Downloading collection 'child_dep.child_collection" + not in download_collection.stdout + - >- + "Downloading collection 'child_dep.child_dep2" + not in download_collection.stdout + - download_collection_actual.examined == 2 + - download_collection_actual.matched == 2 + - (download_collection_actual.files[0].path | basename) in ['requirements.yml', 'parent_dep-parent_collection-1.0.0.tar.gz'] + - (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 -s {{ fallaxy_galaxy_server }} {{ galaxy_verbosity }} + command: ansible-galaxy collection download parent_dep.parent_collection:1.0.0 -s pulp_v2 {{ galaxy_verbosity }} register: download_collection args: chdir: '{{ galaxy_dir }}/download' @@ -19,9 +48,9 @@ - name: assert download collection with multiple dependencies assert: that: - - '"Downloading collection ''parent_dep.parent_collection'' to" in download_collection.stdout' - - '"Downloading collection ''child_dep.child_collection'' to" in download_collection.stdout' - - '"Downloading collection ''child_dep.child_dep2'' to" in download_collection.stdout' + - '"Downloading collection ''parent_dep.parent_collection:1.0.0'' to" in download_collection.stdout' + - '"Downloading collection ''child_dep.child_collection:0.9.9'' to" in download_collection.stdout' + - '"Downloading collection ''child_dep.child_dep2:1.2.2'' to" in download_collection.stdout' - download_collection_actual.examined == 4 - download_collection_actual.matched == 4 - (download_collection_actual.files[0].path | basename) in ['requirements.yml', 'child_dep-child_dep2-1.2.2.tar.gz', 'child_dep-child_collection-0.9.9.tar.gz', 'parent_dep-parent_collection-1.0.0.tar.gz'] @@ -69,7 +98,7 @@ dest: '{{ galaxy_dir }}/download/download.yml' - name: download collection with req to custom dir - command: ansible-galaxy collection download -r '{{ galaxy_dir }}/download/download.yml' -s {{ fallaxy_ah_server }} -p '{{ galaxy_dir }}/download/collections-custom' {{ galaxy_verbosity }} + command: ansible-galaxy collection download -r '{{ galaxy_dir }}/download/download.yml' -s galaxy_ng -p '{{ galaxy_dir }}/download/collections-custom' {{ galaxy_verbosity }} register: download_req_custom_path - name: get result of download collection with req to custom dir @@ -81,7 +110,7 @@ - name: assert download collection with multiple dependencies assert: that: - - '"Downloading collection ''namespace1.name1'' to" in download_req_custom_path.stdout' + - '"Downloading collection ''namespace1.name1:1.1.0-beta.1'' to" in download_req_custom_path.stdout' - download_req_custom_path_actual.examined == 2 - download_req_custom_path_actual.matched == 2 - (download_req_custom_path_actual.files[0].path | basename) in ['requirements.yml', 'namespace1-name1-1.1.0-beta.1.tar.gz'] @@ -138,5 +167,5 @@ - assert: that: - - '"Downloading collection ''ansible_test.my_collection'' to" in download_collection.stdout' + - '"Downloading collection ''ansible_test.my_collection:1.0.0'' to" in download_collection.stdout' - download_collection_actual.stat.exists diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/init.yml b/test/integration/targets/ansible-galaxy-collection/tasks/init.yml index 15ec5eab..a57cafc3 100644 --- a/test/integration/targets/ansible-galaxy-collection/tasks/init.yml +++ b/test/integration/targets/ansible-galaxy-collection/tasks/init.yml @@ -42,3 +42,34 @@ - (init_custom_path_actual.files | map(attribute='path') | list)[0] | basename in ['docs', 'plugins', 'roles'] - (init_custom_path_actual.files | map(attribute='path') | list)[1] | basename in ['docs', 'plugins', 'roles'] - (init_custom_path_actual.files | map(attribute='path') | list)[2] | basename in ['docs', 'plugins', 'roles'] + +- name: create collection for ignored files and folders + command: ansible-galaxy collection init ansible_test.ignore + args: + chdir: '{{ galaxy_dir }}/scratch' + +- name: create list of ignored files + set_fact: + collection_ignored_files: + - plugins/compiled.pyc + - something.retry + - .git + +- name: plant ignored files into the ansible_test.ignore collection + copy: + dest: '{{ galaxy_dir }}/scratch/ansible_test/ignore/{{ item }}' + content: '{{ item }}' + loop: '{{ collection_ignored_files }}' + +- name: create list of ignored directories + set_fact: + collection_ignored_directories: + - docs/.git + - plugins/doc_fragments/__pycache__ + - .svn + +- name: plant ignored folders into the ansible_test.ignore collection + file: + path: '{{ galaxy_dir }}/scratch/ansible_test/ignore/{{ item }}' + state: directory + loop: '{{ collection_ignored_directories }}' diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/install.yml b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml index 11ce1c01..66d79e59 100644 --- a/test/integration/targets/ansible-galaxy-collection/tasks/install.yml +++ b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml @@ -5,7 +5,7 @@ state: directory - name: install simple collection with implicit path - {{ test_name }} - command: ansible-galaxy collection install namespace1.name1 -s '{{ test_server }}' {{ galaxy_verbosity }} + command: ansible-galaxy collection install namespace1.name1 -s '{{ test_name }}' {{ galaxy_verbosity }} environment: ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' register: install_normal @@ -32,7 +32,7 @@ - (install_normal_manifest.content | b64decode | from_json).collection_info.version == '1.0.9' - name: install existing without --force - {{ test_name }} - command: ansible-galaxy collection install namespace1.name1 -s '{{ test_server }}' {{ galaxy_verbosity }} + command: ansible-galaxy collection install namespace1.name1 -s '{{ test_name }}' {{ galaxy_verbosity }} environment: ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' register: install_existing_no_force @@ -40,10 +40,10 @@ - name: assert install existing without --force - {{ test_name }} assert: that: - - '"Skipping ''namespace1.name1'' as it is already installed" in install_existing_no_force.stdout' + - '"Nothing to do. All requested collections are already installed" in install_existing_no_force.stdout' - name: install existing with --force - {{ test_name }} - command: ansible-galaxy collection install namespace1.name1 -s '{{ test_server }}' --force {{ galaxy_verbosity }} + command: ansible-galaxy collection install namespace1.name1 -s '{{ test_name }}' --force {{ galaxy_verbosity }} environment: ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' register: install_existing_force @@ -59,7 +59,7 @@ state: absent - name: install pre-release as explicit version to custom dir - {{ test_name }} - command: ansible-galaxy collection install 'namespace1.name1:1.1.0-beta.1' -s '{{ test_server }}' -p '{{ galaxy_dir }}/ansible_collections' {{ galaxy_verbosity }} + command: ansible-galaxy collection install 'namespace1.name1:1.1.0-beta.1' -s '{{ test_name }}' -p '{{ galaxy_dir }}/ansible_collections' {{ galaxy_verbosity }} register: install_prerelease - name: get result of install pre-release as explicit version to custom dir - {{ test_name }} @@ -79,7 +79,7 @@ state: absent - name: install pre-release version with --pre to custom dir - {{ test_name }} - command: ansible-galaxy collection install --pre 'namespace1.name1' -s '{{ test_server }}' -p '{{ galaxy_dir }}/ansible_collections' {{ galaxy_verbosity }} + command: ansible-galaxy collection install --pre 'namespace1.name1' -s '{{ test_name }}' -p '{{ galaxy_dir }}/ansible_collections' {{ galaxy_verbosity }} register: install_prerelease - name: get result of install pre-release version with --pre to custom dir - {{ test_name }} @@ -94,7 +94,7 @@ - (install_prerelease_actual.content | b64decode | from_json).collection_info.version == '1.1.0-beta.1' - name: install multiple collections with dependencies - {{ test_name }} - command: ansible-galaxy collection install parent_dep.parent_collection namespace2.name -s {{ test_name }} {{ galaxy_verbosity }} + command: ansible-galaxy collection install parent_dep.parent_collection:1.0.0 namespace2.name -s {{ test_name }} {{ galaxy_verbosity }} args: chdir: '{{ galaxy_dir }}/ansible_collections' environment: @@ -127,13 +127,23 @@ - (install_multiple_with_dep_actual.results[3].content | b64decode | from_json).collection_info.version == '1.2.2' - name: expect failure with dep resolution failure - command: ansible-galaxy collection install fail_namespace.fail_collection -s {{ test_server }} {{ galaxy_verbosity }} + command: ansible-galaxy collection install fail_namespace.fail_collection -s {{ test_name }} {{ galaxy_verbosity }} register: fail_dep_mismatch - failed_when: '"Cannot meet dependency requirement ''fail_dep2.name:<0.0.5'' for collection fail_namespace.fail_collection" not in fail_dep_mismatch.stderr' + 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' + +- name: Find artifact url for namespace3.name + uri: + url: '{{ test_server }}{{ vX }}collections/namespace3/name/versions/1.0.0/' + user: '{{ pulp_user }}' + password: '{{ pulp_password }}' + force_basic_auth: true + register: artifact_url_response - name: download a collection for an offline install - {{ test_name }} get_url: - url: '{{ test_server }}custom/collections/namespace3-name-1.0.0.tar.gz' + url: '{{ artifact_url_response.json.download_url }}' dest: '{{ galaxy_dir }}/namespace3.tar.gz' - name: install a collection from a tarball - {{ test_name }} @@ -153,6 +163,81 @@ - '"Installing ''namespace3.name:1.0.0'' to" in install_tarball.stdout' - (install_tarball_actual.content | b64decode | from_json).collection_info.version == '1.0.0' +- name: write a requirements file using the artifact and a conflicting version + copy: + content: | + collections: + - name: {{ galaxy_dir }}/namespace3.tar.gz + version: 1.2.0 + dest: '{{ galaxy_dir }}/test_req.yml' + +- name: install the requirements file with mismatched versions + command: ansible-galaxy collection install -r '{{ galaxy_dir }}/test_req.yml' {{ galaxy_verbosity }} + ignore_errors: True + register: result + +- name: remove the requirements file + file: + path: '{{ galaxy_dir }}/test_req.yml' + state: absent + +- assert: + that: expected_error in error + vars: + reset_color: '\x1b\[0m' + color: '\x1b\[[0-9];[0-9]{2}m' + error: "{{ result.stderr | regex_replace(reset_color) | regex_replace(color) | regex_replace('\\n', ' ') }}" + expected_error: >- + ERROR! Failed to resolve the requested dependencies map. + Got the candidate namespace3.name:1.0.0 (direct request) + which didn't satisfy all of the following requirements: + * namespace3.name:1.2.0 + +- name: test error for mismatched dependency versions + vars: + reset_color: '\x1b\[0m' + color: '\x1b\[[0-9];[0-9]{2}m' + error: "{{ result.stderr | regex_replace(reset_color) | regex_replace(color) | regex_replace('\\n', ' ') }}" + expected_error: >- + ERROR! Failed to resolve the requested dependencies map. + Got the candidate namespace3.name:1.0.0 (dependency of tmp_parent.name:1.0.0) + which didn't satisfy all of the following requirements: + * namespace3.name:1.2.0 + block: + - name: init a new parent collection + command: ansible-galaxy collection init tmp_parent.name --init-path '{{ galaxy_dir }}/scratch' + + - name: replace the dependencies + lineinfile: + path: "{{ galaxy_dir }}/scratch/tmp_parent/name/galaxy.yml" + regexp: "^dependencies:*" + line: "dependencies: { '{{ galaxy_dir }}/namespace3.tar.gz': '1.2.0' }" + + - name: build the new artifact + command: ansible-galaxy collection build {{ galaxy_dir }}/scratch/tmp_parent/name + args: + chdir: "{{ galaxy_dir }}" + + - name: install the artifact to verify the error is handled + command: ansible-galaxy collection install '{{ galaxy_dir }}/tmp_parent-name-1.0.0.tar.gz' + ignore_errors: yes + register: result + + - debug: msg="Actual - {{ error }}" + + - debug: msg="Expected - {{ expected_error }}" + + - assert: + that: expected_error in error + always: + - name: clean up collection skeleton and artifact + file: + state: absent + path: "{{ item }}" + loop: + - "{{ galaxy_dir }}/scratch/tmp_parent/" + - "{{ galaxy_dir }}/tmp_parent-name-1.0.0.tar.gz" + - name: setup bad tarball - {{ test_name }} script: build_bad_tar.py {{ galaxy_dir | quote }} @@ -173,8 +258,16 @@ that: - not fail_bad_tar_actual.stat.exists +- name: Find artifact url for namespace4.name + uri: + url: '{{ test_server }}{{ vX }}collections/namespace4/name/versions/1.0.0/' + user: '{{ pulp_user }}' + password: '{{ pulp_password }}' + force_basic_auth: true + register: artifact_url_response + - name: install a collection from a URI - {{ test_name }} - command: ansible-galaxy collection install '{{ test_server }}custom/collections/namespace4-name-1.0.0.tar.gz' {{ galaxy_verbosity }} + command: ansible-galaxy collection install {{ artifact_url_response.json.download_url}} {{ galaxy_verbosity }} register: install_uri environment: ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' @@ -197,23 +290,25 @@ environment: ANSIBLE_GALAXY_SERVER_LIST: undefined -- name: install a collection with an empty server list - {{ test_name }} - command: ansible-galaxy collection install namespace5.name -s '{{ test_server }}' {{ galaxy_verbosity }} - register: install_empty_server_list - environment: - ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' - ANSIBLE_GALAXY_SERVER_LIST: '' - -- name: get result of a collection with an empty server list - {{ test_name }} - slurp: - path: '{{ galaxy_dir }}/ansible_collections/namespace5/name/MANIFEST.json' - register: install_empty_server_list_actual - -- name: assert install a collection with an empty server list - {{ test_name }} - assert: - that: - - '"Installing ''namespace5.name:1.0.0'' to" in install_empty_server_list.stdout' - - (install_empty_server_list_actual.content | b64decode | from_json).collection_info.version == '1.0.0' +- when: not requires_auth + block: + - name: install a collection with an empty server list - {{ test_name }} + command: ansible-galaxy collection install namespace5.name -s '{{ test_server }}' {{ galaxy_verbosity }} + register: install_empty_server_list + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + ANSIBLE_GALAXY_SERVER_LIST: '' + + - name: get result of a collection with an empty server list - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace5/name/MANIFEST.json' + register: install_empty_server_list_actual + + - name: assert install a collection with an empty server list - {{ test_name }} + assert: + that: + - '"Installing ''namespace5.name:1.0.0'' to" in install_empty_server_list.stdout' + - (install_empty_server_list_actual.content | b64decode | from_json).collection_info.version == '1.0.0' - name: create test requirements file with both roles and collections - {{ test_name }} copy: @@ -227,7 +322,7 @@ # Need to run with -vvv to validate the roles will be skipped msg - name: install collections only with requirements-with-role.yml - {{ test_name }} - command: ansible-galaxy collection install -r '{{ galaxy_dir }}/ansible_collections/requirements-with-role.yml' -s '{{ test_server }}' -vvv + command: ansible-galaxy collection install -r '{{ galaxy_dir }}/ansible_collections/requirements-with-role.yml' -s '{{ test_name }}' -vvv register: install_req_collection environment: ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' @@ -260,7 +355,7 @@ dest: '{{ galaxy_dir }}/ansible_collections/requirements.yaml' - name: install collections with ansible-galaxy install - {{ test_name }} - command: ansible-galaxy install -r '{{ galaxy_dir }}/ansible_collections/requirements.yaml' -s '{{ test_server }}' + command: ansible-galaxy install -r '{{ galaxy_dir }}/ansible_collections/requirements.yaml' -s '{{ test_name }}' register: install_req environment: ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' @@ -283,13 +378,42 @@ - (install_req_actual.results[0].content | b64decode | from_json).collection_info.version == '1.0.0' - (install_req_actual.results[1].content | b64decode | from_json).collection_info.version == '1.0.0' -- name: remove test collection install directory - {{ test_name }} - file: - path: '{{ galaxy_dir }}/ansible_collections' - state: absent +# 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_name }} +# 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_name }} +# 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_name }} +# 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_name }} - command: ansible-galaxy collection install symlink.symlink -s '{{ test_server }}' {{ galaxy_verbosity }} + command: ansible-galaxy collection install symlink.symlink -s '{{ test_name }}' {{ galaxy_verbosity }} environment: ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections' register: install_symlink @@ -328,3 +452,81 @@ - install_symlink_actual.results[4].stat.lnk_target == 'docs' - install_symlink_actual.results[5].stat.islnk - install_symlink_actual.results[5].stat.lnk_target == '../REÅDMÈ.md' + +- name: remove install directory for the next test because parent_dep.parent_collection was installed - {{ test_name }} + file: + path: '{{ galaxy_dir }}/ansible_collections' + state: absent + +- name: install collection and dep compatible with multiple requirements - {{ test_name }} + command: ansible-galaxy collection install parent_dep.parent_collection parent_dep2.parent_collection + environment: + ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections' + register: install_req + +- name: assert install collections with ansible-galaxy install - {{ test_name }} + assert: + that: + - '"Installing ''parent_dep.parent_collection:1.0.0'' to" in install_req.stdout' + - '"Installing ''parent_dep2.parent_collection:1.0.0'' to" in install_req.stdout' + - '"Installing ''child_dep.child_collection:0.5.0'' to" in install_req.stdout' + +- name: install a collection to a directory that contains another collection with no metadata + block: + + # Collections are usable in ansible without a galaxy.yml or MANIFEST.json + - name: create a collection directory + file: + state: directory + path: '{{ galaxy_dir }}/ansible_collections/unrelated_namespace/collection_without_metadata/plugins' + + - name: install a collection to the same installation directory - {{ test_name }} + command: ansible-galaxy collection install namespace1.name1 + environment: + ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections' + register: install_req + + - name: assert installed collections with ansible-galaxy install - {{ test_name }} + assert: + that: + - '"Installing ''namespace1.name1:1.0.9'' to" in install_req.stdout' + +- name: remove test collection install directory - {{ test_name }} + file: + path: '{{ galaxy_dir }}/ansible_collections' + state: absent + + +- name: download collections with pre-release dep - {{ test_name }} + command: ansible-galaxy collection download dep_with_beta.parent namespace1.name1:1.1.0-beta.1 -p '{{ galaxy_dir }}/scratch' + +- name: install collection with concrete pre-release dep - {{ test_name }} + command: ansible-galaxy collection install -r '{{ galaxy_dir }}/scratch/requirements.yml' + args: + chdir: '{{ galaxy_dir }}/scratch' + environment: + ANSIBLE_COLLECTIONS_PATHS: '{{ galaxy_dir }}/ansible_collections' + register: install_concrete_pre + +- name: get result of install collections with concrete pre-release dep - {{ test_name }} + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ collection }}/MANIFEST.json' + register: install_concrete_pre_actual + loop_control: + loop_var: collection + loop: + - namespace1/name1 + - dep_with_beta/parent + +- name: assert install collections with ansible-galaxy install - {{ test_name }} + assert: + that: + - '"Installing ''namespace1.name1:1.1.0-beta.1'' to" in install_concrete_pre.stdout' + - '"Installing ''dep_with_beta.parent:1.0.0'' to" in install_concrete_pre.stdout' + - (install_concrete_pre_actual.results[0].content | b64decode | from_json).collection_info.version == '1.1.0-beta.1' + - (install_concrete_pre_actual.results[1].content | b64decode | from_json).collection_info.version == '1.0.0' + +- name: remove collection dir after round of testing - {{ test_name }} + file: + path: '{{ galaxy_dir }}/ansible_collections' + state: absent diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/list.yml b/test/integration/targets/ansible-galaxy-collection/tasks/list.yml new file mode 100644 index 00000000..331e0a1c --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/list.yml @@ -0,0 +1,131 @@ +- name: initialize collection structure + command: ansible-galaxy collection init {{ item }} --init-path "{{ galaxy_dir }}/dev/ansible_collections" {{ galaxy_verbosity }} + loop: + - 'dev.collection1' + - 'dev.collection2' + - 'dev.collection3' + +- name: replace the default version of the collections + lineinfile: + path: "{{ galaxy_dir }}/dev/ansible_collections/dev/{{ item.name }}/galaxy.yml" + line: "{{ item.version }}" + regexp: "version: .*" + loop: + - name: "collection1" + version: "version: null" + - name: "collection2" + version: "version: placeholder" + - name: "collection3" + version: "version: ''" + +- name: list collections in development without semver versions + command: ansible-galaxy collection list {{ galaxy_verbosity }} + register: list_result + environment: + ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/dev:{{ galaxy_dir }}/prod" + +- assert: + that: + - "'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" + +- name: list collections in human format + command: ansible-galaxy collection list --format human + register: list_result_human + environment: + ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/dev:{{ galaxy_dir }}/prod" + +- assert: + that: + - "'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" + +- name: list collections in yaml format + command: ansible-galaxy collection list --format yaml + register: list_result_yaml + environment: + ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/dev:{{ galaxy_dir }}/prod" + +- assert: + that: + - "item.value | length == 3" + - "item.value['dev.collection1'].version == '*'" + - "item.value['dev.collection2'].version == 'placeholder'" + - "item.value['dev.collection3'].version == '*'" + with_dict: "{{ list_result_yaml.stdout | from_yaml }}" + +- name: list collections in json format + command: ansible-galaxy collection list --format json + register: list_result_json + environment: + ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/dev:{{ galaxy_dir }}/prod" + +- assert: + that: + - "item.value | length == 3" + - "item.value['dev.collection1'].version == '*'" + - "item.value['dev.collection2'].version == 'placeholder'" + - "item.value['dev.collection3'].version == '*'" + with_dict: "{{ list_result_json.stdout | from_json }}" + +- name: list single collection in json format + command: "ansible-galaxy collection list {{ item.key }} --format json" + register: list_single_result_json + environment: + ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/dev:{{ galaxy_dir }}/prod" + with_dict: "{{ { 'dev.collection1': '*', 'dev.collection2': 'placeholder', 'dev.collection3': '*' } }}" + +- assert: + that: + - "(item.stdout | from_json)[galaxy_dir + '/dev/ansible_collections'][item.item.key].version == item.item.value" + with_items: "{{ list_single_result_json.results }}" + +- name: list single collection in yaml format + command: "ansible-galaxy collection list {{ item.key }} --format yaml" + register: list_single_result_yaml + environment: + ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/dev:{{ galaxy_dir }}/prod" + with_dict: "{{ { 'dev.collection1': '*', 'dev.collection2': 'placeholder', 'dev.collection3': '*' } }}" + +- assert: + that: + - "(item.stdout | from_yaml)[galaxy_dir + '/dev/ansible_collections'][item.item.key].version == item.item.value" + with_items: "{{ list_single_result_json.results }}" + +- name: test that no json is emitted when no collection paths are usable + command: "ansible-galaxy collection list --format json" + register: list_result_error + ignore_errors: True + environment: + ANSIBLE_COLLECTIONS_PATH: "" + +- assert: + that: + - "'{}' not in list_result_error.stdout" + +- name: install an artifact to the second collections path + command: ansible-galaxy collection install namespace1.name1 -s galaxy_ng {{ galaxy_verbosity }} -p "{{ galaxy_dir }}/prod" + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + +- name: replace the artifact version + lineinfile: + path: "{{ galaxy_dir }}/prod/ansible_collections/namespace1/name1/MANIFEST.json" + line: ' "version": null,' + regexp: ' "version": .*' + +- name: test listing collections in all paths + command: ansible-galaxy collection list {{ galaxy_verbosity }} + register: list_result + ignore_errors: True + environment: + ANSIBLE_COLLECTIONS_PATH: "{{ galaxy_dir }}/dev:{{ galaxy_dir }}/prod" + +- assert: + that: + - list_result is failed + - "'is expected to have a valid SemVer version value but got None' in list_result.stderr" diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/main.yml b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml index c4cc9edb..a536a720 100644 --- a/test/integration/targets/ansible-galaxy-collection/tasks/main.yml +++ b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml @@ -14,6 +14,37 @@ - name: run ansible-galaxy collection build tests import_tasks: build.yml +# The pulp container has a long start up time +# The first task to interact with pulp needs to wait until it responds appropriately +- name: list pulp distributions + uri: + url: '{{ pulp_api }}/pulp/api/v3/distributions/ansible/ansible/' + status_code: + - 200 + user: '{{ pulp_user }}' + password: '{{ pulp_password }}' + force_basic_auth: true + register: pulp_distributions + until: pulp_distributions is successful + delay: 1 + retries: 60 + +- name: configure pulp + include_tasks: pulp.yml + +- name: Get galaxy_ng token + uri: + url: '{{ galaxy_ng_server }}v3/auth/token/' + method: POST + body_format: json + body: {} + status_code: + - 200 + user: '{{ pulp_user }}' + password: '{{ pulp_password }}' + force_basic_auth: true + register: galaxy_ng_token + - name: create test ansible.cfg that contains the Galaxy server list template: src: ansible.cfg.j2 @@ -21,155 +52,140 @@ - name: run ansible-galaxy collection publish tests for {{ test_name }} include_tasks: publish.yml + args: + apply: + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' vars: test_name: '{{ item.name }}' test_server: '{{ item.server }}' - with_items: - - name: galaxy - server: '{{ fallaxy_galaxy_server }}' - - name: automation_hub - server: '{{ fallaxy_ah_server }}' + vX: '{{ "v3/" if item.v3|default(false) else "v2/" }}' + loop: + - name: pulp_v2 + server: '{{ pulp_server }}published/api/' + - name: pulp_v3 + server: '{{ pulp_server }}published/api/' + v3: true + - name: galaxy_ng + server: '{{ galaxy_ng_server }}' + v3: true # We use a module for this so we can speed up the test time. +# For pulp interactions, we only upload to galaxy_ng which shares +# the same repo and distribution with pulp_ansible +# However, we use galaxy_ng only, since collections are unique across +# pulp repositories, and galaxy_ng maintains a 2nd list of published collections - name: setup test collections for install and download test setup_collections: - server: '{{ fallaxy_galaxy_server }}' - token: '{{ fallaxy_token }}' - collections: - # Scenario to test out pre-release being ignored unless explicitly set and version pagination. - - namespace: namespace1 - name: name1 - version: 0.0.1 - - namespace: namespace1 - name: name1 - version: 0.0.2 - - namespace: namespace1 - name: name1 - version: 0.0.3 - - namespace: namespace1 - name: name1 - version: 0.0.4 - - namespace: namespace1 - name: name1 - version: 0.0.5 - - namespace: namespace1 - name: name1 - version: 0.0.6 - - namespace: namespace1 - name: name1 - version: 0.0.7 - - namespace: namespace1 - name: name1 - version: 0.0.8 - - namespace: namespace1 - name: name1 - version: 0.0.9 - - namespace: namespace1 - name: name1 - version: 0.0.10 - - namespace: namespace1 - name: name1 - version: 0.1.0 - - namespace: namespace1 - name: name1 - version: 1.0.0 - - namespace: namespace1 - name: name1 - version: 1.0.9 - - namespace: namespace1 - name: name1 - version: 1.1.0-beta.1 - - # Pad out number of namespaces for pagination testing - - namespace: namespace2 - name: name - - namespace: namespace3 - name: name - - namespace: namespace4 - name: name - - namespace: namespace5 - name: name - - namespace: namespace6 - name: name - - namespace: namespace7 - name: name - - namespace: namespace8 - name: name - - namespace: namespace9 - name: name - - # Complex dependency resolution - - namespace: parent_dep - name: parent_collection - dependencies: - child_dep.child_collection: '>=0.5.0,<1.0.0' - - namespace: child_dep - name: child_collection - version: 0.4.0 - - namespace: child_dep - name: child_collection - version: 0.5.0 - - namespace: child_dep - name: child_collection - version: 0.9.9 - dependencies: - child_dep.child_dep2: '!=1.2.3' - - namespace: child_dep - name: child_collection - - namespace: child_dep - name: child_dep2 - version: 1.2.2 - - namespace: child_dep - name: child_dep2 - version: 1.2.3 - - # Dep resolution failure - - namespace: fail_namespace - name: fail_collection - version: 2.1.2 - dependencies: - fail_dep.name: '0.0.5' - fail_dep2.name: '<0.0.5' - - namespace: fail_dep - name: name - version: '0.0.5' - dependencies: - fail_dep2.name: '>0.0.5' - - namespace: fail_dep2 - name: name + server: galaxy_ng + collections: '{{ collection_list }}' + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' - # Symlink tests - - namespace: symlink - name: symlink - use_symlink: yes +# Stores the cached test version number index as we run install many times +- set_fact: + cache_version_build: 0 - name: run ansible-galaxy collection install tests for {{ test_name }} include_tasks: install.yml vars: test_name: '{{ item.name }}' test_server: '{{ item.server }}' - with_items: - - name: galaxy - server: '{{ fallaxy_galaxy_server }}' - - name: automation_hub - server: '{{ fallaxy_ah_server }}' - -# fake.fake does not exist but we check the output to ensure it checked all 3 -# servers defined in the config. We hardcode to -vvv as that's what level the -# message is shown -- name: test install fallback on server list - command: ansible-galaxy collection install fake.fake -vvv - ignore_errors: yes + vX: '{{ "v3/" if item.v3|default(false) else "v2/" }}' + requires_auth: '{{ item.requires_auth|default(false) }}' + args: + apply: + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + loop: + - name: galaxy_ng + server: '{{ galaxy_ng_server }}' + v3: true + requires_auth: true + - name: pulp_v2 + server: '{{ pulp_server }}published/api/' + - name: pulp_v3 + server: '{{ pulp_server }}published/api/' + v3: true + +- name: publish collection with a dep on another server + setup_collections: + server: secondary + collections: + - namespace: secondary + name: name + # parent_dep.parent_collection does not exist on the secondary server + dependencies: + parent_dep.parent_collection: '1.0.0' environment: ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' - register: missing_fallback -- name: assert test install fallback on server list +- name: install collection with dep on another server + command: ansible-galaxy collection install secondary.name -vvv # 3 -v's will show the source in the stdout + register: install_cross_dep + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}/ansible_collections' + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + +- name: get result of install collection with dep on another server + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ item.namespace }}/{{ item.name }}/MANIFEST.json' + register: install_cross_dep_actual + loop: + - namespace: secondary + name: name + - namespace: parent_dep + name: parent_collection + - namespace: child_dep + name: child_collection + - namespace: child_dep + name: child_dep2 + +- name: assert result of install collection with dep on another server assert: that: - - missing_fallback.rc == 1 - - '"Collection ''fake.fake'' is not available from server galaxy" in missing_fallback.stdout' - - '"Collection ''fake.fake'' is not available from server automation_hub" in missing_fallback.stdout' + - >- + "'secondary.name:1.0.0' obtained from server secondary" + 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" + in install_cross_dep.stdout + - >- + "'child_dep.child_collection:0.9.9' obtained from server pulp_v2" + in install_cross_dep.stdout + - >- + "'child_dep.child_dep2:1.2.2' obtained from server pulp_v2" + 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' + - (install_cross_dep_actual.results[2].content | b64decode | from_json).collection_info.version == '0.9.9' + - (install_cross_dep_actual.results[3].content | b64decode | from_json).collection_info.version == '1.2.2' - name: run ansible-galaxy collection download tests include_tasks: download.yml + args: + apply: + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + +- name: run ansible-galaxy collection verify tests for {{ test_name }} + include_tasks: verify.yml + args: + apply: + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + vars: + test_name: 'galaxy_ng' + test_server: '{{ galaxy_ng_server }}' + vX: "v3/" + +- name: run ansible-galaxy collection list tests + include_tasks: list.yml + +- include_tasks: upgrade.yml + args: + apply: + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml b/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml index aa137304..241eae60 100644 --- a/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml +++ b/test/integration/targets/ansible-galaxy-collection/tasks/publish.yml @@ -1,46 +1,33 @@ --- -- name: fail to publish with no token - {{ test_name }} - command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} {{ galaxy_verbosity }} - args: - chdir: '{{ galaxy_dir }}' - register: fail_no_token - failed_when: '"HTTP Code: 401" not in fail_no_token.stderr' - -- name: fail to publish with invalid token - {{ test_name }} - command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} --token fail {{ galaxy_verbosity }} - args: - chdir: '{{ galaxy_dir }}' - register: fail_invalid_token - failed_when: '"HTTP Code: 401" not in fail_invalid_token.stderr' - - name: publish collection - {{ test_name }} - command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} --token {{ fallaxy_token }} {{ galaxy_verbosity }} + command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_name }} {{ galaxy_verbosity }} args: chdir: '{{ galaxy_dir }}' register: publish_collection - name: get result of publish collection - {{ test_name }} uri: - url: '{{ test_server }}v2/collections/ansible_test/my_collection/versions/1.0.0/' + url: '{{ test_server }}{{ vX }}collections/ansible_test/my_collection/versions/1.0.0/' return_content: yes + user: '{{ pulp_user }}' + password: '{{ pulp_password }}' + force_basic_auth: true register: publish_collection_actual - name: assert publish collection - {{ test_name }} assert: that: - '"Collection has been successfully published and imported to the Galaxy server" in publish_collection.stdout' - - publish_collection_actual.json.metadata.name == 'my_collection' - - publish_collection_actual.json.metadata.namespace == 'ansible_test' - - publish_collection_actual.json.metadata.version == '1.0.0' + - publish_collection_actual.json.collection.name == 'my_collection' + - publish_collection_actual.json.namespace.name == 'ansible_test' + - publish_collection_actual.json.version == '1.0.0' - name: fail to publish existing collection version - {{ test_name }} - command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_server }} --token {{ fallaxy_token }} {{ galaxy_verbosity }} + command: ansible-galaxy collection publish ansible_test-my_collection-1.0.0.tar.gz -s {{ test_name }} {{ galaxy_verbosity }} args: chdir: '{{ galaxy_dir }}' register: fail_publish_existing - failed_when: '"Artifact already exists" not in fail_publish_existing.stderr' + failed_when: fail_publish_existing is not failed - name: reset published collections - {{ test_name }} - uri: - url: '{{ test_server }}custom/reset/' - method: POST + include_tasks: pulp.yml diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/pulp.yml b/test/integration/targets/ansible-galaxy-collection/tasks/pulp.yml new file mode 100644 index 00000000..7b6c5f86 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/pulp.yml @@ -0,0 +1,11 @@ +# These tasks configure pulp/pulp_ansible so that we can use the container +# This will also reset pulp between iterations +# A module is used to make the tests run quicker as this will send lots of API requests. +- name: reset pulp content + reset_pulp: + pulp_api: '{{ pulp_api }}' + galaxy_ng_server: '{{ galaxy_ng_server }}' + url_username: '{{ pulp_user }}' + url_password: '{{ pulp_password }}' + repositories: '{{ pulp_repositories }}' + namespaces: '{{ collection_list|map(attribute="namespace")|unique + publish_namespaces }}' diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml b/test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml new file mode 100644 index 00000000..b49f1eec --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/upgrade.yml @@ -0,0 +1,282 @@ +##### Updating collections with a new version constraint + +# No deps + +- name: reset installation directory + file: + state: "{{ item }}" + path: "{{ galaxy_dir }}/ansible_collections" + loop: + - absent + - directory + +- name: install a collection + 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' + +- name: install a new version of the collection without --force + command: ansible-galaxy collection install namespace1.name1:>0.0.4,<=0.0.5 {{ galaxy_verbosity }} + register: result + +- name: check the MANIFEST.json to verify the version + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1/MANIFEST.json' + register: metadata + +- assert: + that: + - '"namespace1.name1:0.0.5 was installed successfully" in result.stdout_lines' + - (metadata.content | b64decode | from_json).collection_info.version == '0.0.5' + +- name: don't reinstall the collection in the requirement is compatible + command: ansible-galaxy collection install namespace1.name1:>=0.0.5 {{ galaxy_verbosity }} + register: result + +- assert: + that: "\"Nothing to do. All requested collections are already installed.\" in result.stdout" + +- name: install a pre-release of the collection without --force + command: ansible-galaxy collection install namespace1.name1:1.1.0-beta.1 {{ galaxy_verbosity }} + register: result + +- name: check the MANIFEST.json to verify the version + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1/MANIFEST.json' + register: metadata + +- assert: + that: + - '"namespace1.name1:1.1.0-beta.1 was installed successfully" in result.stdout_lines' + - (metadata.content | b64decode | from_json).collection_info.version == '1.1.0-beta.1' + +# With deps + +- name: reset installation directory + file: + state: "{{ item }}" + path: "{{ galaxy_dir }}/ansible_collections" + loop: + - absent + - directory + +- name: install a dep that will need to be upgraded to be compatible with the parent + command: ansible-galaxy collection install child_dep.child_collection:0.4.0 --no-deps {{ galaxy_verbosity }} + register: result + failed_when: + - '"child_dep.child_collection:0.4.0 was installed successfully" not in result.stdout_lines' + +- name: install a dep of the dep that will need to be upgraded to be compatible + command: ansible-galaxy collection install child_dep.child_dep2:>1.2.2 {{ galaxy_verbosity }} + register: result + failed_when: + - '"child_dep.child_dep2:1.2.3 was installed successfully" not in result.stdout_lines' + +- name: install the parent without deps to test dep reconciliation during upgrade + command: ansible-galaxy collection install parent_dep.parent_collection:0.0.1 {{ galaxy_verbosity }} + register: result + failed_when: + - '"parent_dep.parent_collection:0.0.1 was installed successfully" not in result.stdout_lines' + +- name: test upgrading the parent collection and dependencies + command: ansible-galaxy collection install parent_dep.parent_collection:>=1.0.0,<1.1.0 {{ galaxy_verbosity }} + register: result + +- name: check the MANIFEST.json to verify the versions + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ item.namespace }}/{{ item.name }}/MANIFEST.json' + register: metadata + loop: + - namespace: parent_dep + name: parent_collection + - namespace: child_dep + name: child_collection + - namespace: child_dep + name: child_dep2 + +- assert: + that: + - '"parent_dep.parent_collection:1.0.0 was installed successfully" in result.stdout_lines' + - (metadata.results[0].content | b64decode | from_json).collection_info.version == '1.0.0' + - '"child_dep.child_collection:0.9.9 was installed successfully" in result.stdout_lines' + - (metadata.results[1].content | b64decode | from_json).collection_info.version == '0.9.9' + - '"child_dep.child_dep2:1.2.2 was installed successfully" in result.stdout_lines' + - (metadata.results[2].content | b64decode | from_json).collection_info.version == '1.2.2' + +- name: test upgrading a collection only upgrades dependencies if necessary + command: ansible-galaxy collection install parent_dep.parent_collection:1.1.0 {{ galaxy_verbosity }} + register: result + +- name: check the MANIFEST.json to verify the versions + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ item.namespace }}/{{ item.name }}/MANIFEST.json' + register: metadata + loop: + - namespace: parent_dep + name: parent_collection + - namespace: child_dep + name: child_collection + - namespace: child_dep + name: child_dep2 + +- assert: + that: + - '"parent_dep.parent_collection:1.1.0 was installed successfully" in result.stdout_lines' + - (metadata.results[0].content | b64decode | from_json).collection_info.version == '1.1.0' + - "\"Skipping 'child_dep.child_collection:0.9.9' as it is already installed\" in result.stdout_lines" + - (metadata.results[1].content | b64decode | from_json).collection_info.version == '0.9.9' + - "\"Skipping 'child_dep.child_dep2:1.2.2' as it is already installed\" in result.stdout_lines" + - (metadata.results[2].content | b64decode | from_json).collection_info.version == '1.2.2' + +##### Updating collections with --upgrade + +# No deps + +- name: reset installation directory + file: + state: "{{ item }}" + path: "{{ galaxy_dir }}/ansible_collections" + loop: + - absent + - directory + +- name: install a collection + 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' + +- name: install a new version of the collection with --upgrade + command: ansible-galaxy collection install namespace1.name1:<=0.0.5 --upgrade {{ galaxy_verbosity }} + register: result + +- name: check the MANIFEST.json to verify the version + slurp: + path: '{{ galaxy_dir }}/ansible_collections/namespace1/name1/MANIFEST.json' + register: metadata + +- assert: + that: + - '"namespace1.name1:0.0.5 was installed successfully" in result.stdout_lines' + - (metadata.content | b64decode | from_json).collection_info.version == '0.0.5' + +- name: upgrade the collection + command: ansible-galaxy collection install namespace1.name1:<0.0.7 -U {{ galaxy_verbosity }} + register: result + +- assert: + that: '"namespace1.name1:0.0.6 was installed successfully" in result.stdout_lines' + +- name: upgrade the collection to the last version excluding prereleases + command: ansible-galaxy collection install namespace1.name1 -U {{ galaxy_verbosity }} + register: result + +- assert: + that: '"namespace1.name1:1.0.9 was installed successfully" in result.stdout_lines' + +- name: upgrade the collection to the latest available version including prereleases + command: ansible-galaxy collection install namespace1.name1 --pre -U {{ galaxy_verbosity }} + register: result + +- assert: + that: '"namespace1.name1:1.1.0-beta.1 was installed successfully" in result.stdout_lines' + +- name: run the same command again + command: ansible-galaxy collection install namespace1.name1 --pre -U {{ galaxy_verbosity }} + register: result + +- assert: + that: "\"Skipping 'namespace1.name1:1.1.0-beta.1' as it is already installed\" in result.stdout" + +# With deps + +- name: reset installation directory + file: + state: "{{ item }}" + path: "{{ galaxy_dir }}/ansible_collections" + loop: + - absent + - directory + +- name: install a parent collection and a particular version of its dependency + command: ansible-galaxy collection install parent_dep.parent_collection:1.0.0 child_dep.child_collection:0.5.0 {{ galaxy_verbosity }} + register: result + failed_when: + - '"parent_dep.parent_collection:1.0.0 was installed successfully" not in result.stdout_lines' + - '"child_dep.child_collection:0.5.0 was installed successfully" not in result.stdout_lines' + +- name: upgrade the parent and child - the child's new version has a dependency that should be installed + command: ansible-galaxy collection install parent_dep.parent_collection -U {{ galaxy_verbosity }} + register: result + +- name: check the MANIFEST.json to verify the versions + slurp: + path: '{{ galaxy_dir }}/ansible_collections/{{ item.namespace }}/{{ item.name }}/MANIFEST.json' + register: metadata + loop: + - namespace: parent_dep + name: parent_collection + - namespace: child_dep + name: child_collection + - namespace: child_dep + name: child_dep2 + +- assert: + that: + - '"parent_dep.parent_collection:2.0.0 was installed successfully" in result.stdout_lines' + - (metadata.results[0].content | b64decode | from_json).collection_info.version == '2.0.0' + - '"child_dep.child_collection:1.0.0 was installed successfully" in result.stdout_lines' + - (metadata.results[1].content | b64decode | from_json).collection_info.version == '1.0.0' + - '"child_dep.child_dep2:1.2.2 was installed successfully" in result.stdout_lines' + - (metadata.results[2].content | b64decode | from_json).collection_info.version == '1.2.2' + +# Test using a requirements.yml file for upgrade + +- name: reset installation directory + file: + state: "{{ item }}" + path: "{{ galaxy_dir }}/ansible_collections" + loop: + - absent + - directory + +- name: install upgradeable collections + command: ansible-galaxy collection install namespace1.name1:1.0.0 parent_dep.parent_collection:0.0.1 {{ galaxy_verbosity }} + register: result + failed_when: + - '"namespace1.name1:1.0.0 was installed successfully" not in result.stdout_lines' + - '"parent_dep.parent_collection:0.0.1 was installed successfully" not in result.stdout_lines' + - '"child_dep.child_collection:0.4.0 was installed successfully" not in result.stdout_lines' + +- name: create test requirements file with both roles and collections - {{ test_name }} + copy: + content: | + collections: + - namespace1.name1 + - name: parent_dep.parent_collection + version: <=2.0.0 + roles: + - skip.me + dest: '{{ galaxy_dir }}/ansible_collections/requirements.yml' + +- name: upgrade collections with the requirements.yml + command: ansible-galaxy collection install -r {{ requirements_path }} --upgrade {{ galaxy_verbosity }} + vars: + requirements_path: '{{ galaxy_dir }}/ansible_collections/requirements.yml' + register: result + +- assert: + that: + - '"namespace1.name1:1.0.9 was installed successfully" in result.stdout_lines' + - '"parent_dep.parent_collection:2.0.0 was installed successfully" in result.stdout_lines' + - '"child_dep.child_collection:1.0.0 was installed successfully" in result.stdout_lines' + - '"child_dep.child_dep2:1.2.2 was installed successfully" in result.stdout_lines' + +- name: cleanup + file: + state: "{{ item }}" + path: "{{ galaxy_dir }}/ansible_collections" + loop: + - absent + - directory diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/verify.yml b/test/integration/targets/ansible-galaxy-collection/tasks/verify.yml new file mode 100644 index 00000000..eacb8d6b --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/verify.yml @@ -0,0 +1,277 @@ +- name: create an empty collection skeleton + command: ansible-galaxy collection init ansible_test.verify + args: + chdir: '{{ galaxy_dir }}/scratch' + +- name: build the collection + command: ansible-galaxy collection build scratch/ansible_test/verify + args: + chdir: '{{ galaxy_dir }}' + +- name: publish collection - {{ test_name }} + command: ansible-galaxy collection publish ansible_test-verify-1.0.0.tar.gz -s {{ test_name }} {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + +- name: test verifying a tarfile + command: ansible-galaxy collection verify {{ galaxy_dir }}/ansible_test-verify-1.0.0.tar.gz + register: verify + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - >- + "ERROR! 'file' type is not supported. The format namespace.name is expected." in verify.stderr + +- name: install the collection from the server + command: ansible-galaxy collection install ansible_test.verify:1.0.0 + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + +- name: verify the installed collection against the server + command: ansible-galaxy collection verify ansible_test.verify:1.0.0 + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + register: verify + +- assert: + that: + - verify is success + - "'Collection ansible_test.verify contains modified content' not in verify.stdout" + +- name: verify the installed collection against the server, with unspecified version in CLI + command: ansible-galaxy collection verify ansible_test.verify + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + +- name: verify a collection that doesn't appear to be installed + command: ansible-galaxy collection verify ansible_test.verify:1.0.0 + register: verify + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - "'Collection ansible_test.verify is not installed in any of the collection paths.' in verify.stderr" + +- name: create a modules directory + file: + state: directory + path: '{{ galaxy_dir }}/scratch/ansible_test/verify/plugins/modules' + +- name: add a module to the collection + copy: + src: test_module.py + dest: '{{ galaxy_dir }}/scratch/ansible_test/verify/plugins/modules/test_module.py' + +- name: update the collection version + lineinfile: + regexp: "version: .*" + line: "version: '2.0.0'" + path: '{{ galaxy_dir }}/scratch/ansible_test/verify/galaxy.yml' + +- name: build the new version + command: ansible-galaxy collection build scratch/ansible_test/verify + args: + chdir: '{{ galaxy_dir }}' + +- name: publish the new version + command: ansible-galaxy collection publish ansible_test-verify-2.0.0.tar.gz -s {{ test_name }} {{ galaxy_verbosity }} + args: + chdir: '{{ galaxy_dir }}' + +- name: verify a version of a collection that isn't installed + command: ansible-galaxy collection verify ansible_test.verify:2.0.0 + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + register: verify + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - '"ansible_test.verify has the version ''1.0.0'' but is being compared to ''2.0.0''" in verify.stdout' + +- name: install the new version from the server + command: ansible-galaxy collection install ansible_test.verify:2.0.0 --force + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + +- name: verify the installed collection against the server + command: ansible-galaxy collection verify ansible_test.verify:2.0.0 + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + register: verify + +- assert: + that: + - "'Collection ansible_test.verify contains modified content' not in verify.stdout" + +# Test a modified collection + +- set_fact: + manifest_path: '{{ galaxy_dir }}/ansible_collections/ansible_test/verify/MANIFEST.json' + file_manifest_path: '{{ galaxy_dir }}/ansible_collections/ansible_test/verify/FILES.json' + module_path: '{{ galaxy_dir }}/ansible_collections/ansible_test/verify/plugins/modules/test_module.py' + +- name: load the FILES.json + set_fact: + files_manifest: "{{ lookup('file', file_manifest_path) | from_json }}" + +- name: get the real checksum of a particular module + stat: + path: "{{ module_path }}" + checksum_algorithm: sha256 + register: file + +- assert: + that: + - "file.stat.checksum == item.chksum_sha256" + loop: "{{ files_manifest.files }}" + when: "item.name == 'plugins/modules/aws_s3.py'" + +- name: append a newline to the module to modify the checksum + shell: "echo '' >> {{ module_path }}" + +- name: get the new checksum + stat: + path: "{{ module_path }}" + checksum_algorithm: sha256 + register: updated_file + +- assert: + that: + - "updated_file.stat.checksum != file.stat.checksum" + +- name: test verifying checksumes of the modified collection + command: ansible-galaxy collection verify ansible_test.verify:2.0.0 + register: verify + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - "'Collection ansible_test.verify contains modified content in the following files:\n plugins/modules/test_module.py' in verify.stdout" + +- name: modify the FILES.json to match the new checksum + lineinfile: + path: "{{ file_manifest_path }}" + regexp: ' "chksum_sha256": "{{ file.stat.checksum }}",' + line: ' "chksum_sha256": "{{ updated_file.stat.checksum }}",' + state: present + diff: true + +- name: ensure a modified FILES.json is validated + command: ansible-galaxy collection verify ansible_test.verify:2.0.0 + register: verify + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - "'Collection ansible_test.verify contains modified content in the following files:\n FILES.json' in verify.stdout" + +- name: get the checksum of the FILES.json + stat: + path: "{{ file_manifest_path }}" + checksum_algorithm: sha256 + register: manifest_info + +- name: modify the MANIFEST.json to contain a different checksum for FILES.json + lineinfile: + regexp: ' "chksum_sha256": *' + path: "{{ manifest_path }}" + line: ' "chksum_sha256": "{{ manifest_info.stat.checksum }}",' + +- name: ensure the MANIFEST.json is validated against the uncorrupted file from the server + command: ansible-galaxy collection verify ansible_test.verify:2.0.0 + register: verify + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - "'Collection ansible_test.verify contains modified content in the following files:\n MANIFEST.json' in verify.stdout" + +- name: remove the artifact metadata to test verifying a collection without it + file: + path: "{{ item }}" + state: absent + loop: + - "{{ manifest_path }}" + - "{{ file_manifest_path }}" + +- name: add some development metadata + copy: + content: | + namespace: 'ansible_test' + name: 'verify' + version: '2.0.0' + readme: 'README.md' + authors: ['Ansible'] + dest: '{{ galaxy_dir }}/ansible_collections/ansible_test/verify/galaxy.yml' + +- name: test we only verify collections containing a MANIFEST.json with the version on the server + command: ansible-galaxy collection verify ansible_test.verify:2.0.0 + register: verify + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - "'Collection ansible_test.verify does not have a MANIFEST.json' in verify.stderr" + +- name: update the collection version to something not present on the server + lineinfile: + regexp: "version: .*" + line: "version: '3.0.0'" + path: '{{ galaxy_dir }}/scratch/ansible_test/verify/galaxy.yml' + +- name: build the new version + command: ansible-galaxy collection build scratch/ansible_test/verify + args: + chdir: '{{ galaxy_dir }}' + +- name: force-install from local artifact + command: ansible-galaxy collection install '{{ galaxy_dir }}/ansible_test-verify-3.0.0.tar.gz' --force + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + +- name: verify locally only, no download or server manifest hash check + command: ansible-galaxy collection verify --offline ansible_test.verify + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + register: verify + +- assert: + that: + - >- + "Verifying 'ansible_test.verify:3.0.0'." in verify.stdout + - '"MANIFEST.json hash: " in verify.stdout' + - >- + "Successfully verified that checksums for 'ansible_test.verify:3.0.0' are internally consistent with its manifest." in verify.stdout + +- name: append a newline to a module to modify the checksum + shell: "echo '' >> {{ module_path }}" + +- name: verify modified collection locally-only (should fail) + command: ansible-galaxy collection verify --offline ansible_test.verify + register: verify + environment: + ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' + failed_when: verify.rc == 0 + +- assert: + that: + - verify.rc != 0 + - "'Collection ansible_test.verify contains modified content in the following files:' in verify.stdout" + - "'plugins/modules/test_module.py' in verify.stdout" 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 74d36aac..62f3dcf9 100644 --- a/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2 +++ b/test/integration/targets/ansible-galaxy-collection/templates/ansible.cfg.j2 @@ -1,10 +1,25 @@ [galaxy] -server_list=galaxy,automation_hub +# Ensures subsequent unstable reruns don't use the cached information causing another failure +cache_dir={{ remote_tmp_dir }}/galaxy_cache +server_list=pulp_v2,pulp_v3,galaxy_ng,secondary -[galaxy_server.galaxy] -url={{ fallaxy_galaxy_server }} -token={{ fallaxy_token }} +[galaxy_server.pulp_v2] +url={{ pulp_server }}published/api/ +username={{ pulp_user }} +password={{ pulp_password }} -[galaxy_server.automation_hub] -url={{ fallaxy_ah_server }} -token={{ fallaxy_token }} +[galaxy_server.pulp_v3] +url={{ pulp_server }}published/api/ +v3=true +username={{ pulp_user }} +password={{ pulp_password }} + +[galaxy_server.galaxy_ng] +url={{ galaxy_ng_server }} +token={{ galaxy_ng_token.json.token }} + +[galaxy_server.secondary] +url={{ pulp_server }}secondary/api/ +v3=true +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 bc006ca5..e8ee9ca0 100644 --- a/test/integration/targets/ansible-galaxy-collection/vars/main.yml +++ b/test/integration/targets/ansible-galaxy-collection/vars/main.yml @@ -1 +1,150 @@ galaxy_verbosity: "{{ '' if not ansible_verbosity else '-' ~ ('v' * ansible_verbosity) }}" + +pulp_repositories: + - published + - secondary + +publish_namespaces: + - ansible_test + +collection_list: + # Scenario to test out pre-release being ignored unless explicitly set and version pagination. + - namespace: namespace1 + name: name1 + version: 0.0.1 + - namespace: namespace1 + name: name1 + version: 0.0.2 + - namespace: namespace1 + name: name1 + version: 0.0.3 + - namespace: namespace1 + name: name1 + version: 0.0.4 + - namespace: namespace1 + name: name1 + version: 0.0.5 + - namespace: namespace1 + name: name1 + version: 0.0.6 + - namespace: namespace1 + name: name1 + version: 0.0.7 + - namespace: namespace1 + name: name1 + version: 0.0.8 + - namespace: namespace1 + name: name1 + version: 0.0.9 + - namespace: namespace1 + name: name1 + version: 0.0.10 + - namespace: namespace1 + name: name1 + version: 0.1.0 + - namespace: namespace1 + name: name1 + version: 1.0.0 + - namespace: namespace1 + name: name1 + version: 1.0.9 + - namespace: namespace1 + name: name1 + version: 1.1.0-beta.1 + + # Pad out number of namespaces for pagination testing + - namespace: namespace2 + name: name + - namespace: namespace3 + name: name + - namespace: namespace4 + name: name + - namespace: namespace5 + name: name + - namespace: namespace6 + name: name + - namespace: namespace7 + name: name + - namespace: namespace8 + name: name + - namespace: namespace9 + name: name + + # Complex dependency resolution + - namespace: parent_dep + name: parent_collection + version: 0.0.1 + dependencies: + child_dep.child_collection: '<0.5.0' + - namespace: parent_dep + name: parent_collection + version: 1.0.0 + dependencies: + child_dep.child_collection: '>=0.5.0,<1.0.0' + - namespace: parent_dep + name: parent_collection + version: 1.1.0 + dependencies: + child_dep.child_collection: '>=0.9.9,<=1.0.0' + - namespace: parent_dep + name: parent_collection + version: 2.0.0 + dependencies: + child_dep.child_collection: '>=1.0.0' + - namespace: parent_dep2 + name: parent_collection + dependencies: + child_dep.child_collection: '0.5.0' + - namespace: child_dep + name: child_collection + version: 0.4.0 + - namespace: child_dep + name: child_collection + version: 0.5.0 + - namespace: child_dep + name: child_collection + version: 0.9.9 + dependencies: + child_dep.child_dep2: '!=1.2.3' + - namespace: child_dep + name: child_collection + version: 1.0.0 + dependencies: + child_dep.child_dep2: '!=1.2.3' + - namespace: child_dep + name: child_dep2 + version: 1.2.2 + - namespace: child_dep + name: child_dep2 + version: 1.2.3 + + # Dep resolution failure + - namespace: fail_namespace + name: fail_collection + version: 2.1.2 + dependencies: + fail_dep.name: '0.0.5' + fail_dep2.name: '<0.0.5' + - namespace: fail_dep + name: name + version: '0.0.5' + dependencies: + fail_dep2.name: '>0.0.5' + - namespace: fail_dep2 + name: name + + # Symlink tests + - namespace: symlink + name: symlink + use_symlink: yes + + # Caching update tests + - namespace: cache + name: cache + version: 1.0.0 + + # Dep with beta version + - namespace: dep_with_beta + name: parent + dependencies: + namespace1.name1: '*' diff --git a/test/integration/targets/ansible-galaxy-role/aliases b/test/integration/targets/ansible-galaxy-role/aliases new file mode 100644 index 00000000..62548acd --- /dev/null +++ b/test/integration/targets/ansible-galaxy-role/aliases @@ -0,0 +1,2 @@ +shippable/posix/group4 +skip/python2.6 # build uses tarfile with features not available until 2.7 diff --git a/test/integration/targets/ansible-galaxy-role/meta/main.yml b/test/integration/targets/ansible-galaxy-role/meta/main.yml new file mode 100644 index 00000000..e6bc9ccb --- /dev/null +++ b/test/integration/targets/ansible-galaxy-role/meta/main.yml @@ -0,0 +1 @@ +dependencies: [setup_remote_tmp_dir] diff --git a/test/integration/targets/ansible-galaxy-role/tasks/main.yml b/test/integration/targets/ansible-galaxy-role/tasks/main.yml new file mode 100644 index 00000000..e49e4e29 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-role/tasks/main.yml @@ -0,0 +1,58 @@ +- name: Archive directories + file: + state: directory + path: "{{ remote_tmp_dir }}/role.d/{{item}}" + loop: + - meta + - tasks + +- name: Metadata file + copy: + content: "'galaxy_info': {}" + dest: "{{ remote_tmp_dir }}/role.d/meta/main.yml" + +- name: Valid files + copy: + content: "" + dest: "{{ remote_tmp_dir }}/role.d/tasks/{{item}}" + loop: + - "main.yml" + - "valid~file.yml" + +- name: Valid role archive + command: "tar cf {{ remote_tmp_dir }}/valid-role.tar {{ remote_tmp_dir }}/role.d" + +- name: Invalid file + copy: + content: "" + dest: "{{ remote_tmp_dir }}/role.d/tasks/~invalid.yml" + +- name: Valid requirements file + copy: + dest: valid-requirements.yml + content: "[{'src': '{{ remote_tmp_dir }}/valid-role.tar', 'name': 'valid-testrole'}]" + +- name: Invalid role archive + command: "tar cf {{ remote_tmp_dir }}/invalid-role.tar {{ remote_tmp_dir }}/role.d" + +- name: Invalid requirements file + copy: + dest: invalid-requirements.yml + content: "[{'src': '{{ remote_tmp_dir }}/invalid-role.tar', 'name': 'invalid-testrole'}]" + +- name: Install valid role + command: ansible-galaxy install -r valid-requirements.yml + +- name: Uninstall valid role + command: ansible-galaxy role remove valid-testrole + +- name: Install invalid role + command: ansible-galaxy install -r invalid-requirements.yml + ignore_errors: yes + register: invalid + +- assert: + that: "invalid.rc != 0" + +- name: Uninstall invalid role + command: ansible-galaxy role remove invalid-testrole diff --git a/test/integration/targets/ansible-galaxy/runme.sh b/test/integration/targets/ansible-galaxy/runme.sh index 22587001..1f2d56b3 100755 --- a/test/integration/targets/ansible-galaxy/runme.sh +++ b/test/integration/targets/ansible-galaxy/runme.sh @@ -273,6 +273,13 @@ f_ansible_galaxy_status \ ansible-galaxy role info -p ./testroles --offline testdesc | tee out.txt grep 'description: Top level' out.txt + # test multiple role listing + ansible-galaxy role init otherrole --init-path ./testroles + ansible-galaxy role info -p ./testroles --offline testdesc otherrole | tee out.txt + grep 'Role: testdesc' out.txt + grep 'Role: otherrole' out.txt + + popd # ${role_testdir} rm -fr "${role_testdir}" diff --git a/test/integration/targets/ansible-galaxy/setup.yml b/test/integration/targets/ansible-galaxy/setup.yml index a82d02ae..ebd5a1c0 100644 --- a/test/integration/targets/ansible-galaxy/setup.yml +++ b/test/integration/targets/ansible-galaxy/setup.yml @@ -3,7 +3,7 @@ - name: install git package: name: git - when: ansible_distribution != "MacOSX" + when: ansible_distribution not in ["MacOSX", "Alpine"] register: git_install - name: save install result copy: diff --git a/test/integration/targets/ansible-inventory/aliases b/test/integration/targets/ansible-inventory/aliases new file mode 100644 index 00000000..70a7b7a9 --- /dev/null +++ b/test/integration/targets/ansible-inventory/aliases @@ -0,0 +1 @@ +shippable/posix/group5 diff --git a/test/integration/targets/ansible-inventory/files/unicode.yml b/test/integration/targets/ansible-inventory/files/unicode.yml new file mode 100644 index 00000000..ff95db0d --- /dev/null +++ b/test/integration/targets/ansible-inventory/files/unicode.yml @@ -0,0 +1,3 @@ +all: + hosts: + příbor: diff --git a/test/integration/targets/ansible-inventory/tasks/main.yml b/test/integration/targets/ansible-inventory/tasks/main.yml new file mode 100644 index 00000000..0ab09c07 --- /dev/null +++ b/test/integration/targets/ansible-inventory/tasks/main.yml @@ -0,0 +1,79 @@ +- name: "test json output with unicode characters" + command: ansible-inventory --list -i {{ role_path }}/files/unicode.yml + register: result + +- assert: + that: + - result is succeeded + - result.stdout is contains('příbor') + +- block: + - name: "test json output file with unicode characters" + command: ansible-inventory --list --output unicode_inventory.json -i {{ role_path }}/files/unicode.yml + + - set_fact: + json_inventory_file: "{{ lookup('file', 'unicode_inventory.json') | string }}" + + - assert: + that: + - json_inventory_file is contains('příbor') + always: + - file: + name: unicode_inventory.json + state: absent + +- name: "test yaml output with unicode characters" + command: ansible-inventory --list --yaml -i {{ role_path }}/files/unicode.yml + register: result + +- assert: + that: + - result is succeeded + - result.stdout is contains('příbor') + +- block: + - name: "test yaml output file with unicode characters" + command: ansible-inventory --list --yaml --output unicode_inventory.yaml -i {{ role_path }}/files/unicode.yml + + - set_fact: + yaml_inventory_file: "{{ lookup('file', 'unicode_inventory.yaml') | string }}" + + - assert: + that: + - yaml_inventory_file is contains('příbor') + always: + - file: + name: unicode_inventory.yaml + state: absent + +- block: + - name: Install toml package + pip: + name: + - toml + state: present + + - name: "test toml output with unicode characters" + command: ansible-inventory --list --toml -i {{ role_path }}/files/unicode.yml + register: result + + - assert: + that: + - result is succeeded + - result.stdout is contains('příbor') + + - block: + - name: "test toml output file with unicode characters" + command: ansible-inventory --list --toml --output unicode_inventory.toml -i {{ role_path }}/files/unicode.yml + + - set_fact: + toml_inventory_file: "{{ lookup('file', 'unicode_inventory.toml') | string }}" + + - assert: + that: + - toml_inventory_file is contains('příbor') + always: + - file: + name: unicode_inventory.toml + state: absent + when: ansible_python.version.major|int == 3 diff --git a/test/integration/targets/ansible-runner/aliases b/test/integration/targets/ansible-runner/aliases index ec9eb3af..42d2022b 100644 --- a/test/integration/targets/ansible-runner/aliases +++ b/test/integration/targets/ansible-runner/aliases @@ -1,5 +1,5 @@ shippable/posix/group3 -skip/python3 +skip/python2 # ansible-runner is for controller and deprecated python2 support skip/aix skip/osx skip/macos diff --git a/test/integration/targets/ansible-runner/files/adhoc_example1.py b/test/integration/targets/ansible-runner/files/adhoc_example1.py index 3e0d8414..ab24bcad 100644 --- a/test/integration/targets/ansible-runner/files/adhoc_example1.py +++ b/test/integration/targets/ansible-runner/files/adhoc_example1.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import json import os import sys diff --git a/test/integration/targets/ansible-runner/files/playbook_example1.py b/test/integration/targets/ansible-runner/files/playbook_example1.py index 83cb19ff..550c1857 100644 --- a/test/integration/targets/ansible-runner/files/playbook_example1.py +++ b/test/integration/targets/ansible-runner/files/playbook_example1.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import json import os import sys diff --git a/test/integration/targets/ansible-runner/tasks/adhoc_example1.yml b/test/integration/targets/ansible-runner/tasks/adhoc_example1.yml index c6fdf03f..ce174f14 100644 --- a/test/integration/targets/ansible-runner/tasks/adhoc_example1.yml +++ b/test/integration/targets/ansible-runner/tasks/adhoc_example1.yml @@ -1,7 +1,5 @@ - name: execute the script command: "'{{ ansible_python_interpreter }}' '{{ role_path }}/files/adhoc_example1.py' '{{ lookup('env', 'OUTPUT_DIR') }}'" - environment: - AWX_LIB_DIRECTORY: "{{ callback_path }}" register: script - name: parse script output diff --git a/test/integration/targets/ansible-runner/tasks/playbook_example1.yml b/test/integration/targets/ansible-runner/tasks/playbook_example1.yml index ec1f7cda..52df8458 100644 --- a/test/integration/targets/ansible-runner/tasks/playbook_example1.yml +++ b/test/integration/targets/ansible-runner/tasks/playbook_example1.yml @@ -1,7 +1,5 @@ - name: execute the script command: "'{{ ansible_python_interpreter }}' '{{ role_path }}/files/playbook_example1.py' '{{ lookup('env', 'OUTPUT_DIR') }}'" - environment: - AWX_LIB_DIRECTORY: "{{ callback_path }}" register: script - name: parse script output diff --git a/test/integration/targets/ansible-runner/tasks/setup.yml b/test/integration/targets/ansible-runner/tasks/setup.yml index ea24ced5..d3a4606a 100644 --- a/test/integration/targets/ansible-runner/tasks/setup.yml +++ b/test/integration/targets/ansible-runner/tasks/setup.yml @@ -1,19 +1,6 @@ -- name: Install docutils - pip: - name: docutils - - name: Install ansible-runner pip: name: ansible-runner - version: 1.2.0 + version: 1.4.6 extra_args: -c {{ role_path }}/files/constraints.txt - -- name: Find location of ansible-runner installation - command: "'{{ ansible_python_interpreter }}' -c 'import os, ansible_runner; print(os.path.dirname(ansible_runner.__file__))'" - register: ansible_runner_path - -# work around for https://github.com/ansible/ansible-runner/issues/132 -- name: Set callback path to work around ansible-runner bug - set_fact: - callback_path: ":{{ ansible_runner_path.stdout }}/callbacks" diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/meta/runtime.yml b/test/integration/targets/ansible-test/ansible_collections/ns/col/meta/runtime.yml index 1ac15484..fee22ad8 100644 --- a/test/integration/targets/ansible-test/ansible_collections/ns/col/meta/runtime.yml +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/meta/runtime.yml @@ -1,3 +1,4 @@ +requires_ansible: '>=2.11' # force ansible-doc to check the Ansible version (requires packaging) plugin_routing: modules: hi: diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/filter/check_pylint.py b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/filter/check_pylint.py index 359fbf07..f1be4f34 100644 --- a/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/filter/check_pylint.py +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/filter/check_pylint.py @@ -18,4 +18,6 @@ result = {None: None}[{}.get('something')] # pylint 2.3.1 and 2.4.4 report the following error but 2.5.0 and 2.6.0 do not # blacklisted-name: Black listed name "foo" # see: https://github.com/PyCQA/pylint/issues/3701 +# regression: documented as a known issue and removed from ignore.txt so pylint can be upgraded to 2.6.0 +# if future versions of pylint fix this issue then the ignore should be restored foo = {}.keys() diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/lookup/bad.py b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/lookup/bad.py new file mode 100644 index 00000000..580f9d87 --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/lookup/bad.py @@ -0,0 +1,31 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +name: bad +short_description: Bad lookup +description: A bad lookup. +author: + - Ansible Core Team +''' + +EXAMPLES = ''' +- debug: + msg: "{{ lookup('ns.col.bad') }}" +''' + +RETURN = ''' # ''' + +from ansible.plugins.lookup import LookupBase +from ansible import constants + +import lxml + + +class LookupModule(LookupBase): + def run(self, terms, variables, **kwargs): + self.set_options(var_options=variables, direct=kwargs) + + return terms diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/lookup/world.py b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/lookup/world.py new file mode 100644 index 00000000..dbb479a7 --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/lookup/world.py @@ -0,0 +1,29 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +name: world +short_description: World lookup +description: A world lookup. +author: + - Ansible Core Team +''' + +EXAMPLES = ''' +- debug: + msg: "{{ lookup('ns.col.world') }}" +''' + +RETURN = ''' # ''' + +from ansible.plugins.lookup import LookupBase +from ansible import constants + + +class LookupModule(LookupBase): + def run(self, terms, variables, **kwargs): + self.set_options(var_options=variables, direct=kwargs) + + return terms diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/random_directory/bad.py b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/random_directory/bad.py new file mode 100644 index 00000000..2e35cf85 --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/random_directory/bad.py @@ -0,0 +1,8 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +# 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 diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/tests/sanity/ignore.txt b/test/integration/targets/ansible-test/ansible_collections/ns/col/tests/sanity/ignore.txt index 079d0161..e1b3f4ca 100644 --- a/test/integration/targets/ansible-test/ansible_collections/ns/col/tests/sanity/ignore.txt +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/tests/sanity/ignore.txt @@ -1,6 +1,6 @@ -plugins/filter/check_pylint.py pylint:blacklisted-name plugins/modules/bad.py import plugins/modules/bad.py pylint:ansible-bad-module-import +plugins/lookup/bad.py import 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/collection-tests/coverage.sh b/test/integration/targets/ansible-test/collection-tests/coverage.sh index 3d01dd4b..033a9836 100755 --- a/test/integration/targets/ansible-test/collection-tests/coverage.sh +++ b/test/integration/targets/ansible-test/collection-tests/coverage.sh @@ -7,7 +7,13 @@ cd "${WORK_DIR}/ansible_collections/ns/col" # rename the sanity ignore file to match the current ansible version and update import ignores with the python version ansible_version="$(python -c 'import ansible.release; print(".".join(ansible.release.__version__.split(".")[:2]))')" -sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt" +if [ "${ANSIBLE_TEST_PYTHON_VERSION}" == "2.6" ]; then + # Non-module/module_utils plugins are not checked on this remote-only Python versions + sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" | grep -v 'plugins/[^m].* import' > "tests/sanity/ignore-${ansible_version}.txt" +else + sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt" +fi +cat "tests/sanity/ignore-${ansible_version}.txt" # common args for all tests common=(--venv --color --truncate 0 "${@}") diff --git a/test/integration/targets/ansible-test/collection-tests/git-common.bash b/test/integration/targets/ansible-test/collection-tests/git-common.bash index 069b157c..5055f2e8 100755 --- a/test/integration/targets/ansible-test/collection-tests/git-common.bash +++ b/test/integration/targets/ansible-test/collection-tests/git-common.bash @@ -3,7 +3,29 @@ set -eux -o pipefail # make sure git is installed -git --version || ansible-playbook collection-tests/install-git.yml -i ../../inventory "$@" +set +e +git --version +gitres=$? +set -e + +if [[ $gitres -ne 0 ]]; then + ansible-playbook collection-tests/install-git.yml -i ../../inventory "$@" +fi + +dir="$(pwd)" + +uninstall_git() { + cd "$dir" + ansible-playbook collection-tests/uninstall-git.yml -i ../../inventory "$@" +} + +# This is kind of a hack. The uninstall playbook has no way to know the result +# of the install playbook to determine if it changed. So instead, we assume +# that if git wasn't found to begin with, it was installed by the playbook and +# and needs to be removed when we exit. +if [[ $gitres -ne 0 ]]; then + trap uninstall_git EXIT +fi # init sub project mkdir "${WORK_DIR}/sub" diff --git a/test/integration/targets/ansible-test/collection-tests/uninstall-git.yml b/test/integration/targets/ansible-test/collection-tests/uninstall-git.yml new file mode 100644 index 00000000..f94caea7 --- /dev/null +++ b/test/integration/targets/ansible-test/collection-tests/uninstall-git.yml @@ -0,0 +1,18 @@ +- hosts: localhost + tasks: + - name: Make sure git is uninstalled + package: + name: git + state: absent + register: git_remove + + # This gets dragged in as a dependency of git on FreeBSD. + # We need to remove it too when done. + - name: remove python37 if necessary + package: + name: python37 + state: absent + when: + - git_remove is changed + - ansible_distribution == 'FreeBSD' + - ansible_python.version.major == 2 diff --git a/test/integration/targets/ansible-test/collection-tests/venv.sh b/test/integration/targets/ansible-test/collection-tests/venv.sh index 862c8ad9..ba0d2628 100755 --- a/test/integration/targets/ansible-test/collection-tests/venv.sh +++ b/test/integration/targets/ansible-test/collection-tests/venv.sh @@ -7,7 +7,13 @@ cd "${WORK_DIR}/ansible_collections/ns/col" # rename the sanity ignore file to match the current ansible version and update import ignores with the python version ansible_version="$(python -c 'import ansible.release; print(".".join(ansible.release.__version__.split(".")[:2]))')" -sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt" +if [ "${ANSIBLE_TEST_PYTHON_VERSION}" == "2.6" ]; then + # Non-module/module_utils plugins are not checked on this remote-only Python versions + sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" | grep -v 'plugins/[^m].* import' > "tests/sanity/ignore-${ansible_version}.txt" +else + sed "s/ import$/ import-${ANSIBLE_TEST_PYTHON_VERSION}/;" < "tests/sanity/ignore.txt" > "tests/sanity/ignore-${ansible_version}.txt" +fi +cat "tests/sanity/ignore-${ansible_version}.txt" # common args for all tests # each test will be run in a separate venv to verify that requirements have been properly specified diff --git a/test/integration/targets/ansible/ansible-testé.cfg b/test/integration/targets/ansible/ansible-testé.cfg index 61a99f48..09af947f 100644 --- a/test/integration/targets/ansible/ansible-testé.cfg +++ b/test/integration/targets/ansible/ansible-testé.cfg @@ -1,2 +1,3 @@ [defaults] remote_user = admin +collections_paths = /tmp/collections diff --git a/test/integration/targets/ansible/callback_plugins/callback_meta.py b/test/integration/targets/ansible/callback_plugins/callback_meta.py new file mode 100644 index 00000000..e19c80f2 --- /dev/null +++ b/test/integration/targets/ansible/callback_plugins/callback_meta.py @@ -0,0 +1,23 @@ +# (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.callback import CallbackBase +import os + + +class CallbackModule(CallbackBase): + CALLBACK_VERSION = 2.0 + CALLBACK_TYPE = 'stdout' + CALLBACK_NAME = 'callback_meta' + + def __init__(self, *args, **kwargs): + super(CallbackModule, self).__init__(*args, **kwargs) + self.wants_implicit_tasks = os.environ.get('CB_WANTS_IMPLICIT', False) + + def v2_playbook_on_task_start(self, task, is_conditional): + if task.implicit: + self._display.display('saw implicit task') + self._display.display(task.get_name()) diff --git a/test/integration/targets/ansible/playbook.yml b/test/integration/targets/ansible/playbook.yml index c38b9060..69c9b2b2 100644 --- a/test/integration/targets/ansible/playbook.yml +++ b/test/integration/targets/ansible/playbook.yml @@ -3,3 +3,6 @@ tasks: - debug: msg: "{{ username }}" + + - name: explicitly refresh inventory + meta: refresh_inventory diff --git a/test/integration/targets/ansible/playbookdir_cfg.ini b/test/integration/targets/ansible/playbookdir_cfg.ini index f4bf8af8..16670c5b 100644 --- a/test/integration/targets/ansible/playbookdir_cfg.ini +++ b/test/integration/targets/ansible/playbookdir_cfg.ini @@ -1,2 +1,2 @@ [defaults] -playbook_dir = /tmp +playbook_dir = /doesnotexist/tmp diff --git a/test/integration/targets/ansible/runme.sh b/test/integration/targets/ansible/runme.sh index 23ae1863..fc79e33e 100755 --- a/test/integration/targets/ansible/runme.sh +++ b/test/integration/targets/ansible/runme.sh @@ -13,23 +13,41 @@ ansible-config dump -c ./ansible-testé.cfg | grep 'DEFAULT_REMOTE_USER([^)]*) = ANSIBLE_REMOTE_USER=administrator ansible-config dump| grep 'DEFAULT_REMOTE_USER([^)]*) = administrator\>' ansible-config list | grep 'DEFAULT_REMOTE_USER' +# Collection +ansible-config view -c ./ansible-testé.cfg | grep 'collections_paths = /tmp/collections' +ansible-config dump -c ./ansible-testé.cfg | grep 'COLLECTIONS_PATHS([^)]*) =' +ANSIBLE_COLLECTIONS_PATHS=/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 ansible-config view -c ./ansible-non-existent.cfg 2> err1.txt || grep -Eq 'ERROR! The provided configuration file is missing or not accessible:' err1.txt || (cat err*.txt; rm -f err1.txt; exit 1) ansible-config view -c ./no-extension 2> err2.txt || grep -q 'Unsupported configuration file extension' err2.txt || (cat err2.txt; rm -f err*.txt; exit 1) rm -f err*.txt # test setting playbook_dir via envvar -ANSIBLE_PLAYBOOK_DIR=/tmp ansible localhost -m debug -a var=playbook_dir | grep '"playbook_dir": "/tmp"' +ANSIBLE_PLAYBOOK_DIR=/doesnotexist/tmp ansible localhost -m debug -a var=playbook_dir | grep '"playbook_dir": "/doesnotexist/tmp"' # test setting playbook_dir via cmdline -ansible localhost -m debug -a var=playbook_dir --playbook-dir=/tmp | grep '"playbook_dir": "/tmp"' +ansible localhost -m debug -a var=playbook_dir --playbook-dir=/doesnotexist/tmp | grep '"playbook_dir": "/doesnotexist/tmp"' # test setting playbook dir via ansible.cfg -env -u ANSIBLE_PLAYBOOK_DIR ANSIBLE_CONFIG=./playbookdir_cfg.ini ansible localhost -m debug -a var=playbook_dir | grep '"playbook_dir": "/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 - +# CB_WANTS_IMPLICIT isn't anything in Ansible itself. +# Our test cb plugin just accepts it. It lets us avoid copypasting the whole +# plugin just for two tests. +CB_WANTS_IMPLICIT=1 ANSIBLE_STDOUT_CALLBACK=callback_meta ANSIBLE_LOAD_CALLBACK_PLUGINS=1 ansible-playbook -i ../../inventory --extra-vars @./vars.yml playbook.yml | grep 'saw implicit task' + +set +e +if ANSIBLE_STDOUT_CALLBACK=callback_meta ANSIBLE_LOAD_CALLBACK_PLUGINS=1 ansible-playbook -i ../../inventory --extra-vars @./vars.yml playbook.yml | grep 'saw implicit task'; then + echo "Callback got implicit task and should not have" + exit 1 +fi +set -e + # Test that no tmp dirs are left behind when running ansible-config TMP_DIR=~/.ansible/tmptest if [[ -d "$TMP_DIR" ]]; then diff --git a/test/integration/targets/apt/tasks/apt.yml b/test/integration/targets/apt/tasks/apt.yml index 68728376..1b2d9206 100644 --- a/test/integration/targets/apt/tasks/apt.yml +++ b/test/integration/targets/apt/tasks/apt.yml @@ -1,13 +1,3 @@ -- name: use python-apt - set_fact: - python_apt: python-apt - when: ansible_python_version is version('3', '<') - -- name: use python3-apt - set_fact: - python_apt: python3-apt - when: ansible_python_version is version('3', '>=') - - name: use Debian mirror set_fact: distro_mirror: http://ftp.debian.org/debian @@ -19,17 +9,14 @@ when: ansible_distribution == 'Ubuntu' # UNINSTALL 'python-apt' -# The `apt` module has the smarts to auto-install `python-apt`. To test, we +# The `apt` module has the smarts to auto-install `python-apt(3)`. To test, we # will first uninstall `python-apt`. -- name: check {{ python_apt }} with dpkg - shell: dpkg -s {{ python_apt }} - register: dpkg_result - ignore_errors: true - -- name: uninstall {{ python_apt }} with apt - apt: pkg={{ python_apt }} state=absent purge=yes +- name: uninstall python-apt with apt + apt: + pkg: [python-apt, python3-apt] + state: absent + purge: yes register: apt_result - when: dpkg_result is successful # In check mode, auto-install of `python-apt` must fail - name: test fail uninstall hello without required apt deps in check mode @@ -47,8 +34,8 @@ - apt_result is failed - '"If run normally this module can auto-install it." in apt_result.msg' -- name: check {{ python_apt }} with dpkg - shell: dpkg -s {{ python_apt }} +- name: check with dpkg + shell: dpkg -s python-apt python3-apt register: dpkg_result ignore_errors: true @@ -74,7 +61,6 @@ - "'changed' in apt_result" - apt_result is not changed - "dpkg_result.rc == 1" - - "'Auto-installing missing dependency without updating cache: {{ python_apt }}' in apt_result.warnings" - name: Test installing fnmatch package apt: @@ -115,9 +101,9 @@ - apt_update_cache_1 is changed - apt_update_cache_2 is not changed -- name: uninstall {{ python_apt }} with apt again +- name: uninstall apt bindings with apt again apt: - pkg: "{{ python_apt }}" + pkg: [python-apt, python3-apt] state: absent purge: yes @@ -140,7 +126,6 @@ - "'changed' in apt_result" - apt_result is not changed - "dpkg_result.rc == 1" - - "'Updating cache and auto-installing missing dependency: {{ python_apt }}' in apt_result.warnings" # UNINSTALL AGAIN - name: uninstall hello with apt @@ -218,6 +203,44 @@ - name: uninstall hello with apt apt: pkg=hello state=absent purge=yes +# INSTALL WITHOUT REMOVALS +- name: Install hello, that conflicts with hello-traditional + apt: + pkg: hello + state: present + update_cache: no + +- name: check hello + shell: dpkg-query -l hello + register: dpkg_result + +- name: verify installation of hello + assert: + that: + - "apt_result.changed" + - "dpkg_result.rc == 0" + +- name: Try installing hello-traditional, that conflicts with hello + apt: + pkg: hello-traditional + state: present + fail_on_autoremove: yes + ignore_errors: yes + register: apt_result + +- name: verify failure of installing hello-traditional, because it is required to remove hello to install. + assert: + that: + - apt_result is failed + - '"Packages need to be removed but remove is disabled." in apt_result.msg' + +- name: uninstall hello with apt + apt: + pkg: hello + state: absent + purge: yes + update_cache: no + - name: install deb file apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ hello_architecture.stdout }}.deb" register: apt_initial diff --git a/test/integration/targets/apt/tasks/main.yml b/test/integration/targets/apt/tasks/main.yml index 1ecd8a63..13d3e4f4 100644 --- a/test/integration/targets/apt/tasks/main.yml +++ b/test/integration/targets/apt/tasks/main.yml @@ -16,18 +16,18 @@ # along with Ansible. If not, see <http://www.gnu.org/licenses/>. - block: - - include: 'apt.yml' + - import_tasks: 'apt.yml' - - include: 'url-with-deps.yml' + - import_tasks: 'url-with-deps.yml' - - include: 'apt-multiarch.yml' + - import_tasks: 'apt-multiarch.yml' when: - ansible_userspace_architecture != apt_foreign_arch - - include: 'apt-builddep.yml' + - import_tasks: 'apt-builddep.yml' - block: - - include: 'repo.yml' + - import_tasks: 'repo.yml' always: - file: path: /etc/apt/sources.list.d/file_tmp_repo.list diff --git a/test/integration/targets/apt/tasks/repo.yml b/test/integration/targets/apt/tasks/repo.yml index f568be9f..e1863f38 100644 --- a/test/integration/targets/apt/tasks/repo.yml +++ b/test/integration/targets/apt/tasks/repo.yml @@ -213,7 +213,11 @@ - name: Upgrades block: - - include: "upgrade.yml aptitude_present={{ True | bool }} upgrade_type=dist force_apt_get={{ False | bool }}" + - import_tasks: "upgrade.yml" + vars: + aptitude_present: "{{ True | bool }}" + upgrade_type: "dist" + force_apt_get: "{{ False | bool }}" - name: Check if aptitude is installed command: dpkg-query --show --showformat='${db:Status-Abbrev}' aptitude @@ -226,7 +230,11 @@ when: - aptitude_status.stdout.find('ii') != -1 - - include: "upgrade.yml aptitude_present={{ False | bool }} upgrade_type={{ item.upgrade_type }} force_apt_get={{ item.force_apt_get }}" + - include_tasks: "upgrade.yml" + vars: + aptitude_present: "{{ False | bool }}" + upgrade_type: "{{ item.upgrade_type }}" + force_apt_get: "{{ item.force_apt_get }}" with_items: - { upgrade_type: safe, force_apt_get: False } - { upgrade_type: full, force_apt_get: False } @@ -238,7 +246,11 @@ pkg: aptitude state: present - - include: "upgrade.yml aptitude_present={{ True | bool }} upgrade_type={{ item.upgrade_type }} force_apt_get={{ item.force_apt_get }}" + - include_tasks: "upgrade.yml" + vars: + aptitude_present: "{{ True | bool }}" + upgrade_type: "{{ item.upgrade_type }}" + force_apt_get: "{{ item.force_apt_get }}" with_items: - { upgrade_type: safe, force_apt_get: False } - { upgrade_type: full, force_apt_get: False } diff --git a/test/integration/targets/apt_key/tasks/apt_key.yml b/test/integration/targets/apt_key/tasks/apt_key.yml index a5969b6f..0e017237 100644 --- a/test/integration/targets/apt_key/tasks/apt_key.yml +++ b/test/integration/targets/apt_key/tasks/apt_key.yml @@ -1,8 +1,14 @@ +- name: Ensure key is not there to start with. + apt_key: + id: 36A1D7869245C8950F966E92D8576A8BA88D21E9 + state: absent + - name: run first docs example apt_key: keyserver: keyserver.ubuntu.com id: 36A1D7869245C8950F966E92D8576A8BA88D21E9 register: apt_key_test0 + - debug: var=apt_key_test0 - name: re-run first docs example diff --git a/test/integration/targets/apt_key/tasks/apt_key_binary.yml b/test/integration/targets/apt_key/tasks/apt_key_binary.yml new file mode 100644 index 00000000..4a351446 --- /dev/null +++ b/test/integration/targets/apt_key/tasks/apt_key_binary.yml @@ -0,0 +1,12 @@ +--- + +- name: Ensure import of binary key downloaded using URLs works + apt_key: + url: https://ansible-ci-files.s3.us-east-1.amazonaws.com/test/integration/targets/apt_key/apt-key-example-binary.gpg + register: apt_key_binary_test + +- name: Validate the results + assert: + that: + - 'apt_key_binary_test.changed is defined' + - 'apt_key_binary_test.changed' diff --git a/test/integration/targets/apt_key/tasks/apt_key_inline_data.yml b/test/integration/targets/apt_key/tasks/apt_key_inline_data.yml new file mode 100644 index 00000000..13174e48 --- /dev/null +++ b/test/integration/targets/apt_key/tasks/apt_key_inline_data.yml @@ -0,0 +1,5 @@ +- name: "Ensure import of a deliberately corrupted downloaded GnuPG binary key results in an 'inline data' occurence in the message" + apt_key: + url: https://ansible-ci-files.s3.us-east-1.amazonaws.com/test/integration/targets/apt_key/apt-key-corrupt-zeros-2k.gpg + register: gpg_inline_result + failed_when: "not ('inline data' in gpg_inline_result.msg)" diff --git a/test/integration/targets/apt_key/tasks/file.yml b/test/integration/targets/apt_key/tasks/file.yml new file mode 100644 index 00000000..16b62736 --- /dev/null +++ b/test/integration/targets/apt_key/tasks/file.yml @@ -0,0 +1,52 @@ +- name: Get Fedora GPG Key + get_url: + url: https://ansible-ci-files.s3.us-east-1.amazonaws.com/test/integration/targets/apt_key/fedora.gpg + dest: /tmp/fedora.gpg + +- name: Ensure clean slate + apt_key: + id: 1161AE6945719A39 + state: absent + +- name: Run apt_key with both file and keyserver + apt_key: + file: /tmp/fedora.gpg + keyserver: keys.gnupg.net + id: 97A1AE57C3A2372CCA3A4ABA6C13026D12C944D0 + register: both_file_keyserver + ignore_errors: true + +- name: Run apt_key with file only + apt_key: + file: /tmp/fedora.gpg + register: only_file + +- name: Run apt_key with keyserver only + apt_key: + keyserver: keys.gnupg.net + id: 97A1AE57C3A2372CCA3A4ABA6C13026D12C944D0 + register: only_keyserver + +- name: validate results + assert: + that: + - 'both_file_keyserver is failed' + - 'only_file.changed' + - 'not only_keyserver.changed' + +- name: remove fedora.gpg + apt_key: + id: 1161AE6945719A39 + state: absent + register: remove_fedora + +- name: add key from url + apt_key: + url: https://ansible-ci-files.s3.us-east-1.amazonaws.com/test/integration/targets/apt_key/fedora.gpg + register: apt_key_url + +- name: verify key from url + assert: + that: + - remove_fedora is changed + - apt_key_url is changed diff --git a/test/integration/targets/apt_key/tasks/main.yml b/test/integration/targets/apt_key/tasks/main.yml index a268b2b9..9ef44e45 100644 --- a/test/integration/targets/apt_key/tasks/main.yml +++ b/test/integration/targets/apt_key/tasks/main.yml @@ -24,5 +24,14 @@ - name: create our testing sub-directory file: path="{{ output_dir_test }}" state=directory -- include: 'apt_key.yml' +- import_tasks: 'apt_key.yml' + when: ansible_distribution in ('Ubuntu', 'Debian') + +- import_tasks: 'apt_key_inline_data.yml' + when: ansible_distribution in ('Ubuntu', 'Debian') + +- import_tasks: 'file.yml' + when: ansible_distribution in ('Ubuntu', 'Debian') + +- import_tasks: 'apt_key_binary.yml' when: ansible_distribution in ('Ubuntu', 'Debian') diff --git a/test/integration/targets/apt_repository/tasks/apt.yml b/test/integration/targets/apt_repository/tasks/apt.yml index 66790bb0..0dc25afd 100644 --- a/test/integration/targets/apt_repository/tasks/apt.yml +++ b/test/integration/targets/apt_repository/tasks/apt.yml @@ -35,7 +35,7 @@ # # TEST: apt_repository: repo=<name> # -- include: 'cleanup.yml' +- import_tasks: 'cleanup.yml' - name: 'record apt cache mtime' stat: path='/var/cache/apt/pkgcache.bin' @@ -67,7 +67,7 @@ # # TEST: apt_repository: repo=<name> update_cache=no # -- include: 'cleanup.yml' +- import_tasks: 'cleanup.yml' - name: 'record apt cache mtime' stat: path='/var/cache/apt/pkgcache.bin' @@ -98,7 +98,7 @@ # # TEST: apt_repository: repo=<name> update_cache=yes # -- include: 'cleanup.yml' +- import_tasks: 'cleanup.yml' - name: 'record apt cache mtime' stat: path='/var/cache/apt/pkgcache.bin' @@ -129,7 +129,7 @@ # # TEST: apt_repository: repo=<spec> # -- include: 'cleanup.yml' +- import_tasks: 'cleanup.yml' - name: 'record apt cache mtime' stat: path='/var/cache/apt/pkgcache.bin' @@ -174,7 +174,7 @@ # # TEST: apt_repository: repo=<spec> filename=<filename> # -- include: 'cleanup.yml' +- import_tasks: 'cleanup.yml' - name: 'record apt cache mtime' stat: path='/var/cache/apt/pkgcache.bin' @@ -240,4 +240,4 @@ # # TEARDOWN # -- include: 'cleanup.yml' +- import_tasks: 'cleanup.yml' diff --git a/test/integration/targets/apt_repository/tasks/main.yml b/test/integration/targets/apt_repository/tasks/main.yml index 41010112..5d72f6f1 100644 --- a/test/integration/targets/apt_repository/tasks/main.yml +++ b/test/integration/targets/apt_repository/tasks/main.yml @@ -16,10 +16,10 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. -- include: 'apt.yml' +- import_tasks: 'apt.yml' when: ansible_distribution in ('Ubuntu') -- include: mode.yaml +- import_tasks: mode.yaml when: ansible_distribution in ('Ubuntu') tags: - - test_apt_repository_mode
\ No newline at end of file + - test_apt_repository_mode diff --git a/test/integration/targets/apt_repository/tasks/mode.yaml b/test/integration/targets/apt_repository/tasks/mode.yaml index d9895368..f363fd8f 100644 --- a/test/integration/targets/apt_repository/tasks/mode.yaml +++ b/test/integration/targets/apt_repository/tasks/mode.yaml @@ -8,7 +8,7 @@ test_repo_spec: "deb http://apt.postgresql.org/pub/repos/apt/ {{ ansible_distribution_release }}-pgdg main" test_repo_path: /etc/apt/sources.list.d/apt_postgresql_org_pub_repos_apt.list -- include: mode_cleanup.yaml +- import_tasks: mode_cleanup.yaml - name: Add GPG key to verify signatures apt_key: @@ -31,7 +31,7 @@ debug: var: mode_given_yaml_literal_0600 -- include: mode_cleanup.yaml +- import_tasks: mode_cleanup.yaml - name: Assert mode_given_yaml_literal_0600 is correct assert: @@ -52,7 +52,7 @@ debug: var: no_mode_stat -- include: mode_cleanup.yaml +- import_tasks: mode_cleanup.yaml - name: Assert no_mode_stat is correct assert: @@ -74,7 +74,7 @@ debug: var: mode_given_string_stat -- include: mode_cleanup.yaml +- import_tasks: mode_cleanup.yaml - name: Mode specified as string 600 apt_repository: @@ -92,7 +92,7 @@ debug: var: mode_given_string_600_stat -- include: mode_cleanup.yaml +- import_tasks: mode_cleanup.yaml - name: Assert mode is correct assert: @@ -114,7 +114,7 @@ debug: var: mode_given_yaml_literal_600 -- include: mode_cleanup.yaml +- import_tasks: mode_cleanup.yaml # a literal 600 as the mode will fail currently, in the sense that it # doesn't guess and consider 600 and 0600 to be the same, and will instead @@ -127,4 +127,4 @@ # See https://github.com/ansible/ansible/issues/16370 - name: Assert mode_given_yaml_literal_600 is correct assert: - that: "mode_given_yaml_literal_600.stat.mode == '1130'"
\ No newline at end of file + that: "mode_given_yaml_literal_600.stat.mode == '1130'" diff --git a/test/integration/targets/argspec/library/argspec.py b/test/integration/targets/argspec/library/argspec.py index 08dad1a0..1a1d288d 100644 --- a/test/integration/targets/argspec/library/argspec.py +++ b/test/integration/targets/argspec/library/argspec.py @@ -126,6 +126,19 @@ def main(): 'int': { 'type': 'int', }, + 'apply_defaults': { + 'type': 'dict', + 'apply_defaults': True, + 'options': { + 'foo': { + 'type': 'str', + }, + 'bar': { + 'type': 'str', + 'default': 'baz', + }, + }, + }, }, required_if=( ('state', 'present', ('path', 'content'), True), diff --git a/test/integration/targets/argspec/tasks/main.yml b/test/integration/targets/argspec/tasks/main.yml index d90bdf02..283c922d 100644 --- a/test/integration/targets/argspec/tasks/main.yml +++ b/test/integration/targets/argspec/tasks/main.yml @@ -342,6 +342,30 @@ register: argspec_int_invalid ignore_errors: true +- argspec: + required: value + required_one_of_one: value + register: argspec_apply_defaults_not_specified + +- argspec: + required: value + required_one_of_one: value + apply_defaults: ~ + register: argspec_apply_defaults_none + +- argspec: + required: value + required_one_of_one: value + apply_defaults: {} + register: argspec_apply_defaults_empty + +- argspec: + required: value + required_one_of_one: value + apply_defaults: + foo: bar + register: argspec_apply_defaults_one + - assert: that: - argspec_required_fail is failed @@ -417,3 +441,8 @@ - argspec_password_no_log.stdout|regex_findall('VALUE_SPECIFIED_IN_NO_LOG_PARAMETER')|length == 1 - argspec_int_invalid is failed + + - "argspec_apply_defaults_not_specified.apply_defaults == {'foo': none, 'bar': 'baz'}" + - "argspec_apply_defaults_none.apply_defaults == {'foo': none, 'bar': 'baz'}" + - "argspec_apply_defaults_empty.apply_defaults == {'foo': none, 'bar': 'baz'}" + - "argspec_apply_defaults_one.apply_defaults == {'foo': 'bar', 'bar': 'baz'}" diff --git a/test/integration/targets/async/callback_test.yml b/test/integration/targets/async/callback_test.yml new file mode 100644 index 00000000..01afd593 --- /dev/null +++ b/test/integration/targets/async/callback_test.yml @@ -0,0 +1,7 @@ +- hosts: localhost + gather_facts: false + tasks: + - name: Async poll callback test + command: sleep 5 + async: 6 + poll: 1 diff --git a/test/integration/targets/async/tasks/main.yml b/test/integration/targets/async/tasks/main.yml index b1925d25..c8c12f6d 100644 --- a/test/integration/targets/async/tasks/main.yml +++ b/test/integration/targets/async/tasks/main.yml @@ -42,6 +42,25 @@ - async_result.finished == 1 - async_result is finished +- name: assert temp async directory exists + stat: + path: "~/.ansible_async" + register: dir_st + +- assert: + that: + - dir_st.stat.isdir is defined and dir_st.stat.isdir + +- name: stat temp async status file + stat: + path: "~/.ansible_async/{{ async_result.ansible_job_id }}" + register: tmp_async_file_st + +- name: validate automatic cleanup of temp async status file on completed run + assert: + that: + - not tmp_async_file_st.stat.exists + - name: test async without polling command: sleep 5 async: 30 @@ -298,3 +317,11 @@ {{ ansible_python_interpreter|default('/usr/bin/python') }} -c 'import os; os.fdopen(os.dup(0), "r")' async: 1 poll: 1 + +- name: run async poll callback test playbook + command: ansible-playbook {{ role_path }}/callback_test.yml + register: callback_output + +- assert: + that: + - '"ASYNC POLL on localhost" in callback_output.stdout' diff --git a/test/integration/targets/async_fail/library/async_test.py b/test/integration/targets/async_fail/library/async_test.py index 838f2f07..e0cbd6fe 100644 --- a/test/integration/targets/async_fail/library/async_test.py +++ b/test/integration/targets/async_fail/library/async_test.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import json import sys import time diff --git a/test/integration/targets/become_unprivileged/action_plugins/tmpdir.py b/test/integration/targets/become_unprivileged/action_plugins/tmpdir.py new file mode 100644 index 00000000..b7cbb7a7 --- /dev/null +++ b/test/integration/targets/become_unprivileged/action_plugins/tmpdir.py @@ -0,0 +1,14 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + result = super(ActionModule, self).run(tmp, task_vars) + result.update(self._execute_module('ping', task_vars=task_vars)) + result['tmpdir'] = self._connection._shell.tmpdir + return result diff --git a/test/integration/targets/become_unprivileged/aliases b/test/integration/targets/become_unprivileged/aliases new file mode 100644 index 00000000..c96617f6 --- /dev/null +++ b/test/integration/targets/become_unprivileged/aliases @@ -0,0 +1,5 @@ +destructive +shippable/posix/group1 +skip/aix +needs/ssh +needs/root diff --git a/test/integration/targets/become_unprivileged/chmod_acl_macos/test.yml b/test/integration/targets/become_unprivileged/chmod_acl_macos/test.yml new file mode 100644 index 00000000..eaa5f5f8 --- /dev/null +++ b/test/integration/targets/become_unprivileged/chmod_acl_macos/test.yml @@ -0,0 +1,26 @@ +- name: Tests for chmod +a ACL functionality on macOS + hosts: ssh + gather_facts: yes + remote_user: unpriv1 + become: yes + become_user: unpriv2 + + tasks: + - name: Get AnsiballZ temp directory + action: tmpdir + register: tmpdir + become_user: unpriv2 + become: yes + + - name: run whoami + command: whoami + register: whoami + + - name: Ensure we used the right fallback + shell: ls -le /var/tmp/ansible*/*_command.py + register: ls + + - assert: + that: + - whoami.stdout == "unpriv2" + - "'user:unpriv2 allow read' in ls.stdout" diff --git a/test/integration/targets/become_unprivileged/cleanup_unpriv_users.yml b/test/integration/targets/become_unprivileged/cleanup_unpriv_users.yml new file mode 100644 index 00000000..8be2fe60 --- /dev/null +++ b/test/integration/targets/become_unprivileged/cleanup_unpriv_users.yml @@ -0,0 +1,53 @@ +- name: Clean up host and remove unprivileged users + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + # Do this first so we can use tilde notation while the user still exists + - name: Delete homedirs + file: + path: '~{{ item }}' + state: absent + with_items: + - unpriv1 + - unpriv2 + + - name: Delete users + user: + name: "{{ item }}" + state: absent + force: yes # I think this is needed in case pipelining is used and the session remains open + with_items: + - unpriv1 + - unpriv2 + + - name: Delete groups + group: + name: "{{ item }}" + state: absent + with_items: + - acommongroup + - unpriv1 + - unpriv2 + + - name: Fix sudoers.d path for FreeBSD + set_fact: + sudoers_etc: /usr/local/etc + when: ansible_distribution == 'FreeBSD' + + - name: Fix sudoers.d path for everything else + set_fact: + sudoers_etc: /etc + when: ansible_distribution != 'FreeBSD' + + - name: Undo OpenSUSE + lineinfile: + path: "{{ sudoers_etc }}/sudoers" + regexp: '^### Defaults targetpw' + line: 'Defaults targetpw' + backrefs: yes + + - name: Nuke custom sudoers file + file: + path: "{{ sudoers_etc }}/sudoers.d/unpriv1" + state: absent diff --git a/test/integration/targets/become_unprivileged/common_remote_group/cleanup.yml b/test/integration/targets/become_unprivileged/common_remote_group/cleanup.yml new file mode 100644 index 00000000..41784fcc --- /dev/null +++ b/test/integration/targets/become_unprivileged/common_remote_group/cleanup.yml @@ -0,0 +1,35 @@ +- name: Cleanup (as root) + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + - name: Remove group for unprivileged users + group: + name: commongroup + state: absent + + - name: Check if /usr/bin/setfacl exists + stat: + path: /usr/bin/setfacl + register: usr_bin_setfacl + + - name: Check if /bin/setfacl exists + stat: + path: /bin/setfacl + register: bin_setfacl + + - name: Set path to setfacl + set_fact: + setfacl_path: /usr/bin/setfacl + when: usr_bin_setfacl.stat.exists + + - name: Set path to setfacl + set_fact: + setfacl_path: /bin/setfacl + when: bin_setfacl.stat.exists + + - name: chmod +x setfacl + file: + path: "{{ setfacl_path }}" + mode: a+x + when: setfacl_path is defined diff --git a/test/integration/targets/become_unprivileged/common_remote_group/setup.yml b/test/integration/targets/become_unprivileged/common_remote_group/setup.yml new file mode 100644 index 00000000..1e799c47 --- /dev/null +++ b/test/integration/targets/become_unprivileged/common_remote_group/setup.yml @@ -0,0 +1,43 @@ +- name: Prep (as root) + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + - name: Create group for unprivileged users + group: + name: commongroup + + - name: Add them to the group + user: + name: "{{ item }}" + groups: commongroup + append: yes + with_items: + - unpriv1 + - unpriv2 + + - name: Check if /usr/bin/setfacl exists + stat: + path: /usr/bin/setfacl + register: usr_bin_setfacl + + - name: Check if /bin/setfacl exists + stat: + path: /bin/setfacl + register: bin_setfacl + + - name: Set path to setfacl + set_fact: + setfacl_path: /usr/bin/setfacl + when: usr_bin_setfacl.stat.exists + + - name: Set path to setfacl + set_fact: + setfacl_path: /bin/setfacl + when: bin_setfacl.stat.exists + + - name: chmod -x setfacl to disable it + file: + path: "{{ setfacl_path }}" + mode: a-x + when: setfacl_path is defined diff --git a/test/integration/targets/become_unprivileged/common_remote_group/test.yml b/test/integration/targets/become_unprivileged/common_remote_group/test.yml new file mode 100644 index 00000000..4bc51f84 --- /dev/null +++ b/test/integration/targets/become_unprivileged/common_remote_group/test.yml @@ -0,0 +1,36 @@ +- name: Tests for ANSIBLE_COMMON_REMOTE_GROUP functionality + hosts: ssh + gather_facts: yes + remote_user: unpriv1 + + tasks: + - name: foo + action: tmpdir + register: tmpdir + become_user: unpriv2 + become: yes + + - name: run whoami with become + command: whoami + register: whoami + become_user: unpriv2 + become: yes + + - set_fact: + stat_cmd: stat -c '%U %G' {{ tmpdir.tmpdir }}/* + when: ansible_distribution not in ['MacOSX', 'FreeBSD'] + + - set_fact: + stat_cmd: stat -f '%Su %Sg' {{ tmpdir.tmpdir }}/* + when: ansible_distribution in ['MacOSX', 'FreeBSD'] + + - name: Ensure we tested the right fallback + shell: "{{ stat_cmd }}" + register: stat + become_user: unpriv2 + become: yes + + - assert: + that: + - whoami.stdout == "unpriv2" + - stat.stdout == 'unpriv1 commongroup' diff --git a/test/integration/targets/become_unprivileged/inventory b/test/integration/targets/become_unprivileged/inventory new file mode 100644 index 00000000..025d8cf9 --- /dev/null +++ b/test/integration/targets/become_unprivileged/inventory @@ -0,0 +1,10 @@ +[ssh] +#ssh-pipelining ansible_ssh_pipelining=true +ssh-no-pipelining ansible_ssh_pipelining=false +[ssh:vars] +ansible_host=localhost +ansible_connection=ssh +ansible_python_interpreter="{{ ansible_playbook_python }}" + +[all:vars] +ansible_python_interpreter="{{ ansible_playbook_python }}"
\ No newline at end of file diff --git a/test/integration/targets/become_unprivileged/runme.sh b/test/integration/targets/become_unprivileged/runme.sh new file mode 100755 index 00000000..7a3f7b87 --- /dev/null +++ b/test/integration/targets/become_unprivileged/runme.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +set -eux + +export ANSIBLE_KEEP_REMOTE_FILES=True +ANSIBLE_ACTION_PLUGINS="$(pwd)/action_plugins" +export ANSIBLE_ACTION_PLUGINS +export ANSIBLE_BECOME_PASS='iWishIWereCoolEnoughForRoot!' + +begin_sandwich() { + ansible-playbook setup_unpriv_users.yml -i inventory -v "$@" +} + +end_sandwich() { + unset ANSIBLE_KEEP_REMOTE_FILES + unset ANSIBLE_COMMON_REMOTE_GROUP + unset ANSIBLE_BECOME_PASS + + # Do a few cleanup tasks (nuke users, groups, and homedirs, undo config changes) + ansible-playbook cleanup_unpriv_users.yml -i inventory -v "$@" + + # We do these last since they do things like remove groups and will error + # if there are still users in them. + for pb in */cleanup.yml; do + ansible-playbook "$pb" -i inventory -v "$@" + done +} + +trap "end_sandwich \"\$@\"" EXIT + +# Common group tests +# Skip on macOS, chmod fallback will take over. +# 1) chmod is stupidly hard to disable, so hitting this test case on macOS would +# be a suuuuuuper edge case scenario +# 2) even if we can trick it so chmod doesn't exist, then other things break. +# Ansible wants a `chmod` around, even if it's not the final thing that gets +# us enough permission to run the task. +if [[ "$OSTYPE" != darwin* ]]; then + begin_sandwich "$@" + ansible-playbook common_remote_group/setup.yml -i inventory -v "$@" + export ANSIBLE_COMMON_REMOTE_GROUP=commongroup + ansible-playbook common_remote_group/test.yml -i inventory -v "$@" + end_sandwich "$@" +fi + +if [[ "$OSTYPE" == darwin* ]]; then + begin_sandwich "$@" + # In the default case this should happen on macOS, so no need for a setup + # It should just work. + ansible-playbook chmod_acl_macos/test.yml -i inventory -v "$@" + end_sandwich "$@" +fi diff --git a/test/integration/targets/become_unprivileged/setup_unpriv_users.yml b/test/integration/targets/become_unprivileged/setup_unpriv_users.yml new file mode 100644 index 00000000..4f677413 --- /dev/null +++ b/test/integration/targets/become_unprivileged/setup_unpriv_users.yml @@ -0,0 +1,109 @@ +#################################################################### +# NOTE! Any destructive changes you make here... Undo them in +# cleanup_become_unprivileged so that they don't affect other tests. +#################################################################### +- name: Set up host and create unprivileged users + hosts: ssh + gather_facts: yes + remote_user: root + tasks: + - name: Create groups for unprivileged users + group: + name: "{{ item }}" + with_items: + - unpriv1 + - unpriv2 + + # MacOS requires unencrypted password + - name: Set password for unpriv1 (MacOSX) + set_fact: + password: 'iWishIWereCoolEnoughForRoot!' + when: ansible_distribution == 'MacOSX' + + - name: Set password for unpriv1 (everything else) + set_fact: + password: $6$CRuKRUfAoVwibjUI$1IEOISMFAE/a0VG73K9QsD0uruXNPLNkZ6xWg4Sk3kZIXwv6.YJLECzfNjn6pu8ay6XlVcj2dUvycLetL5Lgx1 + when: ansible_distribution != 'MacOSX' + + # This user is special. It gets a password so we can sudo as it + # (we set the sudo password in runme.sh) and it gets wheel so it can + # `become` unpriv2 without an overly complex sudoers file. + - name: Create first unprivileged user + user: + name: unpriv1 + group: unpriv1 + password: "{{ password }}" + + - name: Create second unprivileged user + user: + name: unpriv2 + group: unpriv2 + + - name: Special case group add for macOS + user: + name: unpriv1 + groups: com.apple.access_ssh + append: yes + when: ansible_distribution == 'MacOSX' + + - name: Create .ssh for unpriv1 + file: + path: ~unpriv1/.ssh + state: directory + owner: unpriv1 + group: unpriv1 + mode: 0700 + + - name: Set authorized key for unpriv1 + copy: + src: ~root/.ssh/authorized_keys + dest: ~unpriv1/.ssh/authorized_keys + remote_src: yes + owner: unpriv1 + group: unpriv1 + mode: 0600 + + # Without this we get: + # "Failed to connect to the host via ssh: "System is booting up. Unprivileged + # users are not permitted to log in yet. Please come back later." + - name: Nuke /run/nologin + file: + path: /run/nologin + state: absent + + - name: Fix sudoers.d path for FreeBSD + set_fact: + sudoers_etc: /usr/local/etc + when: ansible_distribution == 'FreeBSD' + + - name: Fix sudoers.d path for everything else + set_fact: + sudoers_etc: /etc + when: sudoers_etc is not defined + + - name: Set chown group for bsd and osx + set_fact: + chowngroup: wheel + when: ansible_distribution in ('FreeBSD', 'MacOSX') + + - name: Chown group for everything else + set_fact: + chowngroup: root + when: chowngroup is not defined + + - name: Make it so unpriv1 can sudo (Chapter 1) + copy: + dest: "{{ sudoers_etc }}/sudoers.d/unpriv1" + content: unpriv1 ALL=(ALL) ALL + owner: root + group: "{{ chowngroup }}" + mode: 0644 + + # OpenSUSE has a weird sudo default here and requires the root pw + # instead of the user pw. Undo that setting, we can clean it up later. + - name: Make it so unpriv1 can sudo (Chapter 2 - The Return Of the OpenSUSE) + lineinfile: + dest: "{{ sudoers_etc }}/sudoers" + regexp: '^Defaults targetpw' + line: '### Defaults targetpw' + backrefs: yes diff --git a/test/integration/targets/blockinfile/tasks/add_block_to_existing_file.yml b/test/integration/targets/blockinfile/tasks/add_block_to_existing_file.yml index dbb93ecc..7093ed2b 100644 --- a/test/integration/targets/blockinfile/tasks/add_block_to_existing_file.yml +++ b/test/integration/targets/blockinfile/tasks/add_block_to_existing_file.yml @@ -12,6 +12,11 @@ backup: yes register: blockinfile_test0 +- name: ensure we have a bcackup file + assert: + that: + - "'backup_file' in blockinfile_test0" + - name: check content shell: 'grep -c -e "Match User ansible-agent" -e "PasswordAuthentication no" {{ output_dir_test }}/sshd_config' register: blockinfile_test0_grep diff --git a/test/integration/targets/blocks/69848.yml b/test/integration/targets/blocks/69848.yml new file mode 100644 index 00000000..3b43eebb --- /dev/null +++ b/test/integration/targets/blocks/69848.yml @@ -0,0 +1,5 @@ +- hosts: host1,host2 + gather_facts: no + roles: + - role-69848-1 + - role-69848-2 diff --git a/test/integration/targets/blocks/finalized_task.yml b/test/integration/targets/blocks/finalized_task.yml new file mode 100644 index 00000000..300401b5 --- /dev/null +++ b/test/integration/targets/blocks/finalized_task.yml @@ -0,0 +1,17 @@ +- hosts: localhost + gather_facts: false + tasks: + - block: + - include_role: + name: '{{ item }}' + loop: + - fail + rescue: + - debug: + msg: "{{ ansible_failed_task.name }}" + + - assert: + that: + - ansible_failed_task.name == "Fail" + - ansible_failed_task.action == "fail" + - ansible_failed_task.parent is not defined diff --git a/test/integration/targets/blocks/inherit_notify.yml b/test/integration/targets/blocks/inherit_notify.yml new file mode 100644 index 00000000..d8e87423 --- /dev/null +++ b/test/integration/targets/blocks/inherit_notify.yml @@ -0,0 +1,19 @@ +- hosts: localhost + gather_facts: false + tasks: + - name: test notify inheritance in block + notify: hello + block: + - debug: msg='trigger it' + changed_when: true + + handlers: + - name: hello + set_fact: hello=world + + post_tasks: + - name: ensure handler ran + assert: + that: + - hello is defined + - "hello == 'world'" diff --git a/test/integration/targets/blocks/roles/fail/tasks/main.yml b/test/integration/targets/blocks/roles/fail/tasks/main.yml new file mode 100644 index 00000000..176fe542 --- /dev/null +++ b/test/integration/targets/blocks/roles/fail/tasks/main.yml @@ -0,0 +1,3 @@ +- name: Fail + fail: + msg: fail diff --git a/test/integration/targets/blocks/roles/role-69848-1/meta/main.yml b/test/integration/targets/blocks/roles/role-69848-1/meta/main.yml new file mode 100644 index 00000000..d34d6629 --- /dev/null +++ b/test/integration/targets/blocks/roles/role-69848-1/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - role: role-69848-3 diff --git a/test/integration/targets/blocks/roles/role-69848-2/meta/main.yml b/test/integration/targets/blocks/roles/role-69848-2/meta/main.yml new file mode 100644 index 00000000..d34d6629 --- /dev/null +++ b/test/integration/targets/blocks/roles/role-69848-2/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - role: role-69848-3 diff --git a/test/integration/targets/blocks/roles/role-69848-3/tasks/main.yml b/test/integration/targets/blocks/roles/role-69848-3/tasks/main.yml new file mode 100644 index 00000000..0d01b74b --- /dev/null +++ b/test/integration/targets/blocks/roles/role-69848-3/tasks/main.yml @@ -0,0 +1,8 @@ +- block: + - debug: + msg: Tagged task + tags: + - foo + +- debug: + msg: Not tagged task diff --git a/test/integration/targets/blocks/runme.sh b/test/integration/targets/blocks/runme.sh index 4f3db1db..67f07a8a 100755 --- a/test/integration/targets/blocks/runme.sh +++ b/test/integration/targets/blocks/runme.sh @@ -75,6 +75,16 @@ cat rc_test.out [ "$(grep -c 'DID NOT RUN' rc_test.out )" -eq 0 ] rm -f rc_test.out +# https://github.com/ansible/ansible/issues/29047 +ansible-playbook -vv issue29047.yml -i ../../inventory + +# https://github.com/ansible/ansible/issues/61253 +ansible-playbook -vv block_in_rescue.yml -i ../../inventory > rc_test.out +cat rc_test.out +[ "$(grep -c 'rescued=3' rc_test.out)" -eq 1 ] +[ "$(grep -c 'failed=0' rc_test.out)" -eq 1 ] +rm -f rc_test.out + # https://github.com/ansible/ansible/issues/71306 set +e exit_code=0 @@ -84,12 +94,16 @@ cat rc_test.out [ $exit_code -eq 0 ] rm -f rc_test_out -# https://github.com/ansible/ansible/issues/29047 -ansible-playbook -vv issue29047.yml -i ../../inventory +# https://github.com/ansible/ansible/issues/69848 +ansible-playbook -i host1,host2 --tags foo -vv 69848.yml > role_complete_test.out +cat role_complete_test.out +[ "$(grep -c 'Tagged task' role_complete_test.out)" -eq 2 ] +[ "$(grep -c 'Not tagged task' role_complete_test.out)" -eq 0 ] +rm -f role_complete_test.out -# https://github.com/ansible/ansible/issues/61253 -ansible-playbook -vv block_in_rescue.yml -i ../../inventory > rc_test.out -cat rc_test.out -[ "$(grep -c 'rescued=3' rc_test.out)" -eq 1 ] -[ "$(grep -c 'failed=0' rc_test.out)" -eq 1 ] -rm -f rc_test.out +# test notify inheritance +ansible-playbook inherit_notify.yml "$@" + +ansible-playbook unsafe_failed_task.yml "$@" + +ansible-playbook finalized_task.yml "$@" diff --git a/test/integration/targets/blocks/unsafe_failed_task.yml b/test/integration/targets/blocks/unsafe_failed_task.yml new file mode 100644 index 00000000..adfa492a --- /dev/null +++ b/test/integration/targets/blocks/unsafe_failed_task.yml @@ -0,0 +1,17 @@ +- hosts: localhost + gather_facts: false + vars: + - data: {} + tasks: + - block: + - name: template error + debug: + msg: "{{ data.value }}" + rescue: + - debug: + msg: "{{ ansible_failed_task.action }}" + + - assert: + that: + - ansible_failed_task.name == "template error" + - ansible_failed_task.action == "debug" diff --git a/test/integration/targets/builtin_vars_prompt/test-vars_prompt.py b/test/integration/targets/builtin_vars_prompt/test-vars_prompt.py index 6c805fdd..93958fc2 100644 --- a/test/integration/targets/builtin_vars_prompt/test-vars_prompt.py +++ b/test/integration/targets/builtin_vars_prompt/test-vars_prompt.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import os import pexpect import sys diff --git a/test/integration/targets/callback_default/callback_default.out.default.stdout b/test/integration/targets/callback_default/callback_default.out.default.stdout index 05f90beb..e34b5a4b 100644 --- a/test/integration/targets/callback_default/callback_default.out.default.stdout +++ b/test/integration/targets/callback_default/callback_default.out.default.stdout @@ -33,7 +33,7 @@ ok: [testhost] => (item=debug-3) => { "msg": "debug-3" } skipping: [testhost] => (item=debug-4) -fatal: [testhost]: FAILED! => {"msg": "All items completed"} +fatal: [testhost]: FAILED! => {"msg": "One or more items failed"} ...ignoring TASK [EXPECTED FAILURE Failed task to be rescued] ****************************** @@ -50,6 +50,23 @@ ok: [testhost] => { "item": 1 } +TASK [copy] ******************************************************************** +changed: [testhost] + +TASK [replace] ***************************************************************** +--- before: .../test_diff.txt ++++ after: .../test_diff.txt +@@ -1 +1 @@ +-foo +\ No newline at end of file ++bar +\ No newline at end of file + +changed: [testhost] + +TASK [replace] ***************************************************************** +ok: [testhost] + RUNNING HANDLER [Test handler 1] *********************************************** changed: [testhost] @@ -67,6 +84,14 @@ changed: [testhost] TASK [Second free task] ******************************************************** changed: [testhost] +TASK [Include some tasks] ****************************************************** +included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) + +TASK [debug] ******************************************************************* +ok: [testhost] => { + "item": 1 +} + PLAY RECAP ********************************************************************* -testhost : ok=14 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 +testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 diff --git a/test/integration/targets/callback_default/callback_default.out.display_path_on_failure.stderr b/test/integration/targets/callback_default/callback_default.out.display_path_on_failure.stderr new file mode 100644 index 00000000..d3e07d47 --- /dev/null +++ b/test/integration/targets/callback_default/callback_default.out.display_path_on_failure.stderr @@ -0,0 +1,2 @@ ++ ansible-playbook -i inventory test.yml +++ set +x diff --git a/test/integration/targets/callback_default/callback_default.out.display_path_on_failure.stdout b/test/integration/targets/callback_default/callback_default.out.display_path_on_failure.stdout new file mode 100644 index 00000000..d9ffd575 --- /dev/null +++ b/test/integration/targets/callback_default/callback_default.out.display_path_on_failure.stdout @@ -0,0 +1,100 @@ + +PLAY [testhost] **************************************************************** + +TASK [Changed task] ************************************************************ +changed: [testhost] + +TASK [Ok task] ***************************************************************** +ok: [testhost] + +TASK [Failed task] ************************************************************* +task path: TEST_PATH/test.yml:16 +fatal: [testhost]: FAILED! => {"changed": false, "msg": "no reason"} +...ignoring + +TASK [Skipped task] ************************************************************ +skipping: [testhost] + +TASK [Task with var in name (foo bar)] ***************************************** +changed: [testhost] + +TASK [Loop task] *************************************************************** +changed: [testhost] => (item=foo-1) +changed: [testhost] => (item=foo-2) +changed: [testhost] => (item=foo-3) + +TASK [debug loop] ************************************************************** +changed: [testhost] => (item=debug-1) => { + "msg": "debug-1" +} +failed: [testhost] (item=debug-2) => { + "msg": "debug-2" +} +ok: [testhost] => (item=debug-3) => { + "msg": "debug-3" +} +skipping: [testhost] => (item=debug-4) +task path: TEST_PATH/test.yml:38 +fatal: [testhost]: FAILED! => {"msg": "One or more items failed"} +...ignoring + +TASK [EXPECTED FAILURE Failed task to be rescued] ****************************** +task path: TEST_PATH/test.yml:54 +fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"} + +TASK [Rescue task] ************************************************************* +changed: [testhost] + +TASK [include_tasks] *********************************************************** +included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) + +TASK [debug] ******************************************************************* +ok: [testhost] => { + "item": 1 +} + +TASK [copy] ******************************************************************** +changed: [testhost] + +TASK [replace] ***************************************************************** +--- before: .../test_diff.txt ++++ after: .../test_diff.txt +@@ -1 +1 @@ +-foo +\ No newline at end of file ++bar +\ No newline at end of file + +changed: [testhost] + +TASK [replace] ***************************************************************** +ok: [testhost] + +RUNNING HANDLER [Test handler 1] *********************************************** +changed: [testhost] + +RUNNING HANDLER [Test handler 2] *********************************************** +ok: [testhost] + +RUNNING HANDLER [Test handler 3] *********************************************** +changed: [testhost] + +PLAY [testhost] **************************************************************** + +TASK [First free task] ********************************************************* +changed: [testhost] + +TASK [Second free task] ******************************************************** +changed: [testhost] + +TASK [Include some tasks] ****************************************************** +included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) + +TASK [debug] ******************************************************************* +ok: [testhost] => { + "item": 1 +} + +PLAY RECAP ********************************************************************* +testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 + diff --git a/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stderr b/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stderr index 932a2e4f..9a39d78f 100644 --- a/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stderr +++ b/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stderr @@ -1,5 +1,8 @@ + ansible-playbook -i inventory test.yml ++ set +x fatal: [testhost]: FAILED! => {"changed": false, "msg": "no reason"} -fatal: [testhost]: FAILED! => {"msg": "All items completed"} +failed: [testhost] (item=debug-2) => { + "msg": "debug-2" +} +fatal: [testhost]: FAILED! => {"msg": "One or more items failed"} fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"} diff --git a/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stdout b/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stdout index fe990d42..8e165a41 100644 --- a/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stdout +++ b/test/integration/targets/callback_default/callback_default.out.failed_to_stderr.stdout @@ -25,9 +25,6 @@ TASK [debug loop] ************************************************************** changed: [testhost] => (item=debug-1) => { "msg": "debug-1" } -failed: [testhost] (item=debug-2) => { - "msg": "debug-2" -} ok: [testhost] => (item=debug-3) => { "msg": "debug-3" } @@ -47,6 +44,23 @@ ok: [testhost] => { "item": 1 } +TASK [copy] ******************************************************************** +changed: [testhost] + +TASK [replace] ***************************************************************** +--- before: .../test_diff.txt ++++ after: .../test_diff.txt +@@ -1 +1 @@ +-foo +\ No newline at end of file ++bar +\ No newline at end of file + +changed: [testhost] + +TASK [replace] ***************************************************************** +ok: [testhost] + RUNNING HANDLER [Test handler 1] *********************************************** changed: [testhost] @@ -64,6 +78,14 @@ changed: [testhost] TASK [Second free task] ******************************************************** changed: [testhost] +TASK [Include some tasks] ****************************************************** +included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) + +TASK [debug] ******************************************************************* +ok: [testhost] => { + "item": 1 +} + PLAY RECAP ********************************************************************* -testhost : ok=14 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 +testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 diff --git a/test/integration/targets/callback_default/callback_default.out.hide_ok.stdout b/test/integration/targets/callback_default/callback_default.out.hide_ok.stdout index c1e1846b..2b019d31 100644 --- a/test/integration/targets/callback_default/callback_default.out.hide_ok.stdout +++ b/test/integration/targets/callback_default/callback_default.out.hide_ok.stdout @@ -27,7 +27,7 @@ failed: [testhost] (item=debug-2) => { "msg": "debug-2" } skipping: [testhost] => (item=debug-4) -fatal: [testhost]: FAILED! => {"msg": "All items completed"} +fatal: [testhost]: FAILED! => {"msg": "One or more items failed"} ...ignoring TASK [EXPECTED FAILURE Failed task to be rescued] ****************************** @@ -35,8 +35,24 @@ fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested fro TASK [Rescue task] ************************************************************* changed: [testhost] + +TASK [include_tasks] *********************************************************** included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) +TASK [copy] ******************************************************************** +changed: [testhost] + +TASK [replace] ***************************************************************** +--- before: .../test_diff.txt ++++ after: .../test_diff.txt +@@ -1 +1 @@ +-foo +\ No newline at end of file ++bar +\ No newline at end of file + +changed: [testhost] + RUNNING HANDLER [Test handler 1] *********************************************** changed: [testhost] @@ -51,6 +67,9 @@ changed: [testhost] TASK [Second free task] ******************************************************** changed: [testhost] +TASK [Include some tasks] ****************************************************** +included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) + PLAY RECAP ********************************************************************* -testhost : ok=14 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 +testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 diff --git a/test/integration/targets/callback_default/callback_default.out.hide_skipped.stdout b/test/integration/targets/callback_default/callback_default.out.hide_skipped.stdout index 660c7285..c6cd4913 100644 --- a/test/integration/targets/callback_default/callback_default.out.hide_skipped.stdout +++ b/test/integration/targets/callback_default/callback_default.out.hide_skipped.stdout @@ -29,7 +29,7 @@ failed: [testhost] (item=debug-2) => { ok: [testhost] => (item=debug-3) => { "msg": "debug-3" } -fatal: [testhost]: FAILED! => {"msg": "All items completed"} +fatal: [testhost]: FAILED! => {"msg": "One or more items failed"} ...ignoring TASK [EXPECTED FAILURE Failed task to be rescued] ****************************** @@ -37,6 +37,8 @@ fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested fro TASK [Rescue task] ************************************************************* changed: [testhost] + +TASK [include_tasks] *********************************************************** included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) TASK [debug] ******************************************************************* @@ -44,6 +46,23 @@ ok: [testhost] => { "item": 1 } +TASK [copy] ******************************************************************** +changed: [testhost] + +TASK [replace] ***************************************************************** +--- before: .../test_diff.txt ++++ after: .../test_diff.txt +@@ -1 +1 @@ +-foo +\ No newline at end of file ++bar +\ No newline at end of file + +changed: [testhost] + +TASK [replace] ***************************************************************** +ok: [testhost] + RUNNING HANDLER [Test handler 1] *********************************************** changed: [testhost] @@ -61,6 +80,14 @@ changed: [testhost] TASK [Second free task] ******************************************************** changed: [testhost] +TASK [Include some tasks] ****************************************************** +included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) + +TASK [debug] ******************************************************************* +ok: [testhost] => { + "item": 1 +} + PLAY RECAP ********************************************************************* -testhost : ok=14 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 +testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 diff --git a/test/integration/targets/callback_default/callback_default.out.hide_skipped_ok.stdout b/test/integration/targets/callback_default/callback_default.out.hide_skipped_ok.stdout index 13948b9f..45a5ecf6 100644 --- a/test/integration/targets/callback_default/callback_default.out.hide_skipped_ok.stdout +++ b/test/integration/targets/callback_default/callback_default.out.hide_skipped_ok.stdout @@ -23,7 +23,7 @@ changed: [testhost] => (item=debug-1) => { failed: [testhost] (item=debug-2) => { "msg": "debug-2" } -fatal: [testhost]: FAILED! => {"msg": "All items completed"} +fatal: [testhost]: FAILED! => {"msg": "One or more items failed"} ...ignoring TASK [EXPECTED FAILURE Failed task to be rescued] ****************************** @@ -31,8 +31,24 @@ fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested fro TASK [Rescue task] ************************************************************* changed: [testhost] + +TASK [include_tasks] *********************************************************** included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) +TASK [copy] ******************************************************************** +changed: [testhost] + +TASK [replace] ***************************************************************** +--- before: .../test_diff.txt ++++ after: .../test_diff.txt +@@ -1 +1 @@ +-foo +\ No newline at end of file ++bar +\ No newline at end of file + +changed: [testhost] + RUNNING HANDLER [Test handler 1] *********************************************** changed: [testhost] @@ -47,6 +63,9 @@ changed: [testhost] TASK [Second free task] ******************************************************** changed: [testhost] +TASK [Include some tasks] ****************************************************** +included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1) + PLAY RECAP ********************************************************************* -testhost : ok=14 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 +testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2 diff --git a/test/integration/targets/callback_default/inventory b/test/integration/targets/callback_default/inventory index 6d9b3028..5d93afd0 100644 --- a/test/integration/targets/callback_default/inventory +++ b/test/integration/targets/callback_default/inventory @@ -5,6 +5,6 @@ testhost ansible_connection=local ansible_python_interpreter="{{ ansible_playboo testhost5 ansible_host=169.254.199.200 # no connection is ever established with this host [nonlockstep] -testhost10 i=0.5 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" +testhost10 i=0 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" testhost11 i=3.0 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" testhost12 i=12.0 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/test/integration/targets/callback_default/no_implicit_meta_banners.yml b/test/integration/targets/callback_default/no_implicit_meta_banners.yml new file mode 100644 index 00000000..87883dc6 --- /dev/null +++ b/test/integration/targets/callback_default/no_implicit_meta_banners.yml @@ -0,0 +1,11 @@ +# This playbooks generates a noop task in the linear strategy, the output is tested that the banner for noop task is not printed https://github.com/ansible/ansible/pull/71344 +- hosts: all + gather_facts: no + tasks: + - block: + - name: EXPECTED FAILURE # sate shippable + fail: + when: inventory_hostname == 'host1' + rescue: + - name: rescue + debug: diff --git a/test/integration/targets/callback_default/runme.sh b/test/integration/targets/callback_default/runme.sh index a8033d7d..b5c98ef7 100755 --- a/test/integration/targets/callback_default/runme.sh +++ b/test/integration/targets/callback_default/runme.sh @@ -16,7 +16,7 @@ set -eux run_test() { local testname=$1 - # outout was recorded w/o cowsay, ensure we reproduce the same + # output was recorded w/o cowsay, ensure we reproduce the same export ANSIBLE_NOCOWS=1 # The shenanigans with redirection and 'tee' are to capture STDOUT and @@ -27,6 +27,9 @@ run_test() { # Scrub deprication warning that shows up in Python 2.6 on CentOS 6 sed -i -e '/RandomPool_DeprecationWarning/d' "${OUTFILE}.${testname}.stderr" sed -i -e 's/included: .*\/test\/integration/included: ...\/test\/integration/g' "${OUTFILE}.${testname}.stdout" + sed -i -e 's/@@ -1,1 +1,1 @@/@@ -1 +1 @@/g' "${OUTFILE}.${testname}.stdout" + sed -i -e 's/: .*\/test_diff\.txt/: ...\/test_diff.txt/g' "${OUTFILE}.${testname}.stdout" + sed -i -e "s#${ANSIBLE_PLAYBOOK_DIR}#TEST_PATH#g" "${OUTFILE}.${testname}.stdout" diff -u "${ORIGFILE}.${testname}.stdout" "${OUTFILE}.${testname}.stdout" || diff_failure diff -u "${ORIGFILE}.${testname}.stderr" "${OUTFILE}.${testname}.stderr" || diff_failure @@ -145,6 +148,14 @@ export ANSIBLE_DISPLAY_OK_HOSTS=1 export ANSIBLE_DISPLAY_FAILED_STDERR=1 run_test failed_to_stderr +export ANSIBLE_DISPLAY_FAILED_STDERR=0 + + +# Test displaying task path on failure +export ANSIBLE_SHOW_TASK_PATH_ON_FAILURE=1 +run_test display_path_on_failure +export ANSIBLE_SHOW_TASK_PATH_ON_FAILURE=0 + # Default settings with unreachable tasks export ANSIBLE_DISPLAY_SKIPPED_HOSTS=1 @@ -185,6 +196,12 @@ run_test_dryrun check_nomarkers_wet # Test the dry run without check markers run_test_dryrun check_nomarkers_dry --check +# Make sure implicit meta tasks are not printed +ansible-playbook -i host1,host2 no_implicit_meta_banners.yml > meta_test.out +cat meta_test.out +[ "$(grep -c 'TASK \[meta\]' meta_test.out)" -eq 0 ] +rm -f meta_test.out + # Ensure free/host_pinned non-lockstep strategies display correctly diff -u callback_default.out.free.stdout <(ANSIBLE_STRATEGY=free ansible-playbook -i inventory test_non_lockstep.yml 2>/dev/null) diff -u callback_default.out.host_pinned.stdout <(ANSIBLE_STRATEGY=host_pinned ansible-playbook -i inventory test_non_lockstep.yml 2>/dev/null) diff --git a/test/integration/targets/callback_default/test.yml b/test/integration/targets/callback_default/test.yml index b31787bf..a10e8f9d 100644 --- a/test/integration/targets/callback_default/test.yml +++ b/test/integration/targets/callback_default/test.yml @@ -61,6 +61,23 @@ loop: - 1 + - copy: + dest: '{{ lookup("env", "OUTPUT_DIR") }}/test_diff.txt' + content: foo + + - replace: + path: '{{ lookup("env", "OUTPUT_DIR") }}/test_diff.txt' + regexp: '^foo$' + replace: bar + diff: true + + - replace: + path: '{{ lookup("env", "OUTPUT_DIR") }}/test_diff.txt' + regexp: '^bar$' + replace: baz + diff: true + changed_when: false + handlers: - name: Test handler 1 command: echo foo @@ -86,3 +103,9 @@ - name: Second free task command: echo foo + + # Ensure include_tasks task names get shown (#71277) + - name: Include some tasks + include_tasks: include_me.yml + loop: + - 1 diff --git a/test/integration/targets/callback_default/test_dryrun.yml b/test/integration/targets/callback_default/test_dryrun.yml index 26cf0831..e6e32948 100644 --- a/test/integration/targets/callback_default/test_dryrun.yml +++ b/test/integration/targets/callback_default/test_dryrun.yml @@ -3,7 +3,7 @@ hosts: testhost gather_facts: no tasks: - - debug: + - debug: msg: 'ansible_check_mode: {{ansible_check_mode}}' - name: Command @@ -23,12 +23,12 @@ gather_facts: no check_mode: true tasks: - - debug: + - debug: msg: 'ansible_check_mode: {{ansible_check_mode}}' - name: Command command: ls -l - + - name: "Command with check_mode: false" command: ls -l check_mode: false @@ -43,7 +43,7 @@ gather_facts: no check_mode: false tasks: - - debug: + - debug: msg: 'ansible_check_mode: {{ansible_check_mode}}' - name: Command diff --git a/test/integration/targets/cli/runme.sh b/test/integration/targets/cli/runme.sh index d9e84625..c4f0867a 100755 --- a/test/integration/targets/cli/runme.sh +++ b/test/integration/targets/cli/runme.sh @@ -5,3 +5,5 @@ set -eux ANSIBLE_ROLES_PATH=../ ansible-playbook setup.yml python test-cli.py + +ansible-playbook test_syntax/syntax_check.yml --syntax-check -i ../../inventory -v "$@" diff --git a/test/integration/targets/cli/test_syntax/files/vaultsecret b/test/integration/targets/cli/test_syntax/files/vaultsecret new file mode 100644 index 00000000..9daeafb9 --- /dev/null +++ b/test/integration/targets/cli/test_syntax/files/vaultsecret @@ -0,0 +1 @@ +test diff --git a/test/integration/targets/cli/test_syntax/group_vars/all/testvault.yml b/test/integration/targets/cli/test_syntax/group_vars/all/testvault.yml new file mode 100644 index 00000000..dab41f81 --- /dev/null +++ b/test/integration/targets/cli/test_syntax/group_vars/all/testvault.yml @@ -0,0 +1,6 @@ +$ANSIBLE_VAULT;1.1;AES256 +63666636353064303132316633383734303731353066643832633666616162373531306563616139 +3038343765633138376561336530366162646332353132620a643661663366336237636562393662 +61656465393864613832383565306133656332656534326530346638336165346264386666343266 +3066336331313830310a666265653532643434303233306635366635616261373166613564326238 +62306134303765306537396162623232396639316239616534613631336166616561 diff --git a/test/integration/targets/cli/test_syntax/roles/some_role/tasks/main.yml b/test/integration/targets/cli/test_syntax/roles/some_role/tasks/main.yml new file mode 100644 index 00000000..307927cc --- /dev/null +++ b/test/integration/targets/cli/test_syntax/roles/some_role/tasks/main.yml @@ -0,0 +1 @@ +- debug: msg='in role' diff --git a/test/integration/targets/cli/test_syntax/syntax_check.yml b/test/integration/targets/cli/test_syntax/syntax_check.yml new file mode 100644 index 00000000..8e26cf02 --- /dev/null +++ b/test/integration/targets/cli/test_syntax/syntax_check.yml @@ -0,0 +1,7 @@ +- name: "main" + hosts: all + gather_facts: false + tasks: + - import_role: + name: some_role + delegate_to: testhost diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/play.yml b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/play.yml new file mode 100644 index 00000000..6be246cc --- /dev/null +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/play.yml @@ -0,0 +1,4 @@ +- hosts: localhost + gather_facts: false + tasks: + - set_fact: play='tldr' diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/type/play.yml b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/type/play.yml new file mode 100644 index 00000000..dd6d563f --- /dev/null +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/type/play.yml @@ -0,0 +1,4 @@ +- hosts: localhost + gather_facts: false + tasks: + - set_fact: play_type='in type subdir' diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/type/subtype/play.yml b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/type/subtype/play.yml new file mode 100644 index 00000000..0e33a761 --- /dev/null +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/playbooks/type/subtype/play.yml @@ -0,0 +1,4 @@ +- hosts: localhost + gather_facts: false + tasks: + - set_fact: play_type_subtype='in subtype subdir' diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/action/plugin_lookup.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/action/plugin_lookup.py index 1b882810..3fa41e8f 100644 --- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/action/plugin_lookup.py +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/action/plugin_lookup.py @@ -15,19 +15,26 @@ class ActionModule(ActionBase): result = super(ActionModule, self).run(None, task_vars) - type = self._task.args.get('type') + plugin_type = self._task.args.get('type') name = self._task.args.get('name') result = dict(changed=False, collection_list=self._task.collections) - if all([type, name]): - attr_name = '{0}_loader'.format(type) + if all([plugin_type, name]): + attr_name = '{0}_loader'.format(plugin_type) typed_loader = getattr(loader, attr_name, None) if not typed_loader: - return (dict(failed=True, msg='invalid plugin type {0}'.format(type))) + return (dict(failed=True, msg='invalid plugin type {0}'.format(plugin_type))) - result['plugin_path'] = typed_loader.find_plugin(name, collection_list=self._task.collections) + context = typed_loader.find_plugin_with_context(name, collection_list=self._task.collections) + + if not context.resolved: + result['plugin_path'] = None + result['redirect_list'] = [] + else: + result['plugin_path'] = context.plugin_resolved_path + result['redirect_list'] = context.redirect_list return result diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/callback/usercallback.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/callback/usercallback.py index c5b0f66a..b534df2f 100644 --- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/callback/usercallback.py +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/callback/usercallback.py @@ -16,7 +16,6 @@ class CallbackModule(CallbackBase): CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'aggregate' CALLBACK_NAME = 'usercallback' - CALLBACK_NEEDS_WHITELIST = True def __init__(self): diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testredirect/meta/runtime.yml b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testredirect/meta/runtime.yml new file mode 100644 index 00000000..da8e4901 --- /dev/null +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testredirect/meta/runtime.yml @@ -0,0 +1,4 @@ +plugin_routing: + modules: + ping: + redirect: testns.testcoll.ping diff --git a/test/integration/targets/collections/custom_vars_plugins/vars_req_whitelist.py b/test/integration/targets/collections/custom_vars_plugins/vars_req_whitelist.py deleted file mode 100644 index 0ab95273..00000000 --- a/test/integration/targets/collections/custom_vars_plugins/vars_req_whitelist.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2019 RedHat, inc -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. -############################################# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' - vars: vars_req_whitelist - version_added: "2.10" - short_description: load host and group vars - description: test loading host and group vars from a collection - options: - stage: - choices: ['all', 'inventory', 'task'] - type: str - ini: - - key: stage - section: vars_req_whitelist - env: - - name: ANSIBLE_VARS_PLUGIN_STAGE -''' - -from ansible.plugins.vars import BaseVarsPlugin - - -class VarsModule(BaseVarsPlugin): - - REQUIRES_WHITELIST = True - - def get_vars(self, loader, path, entities, cache=True): - super(VarsModule, self).get_vars(loader, path, entities) - return {'whitelisted': True, 'collection': False} diff --git a/test/integration/targets/collections/import_collection_pb.yml b/test/integration/targets/collections/import_collection_pb.yml new file mode 100644 index 00000000..511d9486 --- /dev/null +++ b/test/integration/targets/collections/import_collection_pb.yml @@ -0,0 +1,17 @@ +- import_playbook: testns.testcoll.default_collection_playbook.yml +- import_playbook: testns.testcoll.default_collection_playbook + +# test subdirs +- import_playbook: "testns.testcoll.play" +- import_playbook: "testns.testcoll.type.play" +- import_playbook: "testns.testcoll.type.subtype.play" + +- hosts: localhost + gather_facts: false + tasks: + - name: check values from imports + assert: + that: + - play is defined + - play_type is defined + - play_type_subtype is defined diff --git a/test/integration/targets/collections/redirected.statichost.yml b/test/integration/targets/collections/redirected.statichost.yml index 9fd2c2d8..8cfab462 100644 --- a/test/integration/targets/collections/redirected.statichost.yml +++ b/test/integration/targets/collections/redirected.statichost.yml @@ -1,3 +1,3 @@ # use a plugin redirected by core to a collection to ensure inventory redirection and redirected config names are working -plugin: formerly_core_inventory # this is defined in the ansible-base runtime.yml routing to point at testns.content_adj.statichost +plugin: formerly_core_inventory # this is defined in the ansible-core runtime.yml routing to point at testns.content_adj.statichost hostname: dynamic_host_redirected diff --git a/test/integration/targets/collections/runme.sh b/test/integration/targets/collections/runme.sh index dc01a6c0..1e9584ff 100755 --- a/test/integration/targets/collections/runme.sh +++ b/test/integration/targets/collections/runme.sh @@ -6,29 +6,38 @@ export ANSIBLE_COLLECTIONS_PATH=$PWD/collection_root_user:$PWD/collection_root_s export ANSIBLE_GATHERING=explicit export ANSIBLE_GATHER_SUBSET=minimal export ANSIBLE_HOST_PATTERN_MISMATCH=error +unset ANSIBLE_COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH # FUTURE: just use INVENTORY_PATH as-is once ansible-test sets the right dir ipath=../../$(basename "${INVENTORY_PATH:-../../inventory}") export INVENTORY_PATH="$ipath" +# ensure we can call collection module +ansible localhost -m testns.testcoll.testmodule + +# ensure we can call collection module with ansible_collections in path +ANSIBLE_COLLECTIONS_PATH=$PWD/collection_root_sys/ansible_collections ansible localhost -m testns.testcoll.testmodule + + echo "--- validating callbacks" # validate FQ callbacks in ansible-playbook -ANSIBLE_CALLBACK_WHITELIST=testns.testcoll.usercallback ansible-playbook noop.yml | grep "usercallback says ok" +ANSIBLE_CALLBACKS_ENABLED=testns.testcoll.usercallback ansible-playbook noop.yml | grep "usercallback says ok" # use adhoc for the rest of these tests, must force it to load other callbacks export ANSIBLE_LOAD_CALLBACK_PLUGINS=1 # validate redirected callback -ANSIBLE_CALLBACK_WHITELIST=formerly_core_callback ansible localhost -m debug 2>&1 | grep -- "usercallback says ok" +ANSIBLE_CALLBACKS_ENABLED=formerly_core_callback ansible localhost -m debug 2>&1 | grep -- "usercallback says ok" ## validate missing redirected callback -ANSIBLE_CALLBACK_WHITELIST=formerly_core_missing_callback ansible localhost -m debug 2>&1 | grep -- "Skipping callback plugin 'formerly_core_missing_callback'" +ANSIBLE_CALLBACKS_ENABLED=formerly_core_missing_callback ansible localhost -m debug 2>&1 | grep -- "Skipping callback plugin 'formerly_core_missing_callback'" ## validate redirected + removed callback (fatal) -ANSIBLE_CALLBACK_WHITELIST=formerly_core_removed_callback ansible localhost -m debug 2>&1 | grep -- "testns.testcoll.removedcallback has been removed" +ANSIBLE_CALLBACKS_ENABLED=formerly_core_removed_callback ansible localhost -m debug 2>&1 | grep -- "testns.testcoll.removedcallback has been removed" # validate avoiding duplicate loading of callback, even if using diff names -[ "$(ANSIBLE_CALLBACK_WHITELIST=testns.testcoll.usercallback,formerly_core_callback ansible localhost -m debug 2>&1 | grep -c 'usercallback says ok')" = "1" ] +[ "$(ANSIBLE_CALLBACKS_ENABLED=testns.testcoll.usercallback,formerly_core_callback ansible localhost -m debug 2>&1 | grep -c 'usercallback says ok')" = "1" ] # ensure non existing callback does not crash ansible -ANSIBLE_CALLBACK_WHITELIST=charlie.gomez.notme ansible localhost -m debug 2>&1 | grep -- "Skipping callback plugin 'charlie.gomez.notme'" +ANSIBLE_CALLBACKS_ENABLED=charlie.gomez.notme ansible localhost -m debug 2>&1 | grep -- "Skipping callback plugin 'charlie.gomez.notme'" + unset ANSIBLE_LOAD_CALLBACK_PLUGINS # adhoc normally shouldn't load non-default plugins- let's be sure -output=$(ANSIBLE_CALLBACK_WHITELIST=testns.testcoll.usercallback ansible localhost -m debug) +output=$(ANSIBLE_CALLBACKS_ENABLED=testns.testcoll.usercallback ansible localhost -m debug) if [[ "${output}" =~ "usercallback says ok" ]]; then echo fail; exit 1; fi echo "--- validating docs" @@ -75,6 +84,26 @@ echo "--- validating inventory" # test collection inventories ansible-playbook inventory_test.yml -i a.statichost.yml -i redirected.statichost.yml "$@" +if [[ ${INVENTORY_PATH} != *.winrm ]]; then + # base invocation tests + ansible-playbook -i "${INVENTORY_PATH}" -v invocation_tests.yml "$@" + + # run playbook from collection, test default again, but with FQCN + ansible-playbook -i "${INVENTORY_PATH}" testns.testcoll.default_collection_playbook.yml "$@" + + # run playbook from collection, test default again, but with FQCN and no extension + ansible-playbook -i "${INVENTORY_PATH}" testns.testcoll.default_collection_playbook "$@" + + # run playbook that imports from collection + ansible-playbook -i "${INVENTORY_PATH}" import_collection_pb.yml "$@" +fi + +# test collection inventories +ansible-playbook inventory_test.yml -i a.statichost.yml -i redirected.statichost.yml "$@" + +# test plugin loader redirect_list +ansible-playbook test_redirect_list.yml -v "$@" + # test adjacent with --playbook-dir export ANSIBLE_COLLECTIONS_PATH='' ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=1 ansible-inventory --list --export --playbook-dir=. -v "$@" @@ -108,4 +137,3 @@ if [[ "$(grep -wc "dynamic_host_a" "$CACHEFILE")" -ne "0" ]]; then fi ./vars_plugin_tests.sh - diff --git a/test/integration/targets/collections/test_redirect_list.yml b/test/integration/targets/collections/test_redirect_list.yml new file mode 100644 index 00000000..8a24b960 --- /dev/null +++ b/test/integration/targets/collections/test_redirect_list.yml @@ -0,0 +1,86 @@ +--- +- hosts: localhost + gather_facts: no + module_defaults: + testns.testcoll.plugin_lookup: + type: module + tasks: + - name: test builtin + testns.testcoll.plugin_lookup: + name: dnf + register: result + failed_when: + - result['redirect_list'] != ['dnf'] or result['plugin_path'].endswith('library/dnf.py') + + - name: test builtin with collections kw + testns.testcoll.plugin_lookup: + name: dnf + register: result + failed_when: + - result['redirect_list'] != ['dnf'] or result['plugin_path'].endswith('library/dnf.py') + collections: + - testns.unrelatedcoll + + - name: test redirected builtin + testns.testcoll.plugin_lookup: + name: formerly_core_ping + register: result + failed_when: result['redirect_list'] != expected_redirect_list + vars: + expected_redirect_list: + - formerly_core_ping + - ansible.builtin.formerly_core_ping + - testns.testcoll.ping + + - name: test redirected builtin with collections kw + testns.testcoll.plugin_lookup: + name: formerly_core_ping + register: result + failed_when: result['redirect_list'] != expected_redirect_list + vars: + expected_redirect_list: + - formerly_core_ping + - ansible.builtin.formerly_core_ping + - testns.testcoll.ping + collections: + - testns.unrelatedcoll + - testns.testcoll + + - name: test collection module with collections kw + testns.testcoll.plugin_lookup: + name: ping + register: result + failed_when: result['redirect_list'] != expected_redirect_list + vars: + expected_redirect_list: + - ping + - testns.testcoll.ping + collections: + - testns.unrelatedcoll + - testns.testcoll + + - name: test redirected collection module with collections kw + testns.testcoll.plugin_lookup: + name: ping + register: result + failed_when: result['redirect_list'] != expected_redirect_list + vars: + expected_redirect_list: + - ping + - testns.testredirect.ping + - testns.testcoll.ping + collections: + - testns.unrelatedcoll + - testns.testredirect + + - name: test legacy module with collections kw + testns.testcoll.plugin_lookup: + name: ping + register: result + failed_when: + - result['redirect_list'] != expected_redirect_list or not result['plugin_path'].endswith('library/ping.py') + vars: + expected_redirect_list: + - ping + collections: + - testns.unrelatedcoll diff --git a/test/integration/targets/collections/vars_plugin_tests.sh b/test/integration/targets/collections/vars_plugin_tests.sh index 2118af6d..9dddf321 100755 --- a/test/integration/targets/collections/vars_plugin_tests.sh +++ b/test/integration/targets/collections/vars_plugin_tests.sh @@ -2,7 +2,7 @@ set -eux -# Collections vars plugins must be whitelisted with FQCN because PluginLoader.all() does not search collections +# Collections vars plugins must be enabled using the FQCN in the 'enabled' list, because PluginLoader.all() does not search collections # Let vars plugins run for inventory by using the global setting export ANSIBLE_RUN_VARS_PLUGINS=start @@ -33,7 +33,7 @@ grep '"collection": "collection_root_user"' out.txt grep '"adj_var": "value"' out.txt grep -v '"collection": "adjacent"' out.txt -# Test that 3rd party plugins in plugin_path do not need to require whitelisting by default +# Test that 3rd party plugins in plugin_path do not need to require enabling by default # Plugins shipped with Ansible and in the custom plugin dir should be used first export ANSIBLE_VARS_PLUGINS=./custom_vars_plugins @@ -42,15 +42,11 @@ ansible-inventory -i a.statichost.yml --list --playbook-dir=./ | tee out.txt grep '"name": "v2_vars_plugin"' out.txt grep '"collection": "collection_root_user"' out.txt grep '"adj_var": "value"' out.txt -grep -v '"whitelisted": true' out.txt -# Test plugins in plugin paths that opt-in to require whitelisting +# Test plugins in plugin paths that opt-in to require enabling unset ANSIBLE_VARS_ENABLED unset ANSIBLE_COLLECTIONS_PATH -ANSIBLE_VARS_ENABLED=vars_req_whitelist ansible-inventory -i a.statichost.yml --list --playbook-dir=./ | tee out.txt - -grep '"whitelisted": true' out.txt # Test vars plugins that support the stage setting don't run for inventory when stage is set to 'task' # and that the vars plugins that don't support the stage setting don't run for inventory when the global setting is 'demand' @@ -58,7 +54,6 @@ ANSIBLE_VARS_PLUGIN_STAGE=task ansible-inventory -i a.statichost.yml --list --pl grep -v '"v1_vars_plugin": true' out.txt grep -v '"v2_vars_plugin": true' out.txt -grep -v '"vars_req_whitelist": true' out.txt grep -v '"collection": "adjacent"' out.txt grep -v '"collection": "collection_root_user"' out.txt grep -v '"adj_var": "value"' out.txt @@ -66,7 +61,6 @@ grep -v '"adj_var": "value"' out.txt # Test that the global setting allows v1 and v2 plugins to run after importing inventory ANSIBLE_RUN_VARS_PLUGINS=start ansible-inventory -i a.statichost.yml --list --playbook-dir=./ | tee out.txt -grep -v '"vars_req_whitelist": true' out.txt grep '"v1_vars_plugin": true' out.txt grep '"v2_vars_plugin": true' out.txt grep '"name": "v2_vars_plugin"' out.txt diff --git a/test/integration/targets/collections_runtime_pythonpath/runme.sh b/test/integration/targets/collections_runtime_pythonpath/runme.sh index 41236e8b..654104a1 100755 --- a/test/integration/targets/collections_runtime_pythonpath/runme.sh +++ b/test/integration/targets/collections_runtime_pythonpath/runme.sh @@ -5,7 +5,7 @@ set -eux -o pipefail export PIP_DISABLE_PIP_VERSION_CHECK=1 -export ANSIBLE_TEST_PREFER_VENV=1 + source virtualenv.sh diff --git a/test/integration/targets/command_nonexisting/aliases b/test/integration/targets/command_nonexisting/aliases new file mode 100644 index 00000000..e2dcf795 --- /dev/null +++ b/test/integration/targets/command_nonexisting/aliases @@ -0,0 +1 @@ +shippable/posix/group2
\ No newline at end of file diff --git a/test/integration/targets/command_nonexisting/tasks/main.yml b/test/integration/targets/command_nonexisting/tasks/main.yml new file mode 100644 index 00000000..d21856e7 --- /dev/null +++ b/test/integration/targets/command_nonexisting/tasks/main.yml @@ -0,0 +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 diff --git a/test/integration/targets/command_shell/tasks/main.yml b/test/integration/targets/command_shell/tasks/main.yml index 1d614e49..653b0059 100644 --- a/test/integration/targets/command_shell/tasks/main.yml +++ b/test/integration/targets/command_shell/tasks/main.yml @@ -3,46 +3,6 @@ # Copyright: (c) 2014, Richard Isaacson <richard.c.isaacson@gmail.com> # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -- name: use command to execute sudo - command: sudo -h - register: become - -- name: assert become warning was reported - assert: - that: - - "become.warnings | length() == 1" - - "'Consider using' in become.warnings[0]" - -- name: use command to execute sudo without warnings - command: sudo -h warn=no - register: become - -- name: assert become warning was not reported - assert: - that: - - "'warnings' not in become" - -- name: use command to execute tar - command: tar --help - register: tar - -- name: assert tar warning was reported - assert: - that: - - tar.warnings | length() == 1 - - '"Consider using the unarchive module rather than running ''tar''" in tar.warnings[0]' - -- name: use command to execute chown - command: chown -h - register: chown - ignore_errors: true - -- name: assert chown warning was reported - assert: - that: - - chown.warnings | length() == 1 - - '"Consider using the file module with owner rather than running ''chown''" in chown.warnings[0]' - - name: use command with unsupported executable arg command: ls /dev/null args: @@ -56,19 +16,6 @@ - executable.warnings | length() == 1 - "'no longer supported' in executable.warnings[0]" -# The warning isn't on the task since it comes from the action plugin. Not sure -# how to test for that. -# -# - name: Use command with reboot -# command: sleep 2 && /not/shutdown -r now -# ignore_errors: yes -# register: reboot - -# - name: Assert that reboot warning was issued -# assert: -# that: -# - '"Consider using the reboot module" in reboot.warnings[0]' - - name: use command with no command command: args: @@ -185,12 +132,21 @@ chdir: "{{ output_dir_test }}" register: command_result2 +- name: Check invalid chdir + command: echo + args: + chdir: "{{ output_dir }}/nope" + ignore_errors: yes + register: chdir_invalid + - name: assert that the script executed correctly with chdir assert: that: - command_result2.rc == 0 - command_result2.stderr == '' - command_result2.stdout == 'win' + - chdir_invalid is failed + - chdir_invalid.msg is search('Unable to change directory') # creates @@ -444,3 +400,123 @@ file: path: "{{ output_dir_test }}/afile.txt" state: absent + +- name: test warning with command + command: + cmd: "rm -h" + warn: yes + ignore_errors: yes + register: warn_result + +- name: assert warning exists + assert: + that: + - warn_result.warnings | length == 1 + - "'Consider using the file module with state=absent rather than running \\'rm\\'' in warn_result.warnings[0]" + +- name: test warning with shell + shell: "sed -h" + args: + warn: yes + ignore_errors: yes + register: warn_result + +- name: assert warning exists + assert: + that: + - warn_result.warnings | length == 1 + - "'Consider using the replace, lineinfile or template module rather than running \\'sed\\'' in warn_result.warnings[0]" + +- name: test become warning + command: + cmd: "sudo true" + warn: yes + ignore_errors: yes + register: warn_result + +- name: assert warning exists + assert: + that: + - warn_result.warnings | length == 1 + - "'Consider using \\'become\\', \\'become_method\\', and \\'become_user\\' rather than running sudo' in warn_result.warnings[0]" + +- name: test check mode skip message + command: + cmd: "true" + check_mode: yes + register: result + +- name: assert check message exists + assert: + that: + - "'Command would have run if not in check mode' in result.msg" + +- name: test check mode creates/removes message + command: + cmd: "true" + creates: yes + check_mode: yes + register: result + +- name: assert check message exists + assert: + that: + - "'Command would have run if not in check mode' in result.msg" + +- name: command symlink handling + block: + - name: Create target folders + file: + path: '{{output_dir}}/www_root/site' + state: directory + + - name: Create symlink + file: + path: '{{output_dir}}/www' + state: link + src: '{{output_dir}}/www_root' + + - name: check parent using chdir + shell: dirname "$PWD" + args: + chdir: '{{output_dir}}/www/site' + register: parent_dir_chdir + + - name: check parent using cd + shell: cd "{{output_dir}}/www/site" && dirname "$PWD" + register: parent_dir_cd + + - name: check expected outputs + assert: + that: + - parent_dir_chdir.stdout != parent_dir_cd.stdout + - 'parent_dir_cd.stdout == "{{output_dir}}/www"' + - 'parent_dir_chdir.stdout == "{{output_dir}}/www_root"' + +- name: Set print error command for Python 2 + set_fact: + print_error_command: print >> sys.stderr, msg + when: ansible_facts.python_version is version('3', '<') + +- name: Set print error command for Python 3 + set_fact: + print_error_command: print(msg, file=sys.stderr) + when: ansible_facts.python_version is version('3', '>=') + +- name: run command with strip + command: '{{ ansible_playbook_python}} -c "import sys; msg=''hello \n \r''; print(msg); {{ print_error_command }}"' + register: command_strip + +- name: run command without strip + command: '{{ ansible_playbook_python}} -c "import sys; msg=''hello \n \r''; print(msg); {{ print_error_command }}"' + args: + strip_empty_ends: no + register: command_no_strip + +- name: Verify strip behavior worked as expected + assert: + that: + - command_strip.stdout == 'hello \n ' + - command_strip.stderr == 'hello \n ' + - command_no_strip.stdout== 'hello \n \r\n' + - command_no_strip.stderr == 'hello \n \r\n' diff --git a/test/integration/targets/config/inline_comment_ansible.cfg b/test/integration/targets/config/inline_comment_ansible.cfg index afe9197d..01a95c44 100644 --- a/test/integration/targets/config/inline_comment_ansible.cfg +++ b/test/integration/targets/config/inline_comment_ansible.cfg @@ -1,2 +1,2 @@ [defaults] -cow_whitelist = ansibull ; BOOM +cowsay_enabled_stencils = ansibull ; BOOM diff --git a/test/integration/targets/config/lookup_plugins/bogus.py b/test/integration/targets/config/lookup_plugins/bogus.py new file mode 100644 index 00000000..34dc98a2 --- /dev/null +++ b/test/integration/targets/config/lookup_plugins/bogus.py @@ -0,0 +1,51 @@ +# (c) 2021 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +DOCUMENTATION = """ + name: bogus + author: Ansible Core Team + version_added: histerical + short_description: returns what you gave it + description: + - this is mostly a noop + options: + _terms: + description: stuff to pass through + test_list: + description: does nothihng, just for testing values + type: list + choices: + - Dan + - Yevgeni + - Carla + - Manuela +""" + +EXAMPLES = """ +- name: like some other plugins, this is mostly useless + debug: msg={{ q('bogus', [1,2,3])}} +""" + +RETURN = """ + _list: + description: basically the same as you fed in + type: list + elements: raw +""" + +from ansible.plugins.lookup import LookupBase + + +class LookupModule(LookupBase): + + def run(self, terms, variables=None, **kwargs): + + self.set_options(var_options=variables, direct=kwargs) + dump = self.get_option('test_list') + + return terms diff --git a/test/integration/targets/config/runme.sh b/test/integration/targets/config/runme.sh index ea3989b8..bbff6acc 100755 --- a/test/integration/targets/config/runme.sh +++ b/test/integration/targets/config/runme.sh @@ -18,3 +18,6 @@ ANSIBLE_CONFIG=nonexistent.cfg ansible-config dump --only-changed -v | grep 'No # https://github.com/ansible/ansible/pull/73715 ANSIBLE_CONFIG=inline_comment_ansible.cfg ansible-config dump --only-changed | grep "'ansibull'" + +# test the config option validation +ansible-playbook validation.yml "$@" diff --git a/test/integration/targets/config/validation.yml b/test/integration/targets/config/validation.yml new file mode 100644 index 00000000..1c81e662 --- /dev/null +++ b/test/integration/targets/config/validation.yml @@ -0,0 +1,17 @@ +- hosts: localhost + gather_facts: false + tasks: + - name: does nothing but an empty assign, should fail only if lookup gets invalid options + set_fact: whatever={{ lookup('bogus', 1, test_list=['Dan', 'Manuela']) }} + + - name: now pass invalid option and fail! + set_fact: whatever={{ lookup('bogus', 1, test_list=['Dan', 'Manuela', 'Yoko']) }} + register: bad_input + ignore_errors: true + + - name: ensure it fails as expected + assert: + that: + - bad_input is failed + - '"Invalid value " in bad_input.msg' + - '"valid values are:" in bad_input.msg' diff --git a/test/integration/targets/connection_ssh/check_ssh_defaults.yml b/test/integration/targets/connection_ssh/check_ssh_defaults.yml new file mode 100644 index 00000000..937f1f7d --- /dev/null +++ b/test/integration/targets/connection_ssh/check_ssh_defaults.yml @@ -0,0 +1,29 @@ +- hosts: ssh + gather_facts: false + vars: + ansible_connection: ssh + ansible_ssh_timeout: 10 + tasks: + - name: contain the maddness + block: + - name: test all is good + ping: + + - name: start the fun + meta: reset_connection + + - name: now test we can use wrong port from ssh/config + ping: + ignore_unreachable: True + vars: + ansible_ssh_args: "-F {{playbook_dir}}/files/port_overrride_ssh.cfg" + register: expected + + - name: check all is as expected + assert: + that: + - expected['unreachable']|bool + - "'2222' in expected['msg']" + always: + - name: make sure we don't cache the bad connection + meta: reset_connection diff --git a/test/integration/targets/connection_ssh/files/port_overrride_ssh.cfg b/test/integration/targets/connection_ssh/files/port_overrride_ssh.cfg new file mode 100644 index 00000000..7f8422ec --- /dev/null +++ b/test/integration/targets/connection_ssh/files/port_overrride_ssh.cfg @@ -0,0 +1,2 @@ +Host * + Port 2222 diff --git a/test/integration/targets/connection_ssh/runme.sh b/test/integration/targets/connection_ssh/runme.sh index e7b2b21f..7e5953ed 100755 --- a/test/integration/targets/connection_ssh/runme.sh +++ b/test/integration/targets/connection_ssh/runme.sh @@ -22,12 +22,14 @@ if command -v sshpass > /dev/null; then -e ansible_sshpass_prompt=notThis: \ -e ansible_password=foo \ -e ansible_user=definitelynotroot \ - -i test_connection.inventory \ + -i test_connection.inventory \ ssh-pipelining ret=$? - if [[ $ret -ne 124 ]]; then + # 124 is EXIT_TIMEDOUT from gnu coreutils + # 143 is 128+SIGTERM(15) from BusyBox + if [[ $ret -ne 124 && $ret -ne 143 ]]; then echo "Expected to time out and we did not. Exiting with failure." - exit 1 + exit 1 fi else ansible -m ping \ @@ -35,7 +37,7 @@ if command -v sshpass > /dev/null; then -e ansible_sshpass_prompt=notThis: \ -e ansible_password=foo \ -e ansible_user=definitelynotroot \ - -i test_connection.inventory \ + -i test_connection.inventory \ ssh-pipelining | grep 'customized password prompts' ret=$? [[ $ret -eq 0 ]] || exit $ret @@ -63,3 +65,6 @@ fi ANSIBLE_SCP_IF_SSH=true ./posix.sh "$@" "${scp_args[@]}" # piped ANSIBLE_SSH_TRANSFER_METHOD=piped ./posix.sh "$@" + +# test config defaults override +ansible-playbook check_ssh_defaults.yml "$@" -i test_connection.inventory diff --git a/test/integration/targets/connection_windows_ssh/runme.sh b/test/integration/targets/connection_windows_ssh/runme.sh index 488bb7c5..766193f8 100755 --- a/test/integration/targets/connection_windows_ssh/runme.sh +++ b/test/integration/targets/connection_windows_ssh/runme.sh @@ -25,7 +25,7 @@ ansible -i "${OUTPUT_DIR}/test_connection.inventory" windows \ # sftp ./windows.sh "$@" # scp -ANSIBLE_SCP_IF_SSH=true ./windows.sh "$@" +ANSIBLE_SSH_TRANSFER_METHOD=scp ./windows.sh "$@" # other tests not part of the generic connection test framework ansible-playbook -i "${OUTPUT_DIR}/test_connection.inventory" tests.yml \ "$@" @@ -49,6 +49,6 @@ ansible -i "${OUTPUT_DIR}/test_connection.inventory" windows \ "$@" ./windows.sh "$@" -ANSIBLE_SCP_IF_SSH=true ./windows.sh "$@" +ANSIBLE_SSH_TRANSFER_METHOD=scp ./windows.sh "$@" ansible-playbook -i "${OUTPUT_DIR}/test_connection.inventory" tests.yml \ "$@" diff --git a/test/integration/targets/copy/tasks/main.yml b/test/integration/targets/copy/tasks/main.yml index 33a92bf9..e02c0232 100644 --- a/test/integration/targets/copy/tasks/main.yml +++ b/test/integration/targets/copy/tasks/main.yml @@ -74,6 +74,9 @@ - import_tasks: acls.yml when: ansible_system == 'Linux' + - import_tasks: selinux.yml + when: ansible_os_family == 'RedHat' and ansible_selinux.get('mode') == 'enforcing' + - import_tasks: no_log.yml - import_tasks: check_mode.yml diff --git a/test/integration/targets/cron/tasks/main.yml b/test/integration/targets/cron/tasks/main.yml index 3537b48d..899ec549 100644 --- a/test/integration/targets/cron/tasks/main.yml +++ b/test/integration/targets/cron/tasks/main.yml @@ -1,3 +1,22 @@ +- name: Include distribution specific variables + include_vars: "{{ lookup('first_found', search) }}" + vars: + search: + files: + - '{{ ansible_distribution | lower }}.yml' + - '{{ ansible_os_family | lower }}.yml' + - '{{ ansible_system | lower }}.yml' + - default.yml + paths: + - vars + +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=726661 +- name: Work around vixie-cron/PAM issue on old distros + command: sed -i '/pam_loginuid/ s/^/#/' /etc/pam.d/crond + when: + - ansible_distribution in ('RedHat', 'CentOS') + - ansible_distribution_major_version is version('6', '==') + - name: add cron task (check mode enabled, cron task not already created) cron: name: test cron task @@ -128,40 +147,117 @@ # BusyBox does not have /etc/cron.d - name: Removing a cron file when the name is specified is allowed (#57471) + when: ansible_distribution != 'Alpine' block: + - name: Check file does not exist + stat: + path: /etc/cron.d/cron_remove_name + register: cron_file_stats + + - assert: + that: not cron_file_stats.stat.exists + - name: Cron file creation cron: - cron_file: cron_filename + cron_file: cron_remove_name name: "integration test cron" job: 'ls' user: root - name: Cron file deletion cron: - cron_file: cron_filename + cron_file: cron_remove_name name: "integration test cron" state: absent - name: Check file succesfull deletion stat: - path: /etc/cron.d/cron_filename + path: /etc/cron.d/cron_remove_name + register: cron_file_stats + + - assert: + that: not cron_file_stats.stat.exists + +# BusyBox does not have /etc/cron.d +- name: Removing a cron file, which contains only whitespace + when: ansible_distribution != 'Alpine' + block: + - name: Check file does not exist + stat: + path: /etc/cron.d/cron_remove_whitespace register: cron_file_stats - assert: that: not cron_file_stats.stat.exists + - name: Cron file creation + cron: + cron_file: cron_remove_whitespace + name: "integration test cron" + job: 'ls' + user: root + + - name: Add whitespace to cron file + shell: 'printf "\n \n\t\n" >> /etc/cron.d/cron_remove_whitespace' + + - name: Cron file deletion + cron: + cron_file: cron_remove_whitespace + name: "integration test cron" + state: absent + + - name: Check file succesfull deletion + stat: + path: /etc/cron.d/cron_remove_whitespace + register: cron_file_stats + + - assert: + that: not cron_file_stats.stat.exists + +- name: System cron tab does not get removed + block: + - name: Add cron job + cron: + cron_file: "{{ system_crontab }}" + user: root + name: "integration test cron" + job: 'ls' + + - name: Remove cron job + cron: + cron_file: "{{ system_crontab }}" + name: "integration test cron" + state: absent + + - name: Check system crontab still exists + stat: + path: "{{ system_crontab }}" + register: cron_file_stats + + - assert: + that: cron_file_stats.stat.exists + - name: Allow non-ascii chars in job (#69492) + when: ansible_distribution != 'Alpine' block: + - name: Check file does not exist + stat: + path: /etc/cron.d/cron_nonascii + register: cron_file_stats + + - assert: + that: not cron_file_stats.stat.exists + - name: Cron file creation cron: - cron_file: cron_filename + cron_file: cron_nonascii name: "cron job that contain non-ascii chars in job (これは日本語です; This is Japanese)" job: 'echo "うどんは好きだがお化け👻は苦手である。"' user: root - name: "Ensure cron_file contains job string" replace: - path: /etc/cron.d/cron_filename + path: /etc/cron.d/cron_nonascii regexp: "うどんは好きだがお化け👻は苦手である。" replace: "それは機密情報🔓です。" register: find_chars @@ -169,19 +265,20 @@ - name: Cron file deletion cron: - cron_file: cron_filename + cron_file: cron_nonascii name: "cron job that contain non-ascii chars in job (これは日本語です; This is Japanese)" state: absent - name: Check file succesfull deletion stat: - path: /etc/cron.d/cron_filename + path: /etc/cron.d/cron_nonascii register: cron_file_stats - assert: that: not cron_file_stats.stat.exists - name: Allow non-ascii chars in cron_file (#69492) + when: ansible_distribution != 'Alpine' block: - name: Cron file creation with non-ascii filename (これは日本語です; This is Japanese) cron: diff --git a/test/integration/targets/cron/vars/alpine.yml b/test/integration/targets/cron/vars/alpine.yml new file mode 100644 index 00000000..29ca3b94 --- /dev/null +++ b/test/integration/targets/cron/vars/alpine.yml @@ -0,0 +1 @@ +system_crontab: /etc/crontabs/root diff --git a/test/integration/targets/cron/vars/default.yml b/test/integration/targets/cron/vars/default.yml new file mode 100644 index 00000000..69c5de46 --- /dev/null +++ b/test/integration/targets/cron/vars/default.yml @@ -0,0 +1 @@ +system_crontab: /etc/crontab diff --git a/test/integration/targets/debug/nosetfacts.yml b/test/integration/targets/debug/nosetfacts.yml new file mode 100644 index 00000000..231c60e4 --- /dev/null +++ b/test/integration/targets/debug/nosetfacts.yml @@ -0,0 +1,21 @@ +- name: check we dont set facts with debug ansible_facts https://github.com/ansible/ansible/issues/74060 + hosts: localhost + gather_facts: false + tasks: + - name: create namespaced non fact + set_fact: + ansible_facts: + nonfact: 1 + + - name: ensure nonfact does not exist + assert: + that: + - nonfact is not defined + + - name: debug ansible_facts to create issue + debug: var=ansible_facts + + - name: ensure nonfact STILL does not exist + assert: + that: + - nonfact is not defined diff --git a/test/integration/targets/debug/runme.sh b/test/integration/targets/debug/runme.sh index 5ccb1bfd..5faeb782 100755 --- a/test/integration/targets/debug/runme.sh +++ b/test/integration/targets/debug/runme.sh @@ -15,3 +15,6 @@ for i in 1 2 3; do grep "ok: \[localhost\] => (item=$i)" out grep "\"item\": $i" out done + +# ensure debug does not set top level vars when looking at ansible_facts +ansible-playbook nosetfacts.yml "$@" diff --git a/test/integration/targets/delegate_to/delegate_to_lookup_context.yml b/test/integration/targets/delegate_to/delegate_to_lookup_context.yml new file mode 100644 index 00000000..83e24bc4 --- /dev/null +++ b/test/integration/targets/delegate_to/delegate_to_lookup_context.yml @@ -0,0 +1,4 @@ +- hosts: localhost + gather_facts: false + roles: + - delegate_to_lookup_context diff --git a/test/integration/targets/delegate_to/delegate_with_fact_from_delegate_host.yml b/test/integration/targets/delegate_to/delegate_with_fact_from_delegate_host.yml new file mode 100644 index 00000000..16703984 --- /dev/null +++ b/test/integration/targets/delegate_to/delegate_with_fact_from_delegate_host.yml @@ -0,0 +1,18 @@ +- name: ensure we can use fact on delegated host for connection info + hosts: localhost + gather_facts: no + tasks: + - add_host: name=f31 bogus_user=notme ansible_connection=ssh ansible_host=4.2.2.2 + + - name: if not overriding with delegated host info, will not be unreachable + ping: + timeout: 5 + delegate_to: f31 + ignore_errors: true + ignore_unreachable: true + register: delping + + - name: ensure that the expected happened + assert: + that: + - delping is failed diff --git a/test/integration/targets/delegate_to/resolve_vars.yml b/test/integration/targets/delegate_to/resolve_vars.yml new file mode 100644 index 00000000..898c0b0a --- /dev/null +++ b/test/integration/targets/delegate_to/resolve_vars.yml @@ -0,0 +1,16 @@ +--- +- name: though we test for 'vars' this is only for backwards compatibility and the 'vars' variable will be deprecated and removed in the future + hosts: localhost + gather_facts: no + tasks: + - add_host: + name: host1 + ansible_connection: local + +- hosts: localhost + gather_facts: no + vars: + server_name: host1 + tasks: + - command: echo should delegate to host1 with local connection + delegate_to: "{{ vars['server_name'] }}" diff --git a/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/tasks/main.yml b/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/tasks/main.yml new file mode 100644 index 00000000..2b14c554 --- /dev/null +++ b/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/tasks/main.yml @@ -0,0 +1,5 @@ +- name: sends SQL template files to mysql host(s) + debug: + msg: "{{ item }}" + with_fileglob: ../templates/*.j2 + delegate_to: localhost diff --git a/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/templates/one.j2 b/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/templates/one.j2 new file mode 100644 index 00000000..1fad51f6 --- /dev/null +++ b/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/templates/one.j2 @@ -0,0 +1 @@ +{{ inventory_hostname }} diff --git a/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/templates/two.j2 b/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/templates/two.j2 new file mode 100644 index 00000000..1fad51f6 --- /dev/null +++ b/test/integration/targets/delegate_to/roles/delegate_to_lookup_context/templates/two.j2 @@ -0,0 +1 @@ +{{ inventory_hostname }} diff --git a/test/integration/targets/delegate_to/runme.sh b/test/integration/targets/delegate_to/runme.sh index 44059552..af090cdf 100755 --- a/test/integration/targets/delegate_to/runme.sh +++ b/test/integration/targets/delegate_to/runme.sh @@ -63,7 +63,6 @@ ansible-playbook has_hostvars.yml -i inventory -v "$@" # test ansible_x_interpreter # python -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh ( cd "${OUTPUT_DIR}"/venv/bin @@ -72,4 +71,7 @@ ln -s python secondpython ) ansible-playbook verify_interpreter.yml -i inventory_interpreters -v "$@" ansible-playbook discovery_applied.yml -i inventory -v "$@" +ansible-playbook resolve_vars.yml -i inventory -v "$@" +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 "$@" diff --git a/test/integration/targets/delegate_to/test_delegate_to_lookup_context.yml b/test/integration/targets/delegate_to/test_delegate_to_lookup_context.yml new file mode 100644 index 00000000..ae1bb28a --- /dev/null +++ b/test/integration/targets/delegate_to/test_delegate_to_lookup_context.yml @@ -0,0 +1,12 @@ +- hosts: localhost + gather_facts: false + vars: + verbosity: "{{ '' if not ansible_verbosity else '-' ~ ('v' * ansible_verbosity) }}" + tasks: + - command: ansible-playbook {{ verbosity }} delegate_to_lookup_context.yml + register: result + + - assert: + that: + - > + '[WARNING]: Unable to find' not in result.stderr diff --git a/test/integration/targets/dict_transformations/tasks/test_convert_snake_case.yml b/test/integration/targets/dict_transformations/tasks/test_convert_snake_case.yml index ba80aa7a..cf700bc0 100644 --- a/test/integration/targets/dict_transformations/tasks/test_convert_snake_case.yml +++ b/test/integration/targets/dict_transformations/tasks/test_convert_snake_case.yml @@ -24,3 +24,12 @@ - assert: that: - "result.data == {'results': [{'i_a_m_user': 'UserName', 'tags': {'do_convert': 'DoNotConvert'}}], 'tags': {'DoNotConvert': 'DoNotConvert'}}" + +- name: Test converting dict keys in lists within lists + convert_snake_case: + data: {'Results': [{'Changes': [{'DoConvert': 'DoNotConvert', 'Details': ['DoNotConvert']}]}]} + register: result + +- assert: + that: + - "result.data == {'results': [{'changes': [{'do_convert': 'DoNotConvert', 'details': ['DoNotConvert']}]}]}" diff --git a/test/integration/targets/dnf/tasks/dnf.yml b/test/integration/targets/dnf/tasks/dnf.yml index 19008188..7e6a8d8f 100644 --- a/test/integration/targets/dnf/tasks/dnf.yml +++ b/test/integration/targets/dnf/tasks/dnf.yml @@ -112,8 +112,8 @@ - "not dnf_result.changed" # Multiple packages -- name: uninstall sos and pciutils - dnf: name=sos,pciutils state=removed +- name: uninstall sos and dos2unix + dnf: name=sos,dos2unix state=removed register: dnf_result - name: check sos with rpm @@ -121,19 +121,19 @@ failed_when: False register: rpm_sos_result -- name: check pciutils with rpm - shell: rpm -q pciutils +- name: check dos2unix with rpm + shell: rpm -q dos2unix failed_when: False - register: rpm_pciutils_result + register: rpm_dos2unix_result - name: verify packages installed assert: that: - "rpm_sos_result.rc != 0" - - "rpm_pciutils_result.rc != 0" + - "rpm_dos2unix_result.rc != 0" -- name: install sos and pciutils as comma separated - dnf: name=sos,pciutils state=present +- name: install sos and dos2unix as comma separated + dnf: name=sos,dos2unix state=present register: dnf_result - name: check sos with rpm @@ -141,10 +141,10 @@ failed_when: False register: rpm_sos_result -- name: check pciutils with rpm - shell: rpm -q pciutils +- name: check dos2unix with rpm + shell: rpm -q dos2unix failed_when: False - register: rpm_pciutils_result + register: rpm_dos2unix_result - name: verify packages installed assert: @@ -152,17 +152,17 @@ - "not dnf_result.failed | default(False)" - "dnf_result.changed" - "rpm_sos_result.rc == 0" - - "rpm_pciutils_result.rc == 0" + - "rpm_dos2unix_result.rc == 0" -- name: uninstall sos and pciutils - dnf: name=sos,pciutils state=removed +- name: uninstall sos and dos2unix + dnf: name=sos,dos2unix state=removed register: dnf_result -- name: install sos and pciutils as list +- name: install sos and dos2unix as list dnf: name: - sos - - pciutils + - dos2unix state: present register: dnf_result @@ -171,10 +171,10 @@ failed_when: False register: rpm_sos_result -- name: check pciutils with rpm - shell: rpm -q pciutils +- name: check dos2unix with rpm + shell: rpm -q dos2unix failed_when: False - register: rpm_pciutils_result + register: rpm_dos2unix_result - name: verify packages installed assert: @@ -182,17 +182,17 @@ - "not dnf_result.failed | default(False)" - "dnf_result.changed" - "rpm_sos_result.rc == 0" - - "rpm_pciutils_result.rc == 0" + - "rpm_dos2unix_result.rc == 0" -- name: uninstall sos and pciutils +- name: uninstall sos and dos2unix dnf: - name: "sos,pciutils" + name: "sos,dos2unix" state: removed register: dnf_result -- name: install sos and pciutils as comma separated with spaces +- name: install sos and dos2unix as comma separated with spaces dnf: - name: "sos, pciutils" + name: "sos, dos2unix" state: present register: dnf_result @@ -202,9 +202,9 @@ register: rpm_sos_result - name: check sos with rpm - shell: rpm -q pciutils + shell: rpm -q dos2unix failed_when: False - register: rpm_pciutils_result + register: rpm_dos2unix_result - name: verify packages installed assert: @@ -212,13 +212,13 @@ - "not dnf_result.failed | default(False)" - "dnf_result.changed" - "rpm_sos_result.rc == 0" - - "rpm_pciutils_result.rc == 0" + - "rpm_dos2unix_result.rc == 0" -- name: uninstall sos and pciutils (check_mode) +- name: uninstall sos and dos2unix (check_mode) dnf: name: - sos - - pciutils + - dos2unix state: removed check_mode: True register: dnf_result @@ -230,11 +230,11 @@ - "dnf_result.results[0].startswith('Removed: ')" - "dnf_result.results[1].startswith('Removed: ')" -- name: uninstall sos and pciutils +- name: uninstall sos and dos2unix dnf: name: - sos - - pciutils + - dos2unix state: removed register: dnf_result @@ -488,7 +488,7 @@ - name: try to install from non existing url dnf: - name: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/dnf/non-existing-1.0.0.fc26.noarch.rpm + name: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/dnf/non-existing-1.0.0.fc26.noarch.rpm state: present register: dnf_result ignore_errors: yes @@ -583,7 +583,7 @@ - name: try to install not compatible arch rpm, should fail dnf: - name: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/dnf/banner-1.3.4-3.el7.ppc64le.rpm + name: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/dnf/banner-1.3.4-3.el7.ppc64le.rpm state: present register: dnf_result ignore_errors: True @@ -594,28 +594,22 @@ - "not dnf_result is changed" - "dnf_result is failed" -# setup for testing installing an RPM from url +# setup for testing installing an RPM from local file - set_fact: - pkg_name: fpaste + pkg_name: noarchfake + pkg_path: '{{ repodir }}/noarchfake-1.0-1.noarch.rpm' - name: cleanup dnf: name: "{{ pkg_name }}" state: absent -- set_fact: - pkg_url: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/dnf/fpaste-0.3.9.1-1.fc27.noarch.rpm # setup end -- name: download an rpm - get_url: - url: "{{ pkg_url }}" - dest: "/tmp/{{ pkg_name }}.rpm" - -- name: install the downloaded rpm +- name: install a local noarch rpm from file dnf: - name: "/tmp/{{ pkg_name }}.rpm" + name: "{{ pkg_path }}" state: present disable_gpg_check: true register: dnf_result @@ -628,7 +622,7 @@ - name: install the downloaded rpm again dnf: - name: "/tmp/{{ pkg_name }}.rpm" + name: "{{ pkg_path }}" state: present register: dnf_result @@ -645,7 +639,7 @@ - name: install from url dnf: - name: "{{ pkg_url }}" + name: "file://{{ pkg_path }}" state: present disable_gpg_check: true register: dnf_result @@ -772,3 +766,53 @@ that: - wildcard_absent is successful - wildcard_absent is not changed + +- name: Test removing with various package specs + block: + - name: Ensure sos is installed + dnf: + name: sos + state: present + + - name: Determine version of sos + command: rpm -q --queryformat=%{version} sos + register: sos_version_command + + - name: Determine release of sos + command: rpm -q --queryformat=%{release} sos + register: sos_release_command + + - name: Determine arch of sos + command: rpm -q --queryformat=%{arch} sos + register: sos_arch_command + + - set_fact: + sos_version: "{{ sos_version_command.stdout | trim }}" + sos_release: "{{ sos_release_command.stdout | trim }}" + sos_arch: "{{ sos_arch_command.stdout | trim }}" + + # We are just trying to remove the package by specifying its spec in a + # bunch of different ways. Each "item" here is a test (a name passed, to make + # sure it matches, with how we call Hawkey in the dnf module). + - include_tasks: test_sos_removal.yml + with_items: + - sos + - sos-{{ sos_version }} + - sos-{{ sos_version }}-{{ sos_release }} + - sos-{{ sos_version }}-{{ sos_release }}.{{ sos_arch }} + - sos-*-{{ sos_release }} + - sos-{{ sos_version[0] }}* + - sos-{{ sos_version[0] }}*-{{ sos_release }} + - sos-{{ sos_version[0] }}*{{ sos_arch }} + + - name: Ensure deleting a non-existing package fails correctly + dnf: + name: a-non-existent-package + state: absent + ignore_errors: true + register: nonexisting + + - assert: + that: + - nonexisting is success + - nonexisting.msg == 'Nothing to do' diff --git a/test/integration/targets/dnf/tasks/dnfinstallroot.yml b/test/integration/targets/dnf/tasks/dnfinstallroot.yml index b5e09011..19f67069 100644 --- a/test/integration/targets/dnf/tasks/dnfinstallroot.yml +++ b/test/integration/targets/dnf/tasks/dnfinstallroot.yml @@ -3,21 +3,9 @@ command: mktemp -d "{{ remote_tmp_dir }}/ansible.test.XXXXXX" register: dnfroot -- name: Make a necessary directory - file: - path: "/{{ dnfroot.stdout }}/etc/dnf/vars/" - state: directory - mode: 0755 - -- name: Populate directory - copy: - # We need '8' for CentOS, but '8.x' for RHEL. - content: "{{ ansible_distribution_version|int if ansible_distribution != 'RedHat' else ansible_distribution_version }}\n" - dest: "/{{ dnfroot.stdout }}/etc/dnf/vars/releasever" - # This will drag in > 200 MB. - name: attempt installroot - dnf: name=sos installroot="/{{ dnfroot.stdout }}/" disable_gpg_check=yes + dnf: name=sos installroot="/{{ dnfroot.stdout }}/" disable_gpg_check=yes releasever={{ansible_facts['distribution_major_version']}} register: dnf_result - name: check sos with rpm in installroot diff --git a/test/integration/targets/dnf/tasks/filters.yml b/test/integration/targets/dnf/tasks/filters.yml index d5e9ee90..2bff25bc 100644 --- a/test/integration/targets/dnf/tasks/filters.yml +++ b/test/integration/targets/dnf/tasks/filters.yml @@ -98,6 +98,19 @@ allow_downgrade: true disable_gpg_check: true + - name: Verify toaster is not upgraded with state=installed + dnf: + name: "{{ item }}" + state: installed + register: installed + loop: + - toaster + - toaster.noarch + - toaster-1.2.3.4-1.el8.noarch + + - assert: + that: "installed.results | map(attribute='changed') is not any" + - name: Ask for pending updates with bugfix=true and security=true dnf: name: '*' @@ -118,6 +131,21 @@ - '"Installed: oven-1.2.3.5-1.el8.noarch" in update_bugfix.results' - '"Removed: oven-1.2.3.4-1.el8.noarch" in update_bugfix.results' + - name: Install old version of toaster again + dnf: + name: "{{ updateinfo_repo }}/toaster-1.2.3.4-1.el8.noarch.rpm" + allow_downgrade: true + disable_gpg_check: true + + - name: Verify toaster is upgraded with state=latest + dnf: + name: toaster.noarch + state: latest + register: result + + - assert: + that: result.changed + always: - name: Remove installed packages dnf: diff --git a/test/integration/targets/dnf/tasks/gpg.yml b/test/integration/targets/dnf/tasks/gpg.yml index 2b6f4079..184270a0 100644 --- a/test/integration/targets/dnf/tasks/gpg.yml +++ b/test/integration/targets/dnf/tasks/gpg.yml @@ -1,9 +1,13 @@ # Set up a repo of unsigned rpms - block: + - set_fact: + pkg_name: langtable + pkg_repo_dir: "{{ remote_tmp_dir }}/unsigned" + - name: Ensure our test package isn't already installed dnf: name: - - fpaste + - '{{ pkg_name }}' state: absent - name: Install rpm-sign @@ -14,35 +18,36 @@ - name: Create directory to use as local repo file: - path: "{{ remote_tmp_dir }}/unsigned" + path: "{{ pkg_repo_dir }}" state: directory - - name: Download an RPM - get_url: - url: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/dnf/fpaste-0.3.9.1-1.fc27.noarch.rpm - dest: "{{ remote_tmp_dir }}/unsigned/fpaste-0.3.9.1-1.fc27.noarch.rpm" - mode: 0644 + - name: Download the test package + dnf: + name: '{{ pkg_name }}' + state: latest + download_only: true + download_dir: "{{ pkg_repo_dir }}" - name: Unsign the RPM - command: rpmsign --delsign "{{ remote_tmp_dir }}/unsigned/fpaste-0.3.9.1-1.fc27.noarch.rpm" + shell: rpmsign --delsign {{ remote_tmp_dir }}/unsigned/{{ pkg_name }}* - name: createrepo command: createrepo . args: - chdir: "{{ remote_tmp_dir }}/unsigned" + chdir: "{{ pkg_repo_dir }}" - name: Add the repo yum_repository: name: unsigned description: unsigned rpms - baseurl: "file://{{ remote_tmp_dir }}/unsigned/" + baseurl: "file://{{ pkg_repo_dir }}" # we want to ensure that signing is verified gpgcheck: true - - name: Install fpaste from above + - name: Install test package dnf: name: - - fpaste + - "{{ pkg_name }}" disablerepo: '*' enablerepo: unsigned register: res @@ -54,11 +59,11 @@ - "'Failed to validate GPG signature' in res.msg" always: - - name: Remove rpm-sign (and fpaste if it got installed) + - name: Remove rpm-sign (and test package if it got installed) dnf: name: - rpm-sign - - fpaste + - "{{ pkg_name }}" state: absent - name: Remove test repo @@ -68,5 +73,5 @@ - name: Remove repo dir file: - path: "{{ remote_tmp_dir }}/unsigned" + path: "{{ pkg_repo_dir }}" state: absent diff --git a/test/integration/targets/dnf/tasks/logging.yml b/test/integration/targets/dnf/tasks/logging.yml index 4cbeaa61..903bf563 100644 --- a/test/integration/targets/dnf/tasks/logging.yml +++ b/test/integration/targets/dnf/tasks/logging.yml @@ -6,6 +6,7 @@ name: - python3-dnf - python3-libdnf # https://bugzilla.redhat.com/show_bug.cgi?id=1887502 + - libmodulemd # https://bugzilla.redhat.com/show_bug.cgi?id=1942236 state: latest register: dnf_result diff --git a/test/integration/targets/dnf/tasks/main.yml b/test/integration/targets/dnf/tasks/main.yml index 1b6e0941..51ab7d20 100644 --- a/test/integration/targets/dnf/tasks/main.yml +++ b/test/integration/targets/dnf/tasks/main.yml @@ -60,3 +60,9 @@ - include_tasks: logging.yml when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('31', '>=')) or (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + +# TODO: Construct our own instance where 'nobest' applies, so we can stop using +# a third-party repo to test this behavior. +- include_tasks: nobest.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('24', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) diff --git a/test/integration/targets/dnf/tasks/modularity.yml b/test/integration/targets/dnf/tasks/modularity.yml index 48a0111a..94f43a40 100644 --- a/test/integration/targets/dnf/tasks/modularity.yml +++ b/test/integration/targets/dnf/tasks/modularity.yml @@ -1,6 +1,11 @@ # FUTURE - look at including AppStream support in our local repo - name: Include distribution specific variables - include_vars: "{{ ansible_facts.distribution }}.yml" + include_vars: "{{ item }}" + with_first_found: + - files: + - "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml" + - "{{ ansible_facts.distribution }}.yml" + paths: ../vars - name: install "{{ astream_name }}" module dnf: diff --git a/test/integration/targets/dnf/tasks/nobest.yml b/test/integration/targets/dnf/tasks/nobest.yml new file mode 100644 index 00000000..38f0e373 --- /dev/null +++ b/test/integration/targets/dnf/tasks/nobest.yml @@ -0,0 +1,34 @@ +- name: Install dnf-plugins-core in order to use dnf config-manager + dnf: + name: dnf-plugins-core + state: present + +- name: Add docker-ce repo (Only RedHat & CentOS) + shell: dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo + when: (ansible_distribution in ['RedHat', 'CentOS']) + +- name: Add docker-ce repo (Only Fedora) + shell: dnf config-manager --add-repo=https://download.docker.com/linux/fedora/docker-ce.repo + when: (ansible_distribution in ['Fedora']) + +- name: Install docker using nobest option + dnf: + name: docker-ce + state: present + nobest: true + register: dnf_result + +- name: Verify installation of docker-ce + assert: + that: + - not dnf_result is failed + +- name: Cleanup packages + dnf: + name: docker-ce, dnf-plugins-core + state: absent + +- name: Cleanup manually added repos + file: + name: "/etc/yum.repos.d/docker-ce.repo" + state: absent diff --git a/test/integration/targets/dnf/tasks/test_sos_removal.yml b/test/integration/targets/dnf/tasks/test_sos_removal.yml new file mode 100644 index 00000000..40ceb62b --- /dev/null +++ b/test/integration/targets/dnf/tasks/test_sos_removal.yml @@ -0,0 +1,19 @@ +# These are safe to just check in check_mode, because in the module, the +# logic to match packages will happen anyway. check_mode will just prevent +# the transaction from actually running once the matches are found. +- name: Remove {{ item }} + dnf: + name: "{{ item }}" + state: absent + check_mode: true + register: sos_rm + +- debug: + var: sos_rm + +- assert: + 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 diff --git a/test/integration/targets/dnf/vars/Fedora-33.yml b/test/integration/targets/dnf/vars/Fedora-33.yml new file mode 100644 index 00000000..859059f1 --- /dev/null +++ b/test/integration/targets/dnf/vars/Fedora-33.yml @@ -0,0 +1,2 @@ +astream_name: '@httpd:2.4/common' +astream_name_no_stream: '@httpd/common' diff --git a/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml b/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml index 5a46fcd9..cd50f436 100644 --- a/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml +++ b/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml @@ -1,5 +1,5 @@ - name: download and install old version of hello - apt: "deb=https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/dpkg_selections/hello_{{ hello_old_version }}_amd64.deb" + apt: "deb=https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/dpkg_selections/hello_{{ hello_old_version }}_amd64.deb" - name: freeze version for hello dpkg_selections: 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 2a2c8795..59a81a1b 100644 --- a/test/integration/targets/error_from_connection/connection_plugins/dummy.py +++ b/test/integration/targets/error_from_connection/connection_plugins/dummy.py @@ -26,9 +26,6 @@ class Connection(ConnectionBase): raise AnsibleError('an error with {{ some Jinja }}') - def transport(self): - pass - def _connect(self): pass diff --git a/test/integration/targets/expect/files/test_command.py b/test/integration/targets/expect/files/test_command.py index 685c50c2..0e0e2646 100644 --- a/test/integration/targets/expect/files/test_command.py +++ b/test/integration/targets/expect/files/test_command.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import sys try: @@ -7,6 +10,16 @@ except NameError: prompts = sys.argv[1:] or ['foo'] +# latin1 encoded bytes +# to ensure pexpect doesn't have any encoding errors +data = b'premi\xe8re is first\npremie?re is slightly different\n????????? is Cyrillic\n? am Deseret\n' + +try: + sys.stdout.buffer.write(data) +except AttributeError: + sys.stdout.write(data) +print() + for prompt in prompts: user_input = input_function(prompt) print(user_input) diff --git a/test/integration/targets/expect/tasks/main.yml b/test/integration/targets/expect/tasks/main.yml index 0c408d28..168663c9 100644 --- a/test/integration/targets/expect/tasks/main.yml +++ b/test/integration/targets/expect/tasks/main.yml @@ -43,7 +43,7 @@ assert: that: - "expect_result.changed == true" - - "expect_result.stdout == 'foobar'" + - "expect_result.stdout_lines|last == 'foobar'" - name: test creates option expect: @@ -71,7 +71,7 @@ assert: that: - "creates_result.changed == true" - - "creates_result.stdout == 'foobar'" + - "creates_result.stdout_lines|last == 'foobar'" - name: test removes option expect: @@ -85,7 +85,7 @@ assert: that: - "removes_result.changed == true" - - "removes_result.stdout == 'foobar'" + - "removes_result.stdout_lines|last == 'foobar'" - name: test removes option (missing) expect: @@ -139,9 +139,9 @@ - name: assert echo works assert: that: - - "echo_result.stdout_lines|length == 2" - - "echo_result.stdout_lines[0] == 'foobar'" - - "echo_result.stdout_lines[1] == 'bar'" + - "echo_result.stdout_lines|length == 7" + - "echo_result.stdout_lines[-2] == 'foobar'" + - "echo_result.stdout_lines[-1] == 'bar'" - name: test response list expect: @@ -155,9 +155,9 @@ - name: assert list response works assert: that: - - "list_result.stdout_lines|length == 2" - - "list_result.stdout_lines[0] == 'foobar'" - - "list_result.stdout_lines[1] == 'foobaz'" + - "list_result.stdout_lines|length == 7" + - "list_result.stdout_lines[-2] == 'foobar'" + - "list_result.stdout_lines[-1] == 'foobaz'" - name: test no remaining responses expect: diff --git a/test/integration/targets/facts_d/files/basdscript.fact b/test/integration/targets/facts_d/files/basdscript.fact new file mode 100644 index 00000000..2bb8d868 --- /dev/null +++ b/test/integration/targets/facts_d/files/basdscript.fact @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 1 diff --git a/test/integration/targets/facts_d/files/goodscript.fact b/test/integration/targets/facts_d/files/goodscript.fact new file mode 100644 index 00000000..6ee866cf --- /dev/null +++ b/test/integration/targets/facts_d/files/goodscript.fact @@ -0,0 +1,3 @@ +#!/bin/sh + +echo '{"script_ran": true}' diff --git a/test/integration/targets/facts_d/files/preferences.fact b/test/integration/targets/facts_d/files/preferences.fact new file mode 100644 index 00000000..c32583d4 --- /dev/null +++ b/test/integration/targets/facts_d/files/preferences.fact @@ -0,0 +1,2 @@ +[general] +bar=loaded diff --git a/test/integration/targets/facts_d/files/unreadable.fact b/test/integration/targets/facts_d/files/unreadable.fact new file mode 100644 index 00000000..98f562be --- /dev/null +++ b/test/integration/targets/facts_d/files/unreadable.fact @@ -0,0 +1 @@ +wontbeseen=ever diff --git a/test/integration/targets/facts_d/tasks/main.yml b/test/integration/targets/facts_d/tasks/main.yml index ca23544f..aadef4c6 100644 --- a/test/integration/targets/facts_d/tasks/main.yml +++ b/test/integration/targets/facts_d/tasks/main.yml @@ -1,35 +1,36 @@ -# Test code for facts.d and setup filters # (c) 2014, James Tanner <tanner.jc@gmail.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +- name: prep for local facts tests + block: + - name: set factdir var + set_fact: fact_dir={{output_dir}}/facts.d -- set_fact: fact_dir={{output_dir}}/facts.d + - name: create fact dir + file: path={{ fact_dir }} state=directory -- file: path={{ fact_dir }} state=directory -- shell: echo "[general]" > {{ fact_dir }}/preferences.fact -- shell: echo "bar=loaded" >> {{ fact_dir }}/preferences.fact + - name: copy local facts test files + copy: src={{ item['name'] }}.fact dest={{ fact_dir }}/ mode={{ item['mode']|default(omit) }} + loop: + - name: preferences + - name: basdscript + mode: '0775' + - name: goodscript + mode: '0775' + - name: unreadable + mode: '0000' -- setup: +- name: force fact gather to get ansible_local + setup: fact_path: "{{ fact_dir | expanduser }}" filter: "*local*" register: setup_result -- debug: var=setup_result +- name: show gathering results if rerun with -vvv + debug: var=setup_result verbosity=3 -- assert: +- name: check for expected results from local facts + assert: that: - "'ansible_facts' in setup_result" - "'ansible_local' in setup_result.ansible_facts" @@ -39,3 +40,6 @@ - "'general' in setup_result.ansible_facts['ansible_local']['preferences']" - "'bar' in setup_result.ansible_facts['ansible_local']['preferences']['general']" - "setup_result.ansible_facts['ansible_local']['preferences']['general']['bar'] == 'loaded'" + - setup_result['ansible_facts']['ansible_local']['goodscript']['script_ran']|bool + - setup_result['ansible_facts']['ansible_local']['basdscript'].startswith("Failure executing fact script") + - setup_result['ansible_facts']['ansible_local']['unreadable'].startswith('error loading facts') diff --git a/test/integration/targets/file/handlers/main.yml b/test/integration/targets/file/handlers/main.yml new file mode 100644 index 00000000..b5040f6e --- /dev/null +++ b/test/integration/targets/file/handlers/main.yml @@ -0,0 +1,19 @@ +- name: remove users + user: + name: "{{ item }}" + state: absent + remove: yes + loop: + - test1 + - test_uid + - nonexistent + - "{{ remote_unprivileged_user }}" + +- name: remove groups + group: + name: "{{ item }}" + state: absent + loop: + - test1 + - test_gid + - nonexistent1 diff --git a/test/integration/targets/file/tasks/link_rewrite.yml b/test/integration/targets/file/tasks/link_rewrite.yml new file mode 100644 index 00000000..b0e1af3e --- /dev/null +++ b/test/integration/targets/file/tasks/link_rewrite.yml @@ -0,0 +1,47 @@ +- name: create temporary build directory + tempfile: + state: directory + suffix: ansible_test_leave_links_alone_during_touch + register: tempdir + +- name: create file + copy: + mode: 0600 + content: "chicken" + dest: "{{ tempdir.path }}/somefile" + +- name: Create relative link + file: + src: somefile + dest: "{{ tempdir.path }}/somelink" + state: link + +- stat: + path: "{{ tempdir.path }}/somelink" + register: link + +- stat: + path: "{{ tempdir.path }}/somefile" + register: file + +- assert: + that: + - "file.stat.mode == '0600'" + - "link.stat.lnk_target == 'somefile'" + +- file: + path: "{{ tempdir.path }}/somelink" + mode: 0644 + +- stat: + path: "{{ tempdir.path }}/somelink" + register: link + +- stat: + path: "{{ tempdir.path }}/somefile" + register: file + +- assert: + that: + - "file.stat.mode == '0644'" + - "link.stat.lnk_target == 'somefile'" diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml index 34ae4ba4..565afa02 100644 --- a/test/integration/targets/file/tasks/main.yml +++ b/test/integration/targets/file/tasks/main.yml @@ -124,10 +124,7 @@ ignore_errors: yes - name: get attributes from file - # Use of `-v` is important, as that is what the module does (through `set_attributes_if_different` and then `get_file_attributes` in basic.py). - # On some systems, such as in containers, attributes work, but file versions may not. - # It should be possible to update `set_attributes_if_different` in the future to not use `-v` since the file version is unrelated to the attributes. - command: lsattr -vd "{{ attributes_file }}" + command: lsattr -d "{{ attributes_file }}" register: attribute_A_set ignore_errors: yes @@ -136,8 +133,7 @@ ignore_errors: yes - name: get attributes from file - # See the note above on use of the `-v` option. - command: lsattr -vd "{{ attributes_file }}" + command: lsattr -d "{{ attributes_file }}" register: attribute_A_unset ignore_errors: yes @@ -146,9 +142,9 @@ attributes_supported: yes when: - attribute_A_set is success - - "'A' in attribute_A_set.stdout_lines[0].split()[1]" + - "'A' in attribute_A_set.stdout_lines[0].split()[0]" - attribute_A_unset is success - - "'A' not in attribute_A_unset.stdout_lines[0].split()[1]" + - "'A' not in attribute_A_unset.stdout_lines[0].split()[0]" - name: explicitly set file attribute "A" file: path={{output_dir}}/baz.txt attributes=A @@ -183,6 +179,18 @@ - "file_attributes_result_4 is not changed" when: file_attributes_result_4 is changed +- name: create user + user: + name: test1 + uid: 1234 + notify: remove users + +- name: create group + group: + name: test1 + gid: 1234 + notify: remove groups + - name: change ownership and group file: path={{output_dir}}/baz.txt owner=1234 group=1234 @@ -370,6 +378,11 @@ that: - "file9_result.uid != 1234" +- name: create user + user: + name: test2 + uid: 1235 + - name: change the ownership of a directory with recurse=yes file: path={{output_dir}}/foobar owner=1235 recurse=yes @@ -579,6 +592,114 @@ that: - result.mode == '0444' +# https://github.com/ansible/ansible/issues/67307 +# Test the module fails in check_mode when directory and owner/group do not exist +# I don't use state=touch here intentionally to fail and catch warnings +- name: owner does not exist in check_mode + file: + path: '/tmp/nonexistent' + owner: nonexistent + check_mode: yes + register: owner_no_exist + ignore_errors: yes + +- name: create owner + user: + name: nonexistent + notify: remove users + +# I don't use state=touch here intentionally to fail and catch warnings +- name: owner exist in check_mode + file: + path: '/tmp/nonexistent' + owner: nonexistent + check_mode: yes + register: owner_exists + ignore_errors: yes + +# I don't use state=touch here intentionally to fail and catch warnings +- name: owner does not exist in check_mode, using uid + file: + path: '/tmp/nonexistent' + owner: '111111' + check_mode: yes + ignore_errors: yes + register: owner_uid_no_exist + +- name: create owner using uid + user: + name: test_uid + uid: 111111 + notify: remove users + +# I don't use state=touch here intentionally to fail and catch warnings +- name: owner exists in check_mode, using uid + file: + path: '/tmp/nonexistent' + owner: '111111' + state: touch + check_mode: yes + ignore_errors: yes + register: owner_uid_exists + +# I don't use state=touch here intentionally to fail and catch warnings +- name: group does not exist in check_mode + file: + path: '/tmp/nonexistent' + group: nonexistent1 + check_mode: yes + register: group_no_exist + ignore_errors: yes + +- name: create group + group: + name: nonexistent1 + notify: remove groups + +# I don't use state=touch here intentionally to fail and catch warnings +- name: group exists in check_mode + file: + path: '/tmp/nonexistent' + group: nonexistent1 + check_mode: yes + register: group_exists + ignore_errors: yes + +# I don't use state=touch here intentionally to fail and catch warnings +- name: group does not exist in check_mode, using gid + file: + path: '/tmp/nonexistent' + group: '111112' + check_mode: yes + register: group_gid_no_exist + ignore_errors: yes + +- name: create group with gid + group: + name: test_gid + gid: 111112 + notify: remove groups + +# I don't use state=touch here intentionally to fail and catch warnings +- name: group exists in check_mode, using gid + file: + path: '/tmp/nonexistent' + group: '111112' + check_mode: yes + register: group_gid_exists + ignore_errors: yes + +- assert: + that: + - owner_no_exist.warnings[0] is search('failed to look up user') + - owner_uid_no_exist.warnings[0] is search('failed to look up user with uid') + - group_no_exist.warnings[0] is search('failed to look up group') + - group_gid_no_exist.warnings[0] is search('failed to look up group with gid') + - owner_exists.warnings is not defined + - owner_uid_exists.warnings is not defined + - group_exists.warnings is not defined + - group_gid_exists.warnings is not defined + # https://github.com/ansible/ansible/issues/50943 # Need to use /tmp as nobody can't access output_dir at all - name: create file as root with all write permissions diff --git a/test/integration/targets/file/tasks/state_link.yml b/test/integration/targets/file/tasks/state_link.yml index 89150adc..d84bb310 100644 --- a/test/integration/targets/file/tasks/state_link.yml +++ b/test/integration/targets/file/tasks/state_link.yml @@ -125,6 +125,7 @@ user: name: '{{ remote_unprivileged_user }}' register: user + notify: remove users - name: Create a local temporary directory tempfile: @@ -388,7 +389,6 @@ - name: assert that the link target was unmodified assert: that: - - 'file10_result is changed' - 'file10_target_stat["stat"]["mode"] == "0644"' diff --git a/test/integration/targets/filter_core/tasks/main.yml b/test/integration/targets/filter_core/tasks/main.yml index 2197febd..8ab9d446 100644 --- a/test/integration/targets/filter_core/tasks/main.yml +++ b/test/integration/targets/filter_core/tasks/main.yml @@ -110,9 +110,20 @@ that: - '"{{ "hash" | hash("sha1") }}" == "2346ad27d7568ba9896f1b7da6b5991251debdf2"' - '"{{ "café" | hash("sha1") }}" == "f424452a9673918c6f09b0cdd35b20be8e6ae7d7"' - - '"corned beef"|hash("haha, get it?") == None' + +- name: Test unsupported hash type + debug: + msg: "{{ 'hash' | hash('unsupported_hash_type') }}" + ignore_errors: yes + register: unsupported_hash_type_res + +- assert: + that: + - "unsupported_hash_type_res is failed" + - "'unsupported hash type' in unsupported_hash_type_res.msg" - name: Flatten tests + tags: flatten block: - name: use flatten set_fact: @@ -120,6 +131,12 @@ flat_one: '{{orig_list|flatten(levels=1)}}' flat_two: '{{orig_list|flatten(levels=2)}}' flat_tuples: '{{ [1,3] | zip([2,4]) | list | flatten }}' + flat_full_null: '{{list_with_nulls|flatten(skip_nulls=False)}}' + flat_one_null: '{{list_with_nulls|flatten(levels=1, skip_nulls=False)}}' + flat_two_null: '{{list_with_nulls|flatten(levels=2, skip_nulls=False)}}' + flat_full_nonull: '{{list_with_nulls|flatten(skip_nulls=True)}}' + flat_one_nonull: '{{list_with_nulls|flatten(levels=1, skip_nulls=True)}}' + flat_two_nonull: '{{list_with_nulls|flatten(levels=2, skip_nulls=True)}}' - name: Verify flatten filter works as expected assert: @@ -128,8 +145,18 @@ - flat_one == [1, 2, 3, [4, [5]], 6, 7] - flat_two == [1, 2, 3, 4, [5], 6, 7] - flat_tuples == [1, 2, 3, 4] + - flat_full_null == [1, 'None', 3, 4, 5, 6, 7] + - flat_one_null == [1, 'None', 3, [4, [5]], 6, 7] + - flat_two_null == [1, 'None', 3, 4, [5], 6, 7] + - flat_full_nonull == [1, 3, 4, 5, 6, 7] + - flat_one_nonull == [1, 3, [4, [5]], 6, 7] + - flat_two_nonull == [1, 3, 4, [5], 6, 7] + - list_with_subnulls|flatten(skip_nulls=False) == [1, 2, 'None', 4, 5, 6, 7] + - list_with_subnulls|flatten(skip_nulls=True) == [1, 2, 4, 5, 6, 7] vars: orig_list: [1, 2, [3, [4, [5]], 6], 7] + list_with_nulls: [1, None, [3, [4, [5]], 6], 7] + list_with_subnulls: [1, 2, [None, [4, [5]], 6], 7] - name: Test base64 filter assert: @@ -574,3 +601,19 @@ bar: 123 thing_items: '{{ thing_dict.items() }}' thing_range: '{{ range(10) }}' + +- name: Assert that quote works on None + assert: + that: + - thing|quote == "''" + vars: + thing: null + +- name: split filter + assert: + that: + - splitty|map('split', ',')|flatten|map('int') == [1, 2, 3, 4, 5, 6] + vars: + splitty: + - "1,2,3" + - "4,5,6" diff --git a/test/integration/targets/filter_urls/runme.sh b/test/integration/targets/filter_urls/runme.sh index 9362a385..f6460acb 100755 --- a/test/integration/targets/filter_urls/runme.sh +++ b/test/integration/targets/filter_urls/runme.sh @@ -6,7 +6,6 @@ export ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@" -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh # This is necessary for installing Jinja 2.6. We need this because Jinja 2.6 diff --git a/test/integration/targets/find/files/a.txt b/test/integration/targets/find/files/a.txt new file mode 100644 index 00000000..30b622a3 --- /dev/null +++ b/test/integration/targets/find/files/a.txt @@ -0,0 +1,2 @@ +this is a file that has +a few lines in it diff --git a/test/integration/targets/find/files/log.txt b/test/integration/targets/find/files/log.txt new file mode 100644 index 00000000..679893bc --- /dev/null +++ b/test/integration/targets/find/files/log.txt @@ -0,0 +1,4 @@ +01/01- OK +01/02- OK +01/03- KO +01/04- OK diff --git a/test/integration/targets/find/tasks/main.yml b/test/integration/targets/find/tasks/main.yml index 6879404d..91d92471 100644 --- a/test/integration/targets/find/tasks/main.yml +++ b/test/integration/targets/find/tasks/main.yml @@ -19,10 +19,14 @@ - set_fact: output_dir_test={{output_dir}}/test_find - name: make sure our testing sub-directory does not exist - file: path="{{ output_dir_test }}" state=absent + file: + path: "{{ output_dir_test }}" + state: absent - name: create our testing sub-directory - file: path="{{ output_dir_test }}" state=directory + file: + path: "{{ output_dir_test }}" + state: directory ## ## find @@ -97,6 +101,114 @@ - 'find_test2.files[0].pw_name is defined' - 'find_test2.files[0].gr_name is defined' +- name: find the xml file with empty excludes + find: + paths: "{{ output_dir_test }}" + patterns: "*.xml" + recurse: yes + excludes: [] + register: find_test3 +- debug: var=find_test3 +- name: validate gr_name and pw_name are defined + assert: + that: + - 'find_test3.matched == 1' + - 'find_test3.files[0].pw_name is defined' + - 'find_test3.files[0].gr_name is defined' + +- name: Copy some files into the test dir + copy: + src: "{{ item }}" + dest: "{{ output_dir_test }}/{{ item }}" + mode: 0644 + with_items: + - a.txt + - log.txt + +- name: Ensure '$' only matches the true end of the file with read_whole_file, not a line + find: + paths: "{{ output_dir_test }}" + patterns: "*.txt" + contains: "KO$" + read_whole_file: true + register: whole_no_match + +- debug: var=whole_no_match + +- assert: + that: + - whole_no_match.matched == 0 + +- name: Match the end of the file successfully + find: + paths: "{{ output_dir_test }}" + patterns: "*.txt" + contains: "OK$" + read_whole_file: true + register: whole_match + +- debug: var=whole_match + +- assert: + that: + - whole_match.matched == 1 + +- name: When read_whole_file=False, $ should match an individual line + find: + paths: "{{ output_dir_test }}" + patterns: "*.txt" + contains: ".*KO$" + read_whole_file: false + register: match_end_of_line + +- debug: var=match_end_of_line + +- assert: + that: + - match_end_of_line.matched == 1 + +- name: When read_whole_file=True, match across line boundaries + find: + paths: "{{ output_dir_test }}" + patterns: "*.txt" + contains: "has\na few" + read_whole_file: true + register: match_line_boundaries + +- debug: var=match_line_boundaries + +- assert: + that: + - match_line_boundaries.matched == 1 + +- name: When read_whole_file=False, do not match across line boundaries + find: + paths: "{{ output_dir_test }}" + patterns: "*.txt" + contains: "has\na few" + read_whole_file: false + register: no_match_line_boundaries + +- debug: var=no_match_line_boundaries + +- assert: + that: + - no_match_line_boundaries.matched == 0 + +- block: + - set_fact: + mypath: /idontexist{{lookup('pipe', 'mktemp')}} + + - find: + paths: '{{mypath}}' + patterns: '*' + register: failed_path + + - assert: + that: + - failed_path.files == [] + - failed_path.msg.startswith("Skipped '{{mypath}}' path due to this access issue") + - name: test number of examined directories/files block: - name: Get all files/directories in the path @@ -108,8 +220,8 @@ - assert: that: - - total_contents.matched == 16 - - total_contents.examined == 16 + - total_contents.matched == 18 + - total_contents.examined == 18 - name: Get files and directories with depth find: @@ -121,10 +233,10 @@ - assert: that: - - contents_with_depth.matched == 6 + - contents_with_depth.matched == 8 # dir contents are considered until the depth exceeds the requested depth - # there are 6 files/directories in the requested depth and 4 that exceed it by 1 - - contents_with_depth.examined == 10 + # there are 8 files/directories in the requested depth and 4 that exceed it by 1 + - contents_with_depth.examined == 12 - name: Find files with depth find: @@ -135,7 +247,28 @@ - assert: that: - - files_with_depth.matched == 2 + - files_with_depth.matched == 4 # dir contents are considered until the depth exceeds the requested depth - # there are 6 files/directories in the requested depth and 4 that exceed it by 1 - - files_with_depth.examined == 10 + # there are 8 files/directories in the requested depth and 4 that exceed it by 1 + - files_with_depth.examined == 12 +- name: exclude with regex + find: + paths: "{{ output_dir_test }}" + recurse: yes + use_regex: true + exclude: .*\.ogg + register: find_test3 +# Note that currently sane ways of doing this with map() or +# selectattr() aren't available in centos6 era jinja2 ... +- set_fact: + find_test3_list: >- + [ {% for f in find_test3.files %} + {{ f.path }} + {% if not loop.last %},{% endif %} + {% endfor %} + ] +- debug: var=find_test3_list +- name: assert we skipped the ogg file + assert: + that: + - '"{{ output_dir_test }}/e/f/g/h/8.ogg" not in find_test3_list' diff --git a/test/integration/targets/gathering_facts/runme.sh b/test/integration/targets/gathering_facts/runme.sh index 46355627..ebb82ab4 100755 --- a/test/integration/targets/gathering_facts/runme.sh +++ b/test/integration/targets/gathering_facts/runme.sh @@ -16,3 +16,10 @@ ansible-playbook verify_merge_facts.yml -v "$@" -e 'ansible_facts_parallel: Fals # ensure we dont clobber facts in loop ansible-playbook prevent_clobbering.yml -v "$@" + +# ensure we dont fail module on bad subset +ansible-playbook verify_subset.yml "$@" + +# ensure we can set defaults for the action plugin and facts module +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 diff --git a/test/integration/targets/gathering_facts/test_gathering_facts.yml b/test/integration/targets/gathering_facts/test_gathering_facts.yml index d4364d29..0939cba7 100644 --- a/test/integration/targets/gathering_facts/test_gathering_facts.yml +++ b/test/integration/targets/gathering_facts/test_gathering_facts.yml @@ -122,6 +122,24 @@ - 'ansible_virtualization_role|default("UNDEF_VIRT") == "UNDEF_VIRT"' - 'ansible_env|default("UNDEF_ENV") != "UNDEF_ENV"' +- hosts: facthost24 + tags: [ 'fact_min' ] + connection: local + gather_facts: no + tasks: + - setup: + filter: + - "*env*" + - "*virt*" + + - name: Test that retrieving all facts filtered to env and virt works + assert: + that: + - 'ansible_interfaces|default("UNDEF_NET") == "UNDEF_NET"' + - 'ansible_mounts|default("UNDEF_MOUNT") == "UNDEF_MOUNT"' + - 'ansible_virtualization_role|default("UNDEF_VIRT") != "UNDEF_VIRT"' + - 'ansible_env|default("UNDEF_ENV") != "UNDEF_ENV"' + - hosts: facthost13 tags: [ 'fact_min' ] connection: local diff --git a/test/integration/targets/gathering_facts/test_module_defaults.yml b/test/integration/targets/gathering_facts/test_module_defaults.yml new file mode 100644 index 00000000..5b0f9dd8 --- /dev/null +++ b/test/integration/targets/gathering_facts/test_module_defaults.yml @@ -0,0 +1,79 @@ +--- +- hosts: localhost + # The gather_facts keyword has default values for its + # options so module_defaults doesn't have much affect. + gather_facts: no + tags: + - default_fact_module + tasks: + - name: set defaults for the action plugin + gather_facts: + module_defaults: + gather_facts: + gather_subset: min + + - assert: + that: "gather_subset == ['min']" + + - name: set defaults for the module + gather_facts: + module_defaults: + setup: + gather_subset: '!all' + + - assert: + that: "gather_subset == ['!all']" + + # Defaults for the action plugin win because they are + # loaded first and options need to be omitted for + # defaults to be used. + - name: set defaults for the action plugin and module + gather_facts: + module_defaults: + setup: + gather_subset: '!all' + gather_facts: + gather_subset: min + + - assert: + that: "gather_subset == ['min']" + + # The gather_facts 'smart' facts module is 'ansible.legacy.setup' by default. + # If 'setup' (the unqualified name) is explicitly requested, FQCN module_defaults + # would not apply. + - name: set defaults for the fqcn module + gather_facts: + module_defaults: + ansible.legacy.setup: + gather_subset: '!all' + + - assert: + that: "gather_subset == ['!all']" + +- hosts: localhost + gather_facts: no + tags: + - custom_fact_module + tasks: + - name: set defaults for the module + gather_facts: + module_defaults: + ansible.legacy.setup: + gather_subset: '!all' + + - assert: + that: + - "gather_subset == ['!all']" + + # Defaults for the action plugin win. + - name: set defaults for the action plugin and module + gather_facts: + module_defaults: + gather_facts: + gather_subset: min + ansible.legacy.setup: + gather_subset: '!all' + + - assert: + that: + - "gather_subset == ['min']" diff --git a/test/integration/targets/gathering_facts/verify_subset.yml b/test/integration/targets/gathering_facts/verify_subset.yml new file mode 100644 index 00000000..89132756 --- /dev/null +++ b/test/integration/targets/gathering_facts/verify_subset.yml @@ -0,0 +1,13 @@ +- hosts: localhost + gather_facts: false + tasks: + - name: bad subset used + setup: gather_subset=nonsense + register: bad_sub + ignore_errors: true + + - name: verify we fail the right way + assert: + that: + - bad_sub is failed + - "'MODULE FAILURE' not in bad_sub['msg']" diff --git a/test/integration/targets/get_url/files/testserver.py b/test/integration/targets/get_url/files/testserver.py index 81043b66..24967d4f 100644 --- a/test/integration/targets/get_url/files/testserver.py +++ b/test/integration/targets/get_url/files/testserver.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import sys if __name__ == '__main__': diff --git a/test/integration/targets/get_url/tasks/main.yml b/test/integration/targets/get_url/tasks/main.yml index 052bde22..32da1d51 100644 --- a/test/integration/targets/get_url/tasks/main.yml +++ b/test/integration/targets/get_url/tasks/main.yml @@ -254,9 +254,9 @@ # https://github.com/ansible/ansible/issues/29614 - name: Change mode on an already downloaded file and specify checksum get_url: - url: 'https://{{ httpbin_host }}/get' + url: 'https://{{ httpbin_host }}/base64/cHR1eA==' dest: '{{ remote_tmp_dir }}/test' - checksum: 'sha256:7036ede810fad2b5d2e7547ec703cae8da61edbba43c23f9d7203a0239b765c4.' + checksum: 'sha256:b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006.' mode: '0775' register: result @@ -272,9 +272,9 @@ - name: test checksum match in check mode get_url: - url: 'https://{{ httpbin_host }}/get' + url: 'https://{{ httpbin_host }}/base64/cHR1eA==' dest: '{{ remote_tmp_dir }}/test' - checksum: 'sha256:7036ede810fad2b5d2e7547ec703cae8da61edbba43c23f9d7203a0239b765c4.' + checksum: 'sha256:b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006.' check_mode: True register: result @@ -335,11 +335,17 @@ dest: '{{ files_dir }}/27617.txt' content: "ptux" +- name: create duplicate src file + copy: + dest: '{{ files_dir }}/71420.txt' + content: "ptux" + - name: create sha1 checksum file of src copy: dest: '{{ files_dir }}/sha1sum.txt' content: | a97e6837f60cec6da4491bab387296bbcd72bdba 27617.txt + a97e6837f60cec6da4491bab387296bbcd72bdba 71420.txt 3911340502960ca33aece01129234460bfeb2791 not_target1.txt 1b4b6adf30992cedb0f6edefd6478ff0a593b2e4 not_target2.txt @@ -348,6 +354,7 @@ dest: '{{ files_dir }}/sha256sum.txt' content: | b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. 27617.txt + b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. 71420.txt 30949cc401e30ac494d695ab8764a9f76aae17c5d73c67f65e9b558f47eff892 not_target1.txt d0dbfc1945bc83bf6606b770e442035f2c4e15c886ee0c22fb3901ba19900b5b not_target2.txt @@ -356,9 +363,19 @@ dest: '{{ files_dir }}/sha256sum_with_dot.txt' content: | b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. ./27617.txt + b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. ./71420.txt 30949cc401e30ac494d695ab8764a9f76aae17c5d73c67f65e9b558f47eff892 ./not_target1.txt d0dbfc1945bc83bf6606b770e442035f2c4e15c886ee0c22fb3901ba19900b5b ./not_target2.txt +- name: create sha256 checksum file of src with a * leading path + copy: + dest: '{{ files_dir }}/sha256sum_with_asterisk.txt' + content: | + b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. *27617.txt + b1b6ce5073c8fac263a8fc5edfffdbd5dec1980c784e09c5bc69f8fb6056f006. *71420.txt + 30949cc401e30ac494d695ab8764a9f76aae17c5d73c67f65e9b558f47eff892 *not_target1.txt + d0dbfc1945bc83bf6606b770e442035f2c4e15c886ee0c22fb3901ba19900b5b *not_target2.txt + - copy: src: "testserver.py" dest: "{{ remote_tmp_dir }}/testserver.py" @@ -374,6 +391,14 @@ port: '{{ http_port }}' state: started +- name: download src with sha1 checksum url in check mode + get_url: + url: 'http://localhost:{{ http_port }}/27617.txt' + dest: '{{ remote_tmp_dir }}' + checksum: 'sha1:http://localhost:{{ http_port }}/sha1sum.txt' + register: result_sha1_check_mode + check_mode: True + - name: download src with sha1 checksum url get_url: url: 'http://localhost:{{ http_port }}/27617.txt' @@ -407,15 +432,107 @@ path: "{{ remote_tmp_dir }}/27617sha256_with_dot.txt" register: stat_result_sha256_with_dot +- name: download src with sha256 checksum url with asterisk leading paths + get_url: + url: 'http://localhost:{{ http_port }}/27617.txt' + dest: '{{ remote_tmp_dir }}/27617sha256_with_asterisk.txt' + checksum: 'sha256:http://localhost:{{ http_port }}/sha256sum_with_asterisk.txt' + register: result_sha256_with_asterisk + +- stat: + path: "{{ remote_tmp_dir }}/27617sha256_with_asterisk.txt" + register: stat_result_sha256_with_asterisk + +- name: download src with sha256 checksum url with file scheme + get_url: + url: 'http://localhost:{{ http_port }}/27617.txt' + dest: '{{ remote_tmp_dir }}/27617sha256_with_file_scheme.txt' + checksum: 'sha256:file://{{ files_dir }}/sha256sum.txt' + register: result_sha256_with_file_scheme + +- stat: + path: "{{ remote_tmp_dir }}/27617sha256_with_dot.txt" + register: stat_result_sha256_with_file_scheme + +- name: download 71420.txt with sha1 checksum url + get_url: + url: 'http://localhost:{{ http_port }}/71420.txt' + dest: '{{ remote_tmp_dir }}' + checksum: 'sha1:http://localhost:{{ http_port }}/sha1sum.txt' + register: result_sha1_71420 + +- stat: + path: "{{ remote_tmp_dir }}/71420.txt" + register: stat_result_sha1_71420 + +- name: download 71420.txt with sha256 checksum url + get_url: + url: 'http://localhost:{{ http_port }}/71420.txt' + dest: '{{ remote_tmp_dir }}/71420sha256.txt' + checksum: 'sha256:http://localhost:{{ http_port }}/sha256sum.txt' + register: result_sha256_71420 + +- stat: + path: "{{ remote_tmp_dir }}/71420.txt" + register: stat_result_sha256_71420 + +- name: download 71420.txt with sha256 checksum url with dot leading paths + get_url: + url: 'http://localhost:{{ http_port }}/71420.txt' + dest: '{{ remote_tmp_dir }}/71420sha256_with_dot.txt' + checksum: 'sha256:http://localhost:{{ http_port }}/sha256sum_with_dot.txt' + register: result_sha256_with_dot_71420 + +- stat: + path: "{{ remote_tmp_dir }}/71420sha256_with_dot.txt" + register: stat_result_sha256_with_dot_71420 + +- name: download 71420.txt with sha256 checksum url with asterisk leading paths + get_url: + url: 'http://localhost:{{ http_port }}/71420.txt' + dest: '{{ remote_tmp_dir }}/71420sha256_with_asterisk.txt' + checksum: 'sha256:http://localhost:{{ http_port }}/sha256sum_with_asterisk.txt' + register: result_sha256_with_asterisk_71420 + +- stat: + path: "{{ remote_tmp_dir }}/71420sha256_with_asterisk.txt" + register: stat_result_sha256_with_asterisk_71420 + +- name: download 71420.txt with sha256 checksum url with file scheme + get_url: + url: 'http://localhost:{{ http_port }}/71420.txt' + dest: '{{ remote_tmp_dir }}/71420sha256_with_file_scheme.txt' + checksum: 'sha256:file://{{ files_dir }}/sha256sum.txt' + register: result_sha256_with_file_scheme_71420 + +- stat: + path: "{{ remote_tmp_dir }}/71420sha256_with_dot.txt" + register: stat_result_sha256_with_file_scheme_71420 + - name: Assert that the file was downloaded assert: that: + - result_sha1_check_mode is changed - result_sha1 is changed - result_sha256 is changed - result_sha256_with_dot is changed + - result_sha256_with_asterisk is changed + - result_sha256_with_file_scheme is changed - "stat_result_sha1.stat.exists == true" - "stat_result_sha256.stat.exists == true" - "stat_result_sha256_with_dot.stat.exists == true" + - "stat_result_sha256_with_asterisk.stat.exists == true" + - "stat_result_sha256_with_file_scheme.stat.exists == true" + - result_sha1_71420 is changed + - result_sha256_71420 is changed + - result_sha256_with_dot_71420 is changed + - result_sha256_with_asterisk_71420 is changed + - result_sha256_with_file_scheme_71420 is changed + - "stat_result_sha1_71420.stat.exists == true" + - "stat_result_sha256_71420.stat.exists == true" + - "stat_result_sha256_with_dot_71420.stat.exists == true" + - "stat_result_sha256_with_asterisk_71420.stat.exists == true" + - "stat_result_sha256_with_file_scheme_71420.stat.exists == true" #https://github.com/ansible/ansible/issues/16191 - name: Test url split with no filename @@ -461,3 +578,12 @@ that: - '(result.content | b64decode) == "ansible.http.tests:SUCCESS"' when: has_httptester + +- name: Test use_gssapi=True + include_tasks: + file: use_gssapi.yml + apply: + environment: + KRB5_CONFIG: '{{ krb5_config }}' + KRB5CCNAME: FILE:{{ remote_tmp_dir }}/krb5.cc + when: krb5_config is defined diff --git a/test/integration/targets/get_url/tasks/use_gssapi.yml b/test/integration/targets/get_url/tasks/use_gssapi.yml new file mode 100644 index 00000000..f3d2d1fb --- /dev/null +++ b/test/integration/targets/get_url/tasks/use_gssapi.yml @@ -0,0 +1,45 @@ +- name: test Negotiate auth over HTTP with explicit credentials + get_url: + url: http://{{ httpbin_host }}/gssapi + dest: '{{ remote_tmp_dir }}/gssapi_explicit.txt' + use_gssapi: yes + url_username: '{{ krb5_username }}' + url_password: '{{ krb5_password }}' + register: http_explicit + +- name: get result of test Negotiate auth over HTTP with explicit credentials + slurp: + path: '{{ remote_tmp_dir }}/gssapi_explicit.txt' + register: http_explicit_actual + +- name: assert test Negotiate auth with implicit credentials + assert: + that: + - http_explicit.status_code == 200 + - http_explicit_actual.content | b64decode | trim == 'Microsoft Rulz' + +- name: skip tests on macOS, I cannot seem to get it to read a credential from a custom ccache + when: ansible_facts.distribution != 'MacOSX' + block: + - name: get Kerberos ticket for implicit auth tests + httptester_kinit: + username: '{{ krb5_username }}' + password: '{{ krb5_password }}' + + - name: test Negotiate auth over HTTPS with implicit credentials + get_url: + url: https://{{ httpbin_host }}/gssapi + dest: '{{ remote_tmp_dir }}/gssapi_implicit.txt' + use_gssapi: yes + register: https_implicit + + - name: get result of test Negotiate auth over HTTPS with implicit credentials + slurp: + path: '{{ remote_tmp_dir }}/gssapi_implicit.txt' + register: https_implicit_actual + + - name: assert test Negotiate auth with implicit credentials + assert: + that: + - https_implicit.status_code == 200 + - https_implicit_actual.content | b64decode | trim == 'Microsoft Rulz' diff --git a/test/integration/targets/getent/tasks/main.yml b/test/integration/targets/getent/tasks/main.yml index 825ad5ea..bd17bd62 100644 --- a/test/integration/targets/getent/tasks/main.yml +++ b/test/integration/targets/getent/tasks/main.yml @@ -29,13 +29,13 @@ key: root service: files register: getent_test0 - when: ansible_system != 'FreeBSD' + when: ansible_system != 'FreeBSD' and ansible_distribution != 'Alpine' - name: run getent w/o specified service (FreeBSD) getent: database: passwd key: root register: getent_test0 - when: ansible_system == 'FreeBSD' + when: ansible_system == 'FreeBSD' or ansible_distribution == 'Alpine' - debug: var=getent_test0 - name: validate results assert: diff --git a/test/integration/targets/git/tasks/main.yml b/test/integration/targets/git/tasks/main.yml index 722713bf..c5aeacbe 100644 --- a/test/integration/targets/git/tasks/main.yml +++ b/test/integration/targets/git/tasks/main.yml @@ -16,25 +16,26 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. -- include_tasks: setup.yml -- include_tasks: setup-local-repos.yml +- import_tasks: setup.yml +- import_tasks: setup-local-repos.yml -- include_tasks: formats.yml -- include_tasks: missing_hostkey.yml -- include_tasks: no-destination.yml -- include_tasks: specific-revision.yml -- include_tasks: submodules.yml -- include_tasks: change-repo-url.yml -- include_tasks: depth.yml -- include_tasks: checkout-new-tag.yml +- import_tasks: formats.yml +- import_tasks: missing_hostkey.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', '<')) -- include_tasks: localmods.yml -- include_tasks: reset-origin.yml -- include_tasks: ambiguous-ref.yml -- include_tasks: archive.yml -- include_tasks: separate-git-dir.yml -- include_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 diff --git a/test/integration/targets/git/tasks/setup.yml b/test/integration/targets/git/tasks/setup.yml index 16c56904..3158bf62 100644 --- a/test/integration/targets/git/tasks/setup.yml +++ b/test/integration/targets/git/tasks/setup.yml @@ -11,7 +11,7 @@ - name: SETUP | install git package: name: '{{ item }}' - when: ansible_distribution != "MacOSX" + when: ansible_distribution not in ["MacOSX", "Alpine"] notify: - cleanup with_items: "{{ git_required_packages[ansible_os_family | default('default') ] | default(git_required_packages.default) }}" diff --git a/test/integration/targets/git/tasks/single-branch.yml b/test/integration/targets/git/tasks/single-branch.yml new file mode 100644 index 00000000..5cfb4d5b --- /dev/null +++ b/test/integration/targets/git/tasks/single-branch.yml @@ -0,0 +1,87 @@ +# Test single_branch parameter + +- name: SINGLE_BRANCH | clear checkout_dir + file: + state: absent + path: "{{ checkout_dir }}" + +- name: SINGLE_BRANCH | Clone example git repo using single_branch + git: + repo: 'file://{{ repo_dir|expanduser }}/shallow_branches' + dest: '{{ checkout_dir }}' + single_branch: yes + register: single_branch_1 + +- name: SINGLE_BRANCH | Clone example git repo using single_branch again + git: + repo: 'file://{{ repo_dir|expanduser }}/shallow_branches' + dest: '{{ checkout_dir }}' + single_branch: yes + register: single_branch_2 + +- name: SINGLE_BRANCH | List revisions + command: git rev-list --all --count + args: + chdir: '{{ checkout_dir }}' + register: rev_list1 + when: git_version.stdout is version(git_version_supporting_single_branch, '>=') + +- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git >= {{ git_version_supporting_single_branch }} + assert: + that: + - single_branch_1 is changed + - single_branch_2 is not changed + when: git_version.stdout is version(git_version_supporting_single_branch, '>=') + +- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git < {{ git_version_supporting_single_branch }} + assert: + that: + - single_branch_1 is changed + - single_branch_1.warnings | length == 1 + - single_branch_2 is not changed + when: git_version.stdout is version(git_version_supporting_single_branch, '<') + + +- name: SINGLE_BRANCH | clear checkout_dir + file: + state: absent + path: "{{ checkout_dir }}" + +- name: SINGLE_BRANCH | Clone example git repo using single_branch with version + git: + repo: 'file://{{ repo_dir|expanduser }}/shallow_branches' + dest: '{{ checkout_dir }}' + single_branch: yes + version: master + register: single_branch_3 + +- name: SINGLE_BRANCH | Clone example git repo using single_branch with version again + git: + repo: 'file://{{ repo_dir|expanduser }}/shallow_branches' + dest: '{{ checkout_dir }}' + single_branch: yes + version: master + register: single_branch_4 + +- name: SINGLE_BRANCH | List revisions + command: git rev-list --all --count + args: + chdir: '{{ checkout_dir }}' + register: rev_list2 + when: git_version.stdout is version(git_version_supporting_single_branch, '>=') + +- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git >= {{ git_version_supporting_single_branch }} + assert: + that: + - single_branch_3 is changed + - single_branch_4 is not changed + - rev_list2.stdout == '1' + when: git_version.stdout is version(git_version_supporting_single_branch, '>=') + +- name: SINGLE_BRANCH | Ensure single_branch did the right thing with git < {{ git_version_supporting_single_branch }} + assert: + that: + - single_branch_3 is changed + - single_branch_3.warnings | length == 1 + - single_branch_4 is not changed + when: git_version.stdout is version(git_version_supporting_single_branch, '<') diff --git a/test/integration/targets/git/vars/main.yml b/test/integration/targets/git/vars/main.yml index a5bae5ba..a1bbfca9 100644 --- a/test/integration/targets/git/vars/main.yml +++ b/test/integration/targets/git/vars/main.yml @@ -43,6 +43,7 @@ known_host_files: - '/etc/ssh/ssh_known_hosts' git_version_supporting_depth: 1.9.1 git_version_supporting_ls_remote: 1.7.5 +git_version_supporting_single_branch: 1.7.10 # path to a SSH private key for use with github.com (tests skipped if undefined) # github_ssh_private_key: "{{ lookup('env', 'HOME') }}/.ssh/id_rsa" git_gpg_testkey: | diff --git a/test/integration/targets/group/files/gidget.py b/test/integration/targets/group/files/gidget.py index 128985e7..4b771516 100644 --- a/test/integration/targets/group/files/gidget.py +++ b/test/integration/targets/group/files/gidget.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import grp gids = [g.gr_gid for g in grp.getgrall()] diff --git a/test/integration/targets/group/tasks/tests.yml b/test/integration/targets/group/tasks/tests.yml index e35b23c9..a724c9df 100644 --- a/test/integration/targets/group/tasks/tests.yml +++ b/test/integration/targets/group/tasks/tests.yml @@ -138,11 +138,19 @@ 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: + 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'] ## @@ -198,7 +206,13 @@ args: name: libuser state: present - when: ansible_facts.system in ['Linux'] + when: ansible_facts.system in ['Linux'] and ansible_distribution != 'Alpine' + tags: + - user_test_local_mode + +- name: Ensure lgroupadd is present - Alpine + command: apk add -U libuser --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community + when: ansible_distribution == 'Alpine' tags: - user_test_local_mode diff --git a/test/integration/targets/groupby_filter/aliases b/test/integration/targets/groupby_filter/aliases index 765b70da..31094c31 100644 --- a/test/integration/targets/groupby_filter/aliases +++ b/test/integration/targets/groupby_filter/aliases @@ -1 +1,2 @@ shippable/posix/group2 +needs/file/test/lib/ansible_test/_data/requirements/constraints.txt diff --git a/test/integration/targets/groupby_filter/requirements.txt b/test/integration/targets/groupby_filter/requirements.txt new file mode 100644 index 00000000..fdd9ec5c --- /dev/null +++ b/test/integration/targets/groupby_filter/requirements.txt @@ -0,0 +1,4 @@ +# pip 7.1 added support for constraints, which are required by ansible-test to install most python requirements +# see https://github.com/pypa/pip/blame/e648e00dc0226ade30ade99591b245b0c98e86c9/NEWS.rst#L1258 +pip >= 7.1, < 10 ; python_version < '2.7' # pip 10+ drops support for python 2.6 (sanity_ok) +pip >= 7.1 ; python_version >= '2.7' # sanity_ok diff --git a/test/integration/targets/groupby_filter/runme.sh b/test/integration/targets/groupby_filter/runme.sh index 07894b0f..09b47d55 100755 --- a/test/integration/targets/groupby_filter/runme.sh +++ b/test/integration/targets/groupby_filter/runme.sh @@ -2,13 +2,15 @@ set -eux -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh -pip install -U jinja2==2.9.4 +# Update pip in the venv to a version that supports constraints +pip install --requirement requirements.txt + +pip install -U jinja2==2.9.4 --constraint "../../../lib/ansible_test/_data/requirements/constraints.txt" ansible-playbook -i ../../inventory test_jinja2_groupby.yml -v "$@" -pip install -U "jinja2<2.9.0" +pip install -U "jinja2<2.9.0" --constraint "../../../lib/ansible_test/_data/requirements/constraints.txt" ansible-playbook -i ../../inventory test_jinja2_groupby.yml -v "$@" diff --git a/test/integration/targets/handler_race/roles/random_sleep/tasks/main.yml b/test/integration/targets/handler_race/roles/random_sleep/tasks/main.yml index 607318bb..0bc4c382 100644 --- a/test/integration/targets/handler_race/roles/random_sleep/tasks/main.yml +++ b/test/integration/targets/handler_race/roles/random_sleep/tasks/main.yml @@ -2,7 +2,7 @@ # tasks file for random_sleep - name: Generate sleep time set_fact: - sleep_time: "{{ 60 | random }}" + sleep_time: "{{ 30 | random }}" - name: Do random sleep shell: sleep "{{ sleep_time }}" diff --git a/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/A.yml b/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/A.yml new file mode 100644 index 00000000..b7520c77 --- /dev/null +++ b/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/A.yml @@ -0,0 +1 @@ +- debug: msg="handler with tasks from A.yml called" diff --git a/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/main.yml b/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/main.yml new file mode 100644 index 00000000..ba2e8f69 --- /dev/null +++ b/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/handlers/main.yml @@ -0,0 +1,5 @@ +- name: role-based handler from handler subdir + include_tasks: A.yml + +- name: role-based handler from tasks subdir + include_tasks: B.yml diff --git a/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/tasks/B.yml b/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/tasks/B.yml new file mode 100644 index 00000000..956c9754 --- /dev/null +++ b/test/integration/targets/handlers/roles/test_role_handlers_include_tasks/tasks/B.yml @@ -0,0 +1 @@ +- debug: msg="handler with tasks from B.yml called" diff --git a/test/integration/targets/handlers/runme.sh b/test/integration/targets/handlers/runme.sh index 59c81bce..cefa926b 100755 --- a/test/integration/targets/handlers/runme.sh +++ b/test/integration/targets/handlers/runme.sh @@ -87,6 +87,9 @@ set -e # https://github.com/ansible/ansible/issues/47287 [ "$(ansible-playbook test_handlers_including_task.yml -i ../../inventory -v "$@" | grep -E -o 'failed=[0-9]+')" = "failed=0" ] +# https://github.com/ansible/ansible/issues/71222 +ansible-playbook test_role_handlers_including_tasks.yml -i ../../inventory -v "$@" + # https://github.com/ansible/ansible/issues/27237 set +e result="$(ansible-playbook test_handlers_template_run_once.yml -i inventory.handlers "$@" 2>&1)" diff --git a/test/integration/targets/handlers/test_role_handlers_including_tasks.yml b/test/integration/targets/handlers/test_role_handlers_including_tasks.yml new file mode 100644 index 00000000..47d8f00a --- /dev/null +++ b/test/integration/targets/handlers/test_role_handlers_including_tasks.yml @@ -0,0 +1,18 @@ +--- +- name: Verify a role handler can include other tasks from handlers and tasks subdirs + hosts: testhost + roles: + - test_role_handlers_include_tasks + + tasks: + - name: notify a role-based handler (include tasks from handler subdir) + debug: + msg: notifying role handler + changed_when: yes + notify: role-based handler from handler subdir + + - name: notify a role-based handler (include tasks from tasks subdir) + debug: + msg: notifying another role handler + changed_when: yes + notify: role-based handler from tasks subdir diff --git a/test/integration/targets/hardware_facts/aliases b/test/integration/targets/hardware_facts/aliases new file mode 100644 index 00000000..e00c22c3 --- /dev/null +++ b/test/integration/targets/hardware_facts/aliases @@ -0,0 +1,3 @@ +destructive +needs/privileged +shippable/posix/group2 diff --git a/test/integration/targets/incidental_lvg/meta/main.yml b/test/integration/targets/hardware_facts/meta/main.yml index 1810d4be..1810d4be 100644 --- a/test/integration/targets/incidental_lvg/meta/main.yml +++ b/test/integration/targets/hardware_facts/meta/main.yml diff --git a/test/integration/targets/hardware_facts/tasks/Linux.yml b/test/integration/targets/hardware_facts/tasks/Linux.yml new file mode 100644 index 00000000..885aa0ec --- /dev/null +++ b/test/integration/targets/hardware_facts/tasks/Linux.yml @@ -0,0 +1,92 @@ +- name: Test LVM facts + block: + - name: Install lvm2 + package: + name: lvm2 + state: present + register: lvm_pkg + + - name: Create files to use as a disk devices + command: "dd if=/dev/zero of={{ remote_tmp_dir }}/img{{ item }} bs=1M count=10" + with_sequence: 'count=2' + + - name: Create loop device for file + command: "losetup --show -f {{ remote_tmp_dir }}/img{{ item }}" + with_sequence: 'count=2' + register: loop_devices + + - name: Get loop names + set_fact: + loop_device1: "{{ loop_devices.results[0].stdout }}" + loop_device2: "{{ loop_devices.results[1].stdout }}" + + - name: Create pvs + command: pvcreate {{ item }} + with_items: + - "{{ loop_device1 }}" + - "{{ loop_device2 }}" + + - name: Create vg + command: vgcreate first {{ loop_device1 }} + + - name: Create another vg + command: vgcreate second {{ loop_device2 }} + + - name: Create lv + command: lvcreate -L 4M first --name one + + - name: Create another lv + command: lvcreate -L 4M first --name two + + - name: Create yet another lv + command: lvcreate -L 4M second --name uno + + - name: Gather facts + setup: + + - assert: + that: + - ansible_lvm.pvs[loop_device1].vg == 'first' + - ansible_lvm.pvs[loop_device2].vg == 'second' + - ansible_lvm.lvs.one.vg == 'first' + - ansible_lvm.lvs.two.vg == 'first' + - ansible_lvm.lvs.uno.vg == 'second' + - ansible_lvm.vgs.first.num_lvs == "2" + - ansible_lvm.vgs.second.num_lvs == "1" + + always: + - name: remove lvs + shell: "lvremove /dev/{{ item }}/* -f" + with_items: + - first + - second + + - name: remove vgs + command: "vgremove {{ item }}" + with_items: + - first + - second + + - name: remove pvs + command: "pvremove {{ item }}" + with_items: + - "{{ loop_device1 }}" + - "{{ loop_device2 }}" + + - name: Detach loop device + command: "losetup -d {{ item }}" + with_items: + - "{{ loop_device1 }}" + - "{{ loop_device2 }}" + + - name: Remove device files + file: + path: "{{ remote_tmp_dir }}/img{{ item }}" + state: absent + with_sequence: 'count={{ loop_devices.results|length }}' + + - name: Remove lvm-tools + package: + name: lvm2 + state: absent + when: lvm_pkg is changed diff --git a/test/integration/targets/hardware_facts/tasks/main.yml b/test/integration/targets/hardware_facts/tasks/main.yml new file mode 100644 index 00000000..e7059c66 --- /dev/null +++ b/test/integration/targets/hardware_facts/tasks/main.yml @@ -0,0 +1,2 @@ +- include_tasks: Linux.yml + when: ansible_system == 'Linux' diff --git a/test/integration/targets/hostname/aliases b/test/integration/targets/hostname/aliases new file mode 100644 index 00000000..c552d611 --- /dev/null +++ b/test/integration/targets/hostname/aliases @@ -0,0 +1,3 @@ +shippable/posix/group1 +destructive +skip/aix # currently unsupported by hostname module diff --git a/test/integration/targets/hostname/tasks/MacOSX.yml b/test/integration/targets/hostname/tasks/MacOSX.yml new file mode 100644 index 00000000..912ced70 --- /dev/null +++ b/test/integration/targets/hostname/tasks/MacOSX.yml @@ -0,0 +1,52 @@ +- name: macOS | Set hostname + hostname: + name: bugs.acme.example.com + +# These tasks can be changed to a loop once https://github.com/ansible/ansible/issues/71031 +# is fixed +- name: macOS | Set hostname specifiying macos strategy + hostname: + name: bugs.acme.example.com + use: macos + +- name: macOS | Set hostname specifiying macosx strategy + hostname: + name: bugs.acme.example.com + use: macosx + +- name: macOS | Set hostname specifiying darwin strategy + hostname: + name: bugs.acme.example.com + use: darwin + +- name: macOS | Get macOS hostname values + command: scutil --get {{ item }} + loop: + - HostName + - ComputerName + - LocalHostName + register: macos_scutil + ignore_errors: yes + +- name: macOS | Ensure all hostname values were set correctly + assert: + that: + - "['bugs.acme.example.com', 'bugs.acme.example.com', 'bugsacmeexamplecom'] == macos_scutil.results | map(attribute='stdout') | list" + +- name: macOS | Set to a hostname using spaces and punctuation + hostname: + name: The Dude's Computer + +- name: macOS | Get macOS hostname values + command: scutil --get {{ item }} + loop: + - HostName + - ComputerName + - LocalHostName + register: macos_scutil_complex + ignore_errors: yes + +- name: macOS | Ensure all hostname values were set correctly + assert: + that: + - "['The Dude\\'s Computer', 'The Dude\\'s Computer', 'The-Dudes-Computer'] == (macos_scutil_complex.results | map(attribute='stdout') | list)" diff --git a/test/integration/targets/hostname/tasks/RedHat.yml b/test/integration/targets/hostname/tasks/RedHat.yml new file mode 100644 index 00000000..1f61390b --- /dev/null +++ b/test/integration/targets/hostname/tasks/RedHat.yml @@ -0,0 +1,15 @@ +- name: Make sure we used SystemdStrategy... + lineinfile: + path: "{{ _hostname_file }}" + line: crocodile.ansible.test.doesthiswork.net.example.com + check_mode: true + register: etc_hostname + failed_when: etc_hostname is changed + +- name: ...and not RedhatStrategy + lineinfile: + path: /etc/sysconfig/network + line: HOSTNAME=crocodile.ansible.test.doesthiswork.net.example.com + check_mode: true + register: etc_sysconfig_network + failed_when: etc_sysconfig_network is not changed diff --git a/test/integration/targets/hostname/tasks/check_mode.yml b/test/integration/targets/hostname/tasks/check_mode.yml new file mode 100644 index 00000000..e25df97b --- /dev/null +++ b/test/integration/targets/hostname/tasks/check_mode.yml @@ -0,0 +1,20 @@ +# These are less useful (check_mode only) but run even in containers +- block: + - name: Get current hostname + command: hostname + register: original + + - name: Change hostname (check_mode) + hostname: + name: crocodile.ansible.test.doesthiswork.net.example.com + check_mode: true + register: hn + + - name: Get current hostname again + command: hostname + register: after_hn + + - assert: + that: + - hn is changed + - original.stdout == after_hn.stdout diff --git a/test/integration/targets/hostname/tasks/default.yml b/test/integration/targets/hostname/tasks/default.yml new file mode 100644 index 00000000..b3082391 --- /dev/null +++ b/test/integration/targets/hostname/tasks/default.yml @@ -0,0 +1,2 @@ +- debug: + msg: No distro-specific tests defined for this distro. diff --git a/test/integration/targets/hostname/tasks/main.yml b/test/integration/targets/hostname/tasks/main.yml new file mode 100644 index 00000000..8a9e34bd --- /dev/null +++ b/test/integration/targets/hostname/tasks/main.yml @@ -0,0 +1,50 @@ +# Setting the hostname in our test containers doesn't work currently +- when: ansible_facts.virtualization_type not in ('docker', 'container', 'containerd') + block: + - name: Include distribution specific variables + include_vars: "{{ lookup('first_found', params) }}" + vars: + params: + files: + - "{{ ansible_facts.distribution }}.yml" + - "{{ ansible_facts.os_family }}.yml" + - default.yml + paths: + - "{{ role_path }}/vars" + + - name: Get current hostname + command: hostname + register: original + + - import_tasks: test_check_mode.yml + - import_tasks: test_normal.yml + + - name: Include distribution specific tasks + include_tasks: + file: "{{ lookup('first_found', files) }}" + vars: + files: + - "{{ ansible_facts.distribution }}.yml" + - default.yml + + always: + # Reset back to original hostname + - name: Move back original file if it existed + command: mv -f {{ _hostname_file }}.orig {{ _hostname_file }} + when: hn_stat.stat.exists | default(False) + + - name: Delete the file if it never existed + file: + path: "{{ _hostname_file }}" + state: absent + when: not hn_stat.stat.exists | default(True) + + - name: Reset back to original hostname + hostname: + name: "{{ original.stdout }}" + register: revert + + - name: Ensure original hostname was reset + assert: + that: + - revert is changed diff --git a/test/integration/targets/hostname/tasks/test_check_mode.yml b/test/integration/targets/hostname/tasks/test_check_mode.yml new file mode 100644 index 00000000..9ba1d65c --- /dev/null +++ b/test/integration/targets/hostname/tasks/test_check_mode.yml @@ -0,0 +1,50 @@ +- name: Run hostname module in check_mode + hostname: + name: crocodile.ansible.test.doesthiswork.net.example.com + check_mode: true + register: hn1 + +- name: Get current hostname again + command: hostname + register: after_hn + +- name: Ensure hostname changed properly + assert: + that: + - hn1 is changed + - original.stdout == after_hn.stdout + +- when: _hostname_file is defined and _hostname_file + block: + - name: See if current hostname file exists + stat: + path: "{{ _hostname_file }}" + register: hn_stat + + - name: Move the current hostname file if it exists + command: mv {{ _hostname_file }} {{ _hostname_file }}.orig + when: hn_stat.stat.exists + + - name: Run hostname module in check_mode + hostname: + name: crocodile.ansible.test.doesthiswork.net.example.com + check_mode: true + register: hn + + - stat: + path: /etc/rc.conf.d/hostname + register: hn_stat_checkmode + + - assert: + that: + # TODO: This is a legitimate bug and will be fixed in another PR. + # - not hn_stat_checkmode.stat.exists + - hn is changed + + - name: Get hostname again + command: hostname + register: current_after_cm + + - assert: + that: + - original.stdout == current_after_cm.stdout diff --git a/test/integration/targets/hostname/tasks/test_normal.yml b/test/integration/targets/hostname/tasks/test_normal.yml new file mode 100644 index 00000000..a40d96e9 --- /dev/null +++ b/test/integration/targets/hostname/tasks/test_normal.yml @@ -0,0 +1,24 @@ +- name: Run hostname module for real now + hostname: + name: crocodile.ansible.test.doesthiswork.net.example.com + register: hn2 + +- name: Get hostname + command: hostname + register: current_after_hn2 + +- name: Run hostname again to ensure it does not change + hostname: + name: crocodile.ansible.test.doesthiswork.net.example.com + register: hn3 + +- name: Get hostname + command: hostname + register: current_after_hn3 + +- assert: + that: + - hn2 is changed + - hn3 is not changed + - current_after_hn2.stdout == 'crocodile.ansible.test.doesthiswork.net.example.com' + - current_after_hn2.stdout == current_after_hn2.stdout diff --git a/test/integration/targets/hostname/vars/FreeBSD.yml b/test/integration/targets/hostname/vars/FreeBSD.yml new file mode 100644 index 00000000..b63a16ed --- /dev/null +++ b/test/integration/targets/hostname/vars/FreeBSD.yml @@ -0,0 +1 @@ +_hostname_file: /etc/rc.conf.d/hostname diff --git a/test/integration/targets/hostname/vars/RedHat.yml b/test/integration/targets/hostname/vars/RedHat.yml new file mode 100644 index 00000000..08d883b9 --- /dev/null +++ b/test/integration/targets/hostname/vars/RedHat.yml @@ -0,0 +1 @@ +_hostname_file: /etc/hostname diff --git a/test/integration/targets/hostname/vars/default.yml b/test/integration/targets/hostname/vars/default.yml new file mode 100644 index 00000000..a50b3f19 --- /dev/null +++ b/test/integration/targets/hostname/vars/default.yml @@ -0,0 +1 @@ +_hostname_file: ~ diff --git a/test/integration/targets/ignore_unreachable/fake_connectors/bad_exec.py b/test/integration/targets/ignore_unreachable/fake_connectors/bad_exec.py index b5e9ca88..0d8c385b 100644 --- a/test/integration/targets/ignore_unreachable/fake_connectors/bad_exec.py +++ b/test/integration/targets/ignore_unreachable/fake_connectors/bad_exec.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import ansible.plugins.connection.local as ansible_local from ansible.errors import AnsibleConnectionFailure diff --git a/test/integration/targets/ignore_unreachable/fake_connectors/bad_put_file.py b/test/integration/targets/ignore_unreachable/fake_connectors/bad_put_file.py index 98927997..d4131f45 100644 --- a/test/integration/targets/ignore_unreachable/fake_connectors/bad_put_file.py +++ b/test/integration/targets/ignore_unreachable/fake_connectors/bad_put_file.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import ansible.plugins.connection.local as ansible_local from ansible.errors import AnsibleConnectionFailure diff --git a/test/integration/targets/import_tasks/aliases b/test/integration/targets/import_tasks/aliases new file mode 100644 index 00000000..fff62d9f --- /dev/null +++ b/test/integration/targets/import_tasks/aliases @@ -0,0 +1,2 @@ +shippable/posix/group5 +skip/aix diff --git a/test/integration/targets/import_tasks/inherit_notify.yml b/test/integration/targets/import_tasks/inherit_notify.yml new file mode 100644 index 00000000..cf418f9d --- /dev/null +++ b/test/integration/targets/import_tasks/inherit_notify.yml @@ -0,0 +1,15 @@ +- hosts: localhost + gather_facts: false + pre_tasks: + - import_tasks: tasks/trigger_change.yml + notify: hello + + handlers: + - name: hello + set_fact: hello=world + + tasks: + - name: ensure handler ran + assert: + that: + - "hello is defined and hello == 'world'" diff --git a/test/integration/targets/import_tasks/runme.sh b/test/integration/targets/import_tasks/runme.sh new file mode 100755 index 00000000..ea3529b5 --- /dev/null +++ b/test/integration/targets/import_tasks/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook inherit_notify.yml "$@" diff --git a/test/integration/targets/import_tasks/tasks/trigger_change.yml b/test/integration/targets/import_tasks/tasks/trigger_change.yml new file mode 100644 index 00000000..6ee45515 --- /dev/null +++ b/test/integration/targets/import_tasks/tasks/trigger_change.yml @@ -0,0 +1,2 @@ +- debug: msg="I trigger changed!" + changed_when: true diff --git a/test/integration/targets/incidental_azure_rm_mariadbserver/aliases b/test/integration/targets/incidental_azure_rm_mariadbserver/aliases deleted file mode 100644 index 9901373a..00000000 --- a/test/integration/targets/incidental_azure_rm_mariadbserver/aliases +++ /dev/null @@ -1,3 +0,0 @@ -cloud/azure -destructive -shippable/azure/incidental diff --git a/test/integration/targets/incidental_azure_rm_mariadbserver/tasks/main.yml b/test/integration/targets/incidental_azure_rm_mariadbserver/tasks/main.yml deleted file mode 100644 index 5b33ffb9..00000000 --- a/test/integration/targets/incidental_azure_rm_mariadbserver/tasks/main.yml +++ /dev/null @@ -1,640 +0,0 @@ -- name: Prepare random number - set_fact: - rpfx: "{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - run_once: yes - -- name: Create instance of MariaDB Server -- check mode - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - check_mode: yes - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - - output.state == 'Ready' - -- name: Create again instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed == false - - output.state == 'Ready' - -- name: Update instance of MariaDB Server, change storage size - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 128000 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed - - output.state == 'Ready' -- debug: - var: output - -- name: Gather facts MariaDB Server - azure_rm_mariadbserver_facts: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that storage size is correct - assert: - that: - - output.servers[0]['storage_mb'] == 128000 - -- name: Create second instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - tags: - aaa: bbb - -- name: Create second instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - sku: - name: B_Gen5_1 - tier: Basic - location: westus2 - storage_mb: 51200 - version: 10.2 - enforce_ssl: True - admin_username: zimxyz - admin_password: Testpasswordxyz12! - tags: - ccc: ddd - -- name: Gather facts MariaDB Server - azure_rm_mariadbserver_facts: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - register: output - -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.servers[0]['id'] != None - - output.servers[0]['name'] != None - - output.servers[0]['location'] != None - - output.servers[0]['sku']['name'] != None - - output.servers[0]['sku']['tier'] != None - - output.servers[0]['sku']['capacity'] != None - - output.servers[0]['version'] != None - - output.servers[0]['user_visible_state'] != None - - output.servers[0]['fully_qualified_domain_name'] != None - - output.servers[0]['tags']['aaa'] == 'bbb' - - output.servers[0]['tags']['ccc'] == 'ddd' - -- name: Gather facts MariaDB Server - azure_rm_mariadbserver_facts: - resource_group: "{{ resource_group }}" - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.servers[0]['id'] != None - - output.servers[0]['name'] != None - - output.servers[0]['location'] != None - - output.servers[0]['sku']['name'] != None - - output.servers[0]['sku']['tier'] != None - - output.servers[0]['sku']['capacity'] != None - - output.servers[0]['version'] != None - - output.servers[0]['user_visible_state'] != None - - output.servers[0]['fully_qualified_domain_name'] != None - - output.servers[1]['id'] != None - - output.servers[1]['name'] != None - - output.servers[1]['location'] != None - - output.servers[1]['sku']['name'] != None - - output.servers[1]['sku']['tier'] != None - - output.servers[1]['sku']['capacity'] != None - - output.servers[1]['version'] != None - - output.servers[1]['user_visible_state'] != None - - output.servers[1]['fully_qualified_domain_name'] != None - -# -# azure_rm_mariadbdatabase tests below -# -- name: Create instance of MariaDB Database -- check mode - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - check_mode: yes - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_swedish_ci - charset: latin1 - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - - output.name == 'testdatabase' - -- name: Create again instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_swedish_ci - charset: latin1 - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed == false - - output.name == 'testdatabase' - -- name: Try to update database without force_update - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_czech_ci - charset: latin1 - ignore_errors: yes - register: output -- name: Assert that nothing has changed - assert: - that: - - output.changed == False - -- name: Update instance of database using force_update - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - collation: latin1_czech_ci - charset: latin1 - force_update: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - - output.name == 'testdatabase' - -- name: Create second instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase2 - -- name: Gather facts MariaDB Database - azure_rm_mariadbdatabase_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - register: output - -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.databases[0]['server_name'] != None - - output.databases[0]['name'] != None - - output.databases[0]['charset'] != None - - output.databases[0]['collation'] != None - -- name: Gather facts MariaDB Database - azure_rm_mariadbdatabase_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.databases[0]['server_name'] != None - - output.databases[0]['name'] != None - - output.databases[0]['charset'] != None - - output.databases[0]['collation'] != None - - output.databases[1]['server_name'] != None - - output.databases[1]['name'] != None - - output.databases[1]['charset'] != None - - output.databases[1]['collation'] != None - -- name: Delete instance of MariaDB Database -- check mode - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - state: absent - check_mode: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete unexisting instance of MariaDB Database - azure_rm_mariadbdatabase: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: testdatabase - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed == false - -# -# azure_rm_firewallrule tests below -# -- name: Create instance of Firewall Rule -- check mode - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - check_mode: yes - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - register: output -- name: Assert the resource instance is well created - assert: - that: - - output.changed - -- name: Create again instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - register: output -- name: Assert the state has not changed - assert: - that: - - output.changed == false - -- name: Delete instance of Firewall Rule -- check mode - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - state: absent - check_mode: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Create instance of Firewall Rule -- second - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }}second - start_ip_address: 172.28.10.136 - end_ip_address: 172.28.10.138 - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Gather facts MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.rules[0].id != None - - output.rules[0].server_name != None - - output.rules[0].name != None - - output.rules[0].start_ip_address != None - - output.rules[0].end_ip_address != None - - "output.rules | length == 1" - -- name: Gather facts MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.rules[0].id != None - - output.rules[0].server_name != None - - output.rules[0].name != None - - output.rules[0].start_ip_address != None - - output.rules[0].end_ip_address != None - - output.rules[1].id != None - - output.rules[1].name != None - - output.rules[1].start_ip_address != None - - output.rules[1].end_ip_address != None - - "output.rules | length == 2" - -- name: Delete instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete unexisting instance of Firewall Rule - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed == false - -- name: Delete instance of Firewall Rule - second - azure_rm_mariadbfirewallrule: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }}second - state: absent - -- name: Gather facts MariaDB Firewall Rule - azure_rm_mariadbfirewallrule_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: firewallrule{{ rpfx }} - register: output -- name: Assert that empty list was returned - assert: - that: - - output.changed == False - - "output.rules | length == 0" - -# -# configuration -# -- name: Create instance of Configuration -- check mode - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - value: "ON" - check_mode: yes - register: output -- name: Assert that change was registered - assert: - that: - - output.changed - -- name: Try to delete default configuraion - azure_rm_mariadbconfiguration_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - register: output -- name: Get facts of event_scheduler - debug: - var: output - -- name: Try to delete default configuraion - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - state: absent - register: output -- name: Assert that change was registered - assert: - that: - - not output.changed - -- name: Try to change default configuraion - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - value: "ON" - register: output -- name: Assert that change was registered - assert: - that: - - output.changed - -- name: Try to change default configuration -- idempotent - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - value: "ON" - register: output -- name: Assert that change was registered - assert: - that: - - not output.changed - -- name: Try to reset configuration - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - state: absent - register: output -- name: Assert that change was registered - assert: - that: - - output.changed - -- name: Try to reset configuration -- idempotent - azure_rm_mariadbconfiguration: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - state: absent - register: output -- name: Assert that change was registered - assert: - that: - - not output.changed - -- name: Gather facts MariaDB Configuration - azure_rm_mariadbconfiguration_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - name: event_scheduler - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.settings[0].id != None - - output.settings[0].name != None - - output.settings[0].value != None - - output.settings[0].description != None - - output.settings[0].source != None - - output.settings | length == 1 - -- name: Gather facts MariaDB Configuration - azure_rm_mariadbconfiguration_facts: - resource_group: "{{ resource_group }}" - server_name: mariadbsrv{{ rpfx }} - register: output -- name: Assert that facts are returned - assert: - that: - - output.changed == False - - output.settings[0].id != None - - output.settings[0].name != None - - output.settings[0].value != None - - output.settings[0].description != None - - output.settings[0].source != None - - output.settings | length > 1 - -# -# clean up azure_rm_mariadbserver test -# - -- name: Delete instance of MariaDB Server -- check mode - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - state: absent - check_mode: yes - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed - -- name: Delete unexisting instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }} - state: absent - register: output -- name: Assert the state has changed - assert: - that: - - output.changed == false - -- name: Delete second instance of MariaDB Server - azure_rm_mariadbserver: - resource_group: "{{ resource_group }}" - name: mariadbsrv{{ rpfx }}second - state: absent - async: 400 - poll: 0 diff --git a/test/integration/targets/incidental_azure_rm_resource/aliases b/test/integration/targets/incidental_azure_rm_resource/aliases deleted file mode 100644 index 9901373a..00000000 --- a/test/integration/targets/incidental_azure_rm_resource/aliases +++ /dev/null @@ -1,3 +0,0 @@ -cloud/azure -destructive -shippable/azure/incidental diff --git a/test/integration/targets/incidental_azure_rm_resource/tasks/main.yml b/test/integration/targets/incidental_azure_rm_resource/tasks/main.yml deleted file mode 100644 index 7c3024a5..00000000 --- a/test/integration/targets/incidental_azure_rm_resource/tasks/main.yml +++ /dev/null @@ -1,158 +0,0 @@ -- name: Prepare random number - set_fact: - nsgname: "{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - storageaccountname: "stacc{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - dbname: "mdb{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" - run_once: yes - -- name: Call REST API - azure_rm_resource: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - body: - location: eastus - idempotency: yes - register: output - -- name: Assert that something has changed - assert: - that: output.changed - -- name: Call REST API - azure_rm_resource: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - body: - location: eastus - idempotency: yes - register: output - -- name: Assert that nothing has changed - assert: - that: not output.changed - -- name: Call REST API - azure_rm_resource: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - body: - location: eastus - tags: - a: "abc" - b: "cde" - idempotency: yes - register: output - -- name: Assert that something has changed - assert: - that: output.changed - -- name: Try to get information about account - azure_rm_resource_facts: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - resource_name: "{{ nsgname }}" - register: output - -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response[0]['name'] != None - - output.response | length == 1 - -- name: Try to query a list - azure_rm_resource_facts: - api_version: '2018-02-01' - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - register: output -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response[0]['name'] != None - - output.response | length >= 1 - -- name: Try to query a list - same without API version - azure_rm_resource_facts: - resource_group: "{{ resource_group }}" - provider: network - resource_type: networksecuritygroups - register: output -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response[0]['name'] != None - - output.response | length >= 1 - -- name: Query all the resources in the resource group - azure_rm_resource_facts: - resource_group: "{{ resource_group }}" - resource_type: resources - register: output -- name: Assert value was returned - assert: - that: - - not output.changed - - output.response | length >= 1 - -- name: Create storage account that requires LRO polling - azure_rm_resource: - polling_timeout: 600 - polling_interval: 60 - api_version: '2018-07-01' - resource_group: "{{ resource_group }}" - provider: Storage - resource_type: storageAccounts - resource_name: "{{ storageaccountname }}" - body: - sku: - name: Standard_GRS - kind: Storage - location: eastus - register: output - -- name: Assert that storage was successfully created - assert: - that: "output['response']['name'] == '{{ storageaccountname }}'" - - -- name: Try to storage keys -- special case when subresource part has no name - azure_rm_resource: - resource_group: "{{ resource_group }}" - provider: storage - resource_type: storageAccounts - resource_name: "{{ storageaccountname }}" - subresource: - - type: listkeys - api_version: '2018-03-01-preview' - method: POST - register: keys - -- name: Assert that key was returned - assert: - that: keys['response']['keys'][0]['value'] | length > 0 - -- name: Delete storage - without API version - azure_rm_resource: - polling_timeout: 600 - polling_interval: 60 - method: DELETE - resource_group: "{{ resource_group }}" - provider: Storage - resource_type: storageAccounts - resource_name: "{{ storageaccountname }}" diff --git a/test/integration/targets/incidental_cloudformation/aliases b/test/integration/targets/incidental_cloudformation/aliases deleted file mode 100644 index 29f60feb..00000000 --- a/test/integration/targets/incidental_cloudformation/aliases +++ /dev/null @@ -1,2 +0,0 @@ -cloud/aws -shippable/aws/incidental diff --git a/test/integration/targets/incidental_cloudformation/defaults/main.yml b/test/integration/targets/incidental_cloudformation/defaults/main.yml deleted file mode 100644 index aaf0ca7e..00000000 --- a/test/integration/targets/incidental_cloudformation/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ -stack_name: "{{ resource_prefix }}" - -vpc_name: '{{ resource_prefix }}-vpc' -vpc_seed: '{{ resource_prefix }}' -vpc_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.0.0/16' -subnet_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.32.0/24' - -ec2_ami_name: 'amzn2-ami-hvm-2.*-x86_64-gp2' diff --git a/test/integration/targets/incidental_cloudformation/files/cf_template.json b/test/integration/targets/incidental_cloudformation/files/cf_template.json deleted file mode 100644 index ff4c5693..00000000 --- a/test/integration/targets/incidental_cloudformation/files/cf_template.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "AWSTemplateFormatVersion" : "2010-09-09", - - "Description" : "Create an Amazon EC2 instance.", - - "Parameters" : { - "InstanceType" : { - "Description" : "EC2 instance type", - "Type" : "String", - "Default" : "t3.nano", - "AllowedValues" : [ "t3.micro", "t3.nano"] - }, - "ImageId" : { - "Type" : "String" - }, - "SubnetId" : { - "Type" : "String" - } - }, - - "Resources" : { - "EC2Instance" : { - "Type" : "AWS::EC2::Instance", - "Properties" : { - "InstanceType" : { "Ref" : "InstanceType" }, - "ImageId" : { "Ref" : "ImageId" }, - "SubnetId": { "Ref" : "SubnetId" } - } - } - }, - - "Outputs" : { - "InstanceId" : { - "Value" : { "Ref" : "EC2Instance" } - } - } -} diff --git a/test/integration/targets/incidental_cloudformation/tasks/main.yml b/test/integration/targets/incidental_cloudformation/tasks/main.yml deleted file mode 100644 index 10924bcd..00000000 --- a/test/integration/targets/incidental_cloudformation/tasks/main.yml +++ /dev/null @@ -1,476 +0,0 @@ ---- -- name: set up aws connection info - set_fact: - aws_connection_info: &aws_connection_info - aws_access_key: "{{ aws_access_key | default(omit) }}" - aws_secret_key: "{{ aws_secret_key | default(omit) }}" - security_token: "{{ security_token | default(omit) }}" - region: "{{ aws_region | default(omit) }}" - no_log: yes - -- module_defaults: - cloudformation: - <<: *aws_connection_info - cloudformation_info: - <<: *aws_connection_info - - block: - - # ==== Env setup ========================================================== - - name: list available AZs - aws_az_info: - <<: *aws_connection_info - register: region_azs - - - name: pick an AZ for testing - set_fact: - availability_zone: "{{ region_azs.availability_zones[0].zone_name }}" - - - name: Create a test VPC - ec2_vpc_net: - name: "{{ vpc_name }}" - cidr_block: "{{ vpc_cidr }}" - tags: - Name: Cloudformation testing - <<: *aws_connection_info - register: testing_vpc - - - name: Create a test subnet - ec2_vpc_subnet: - vpc_id: "{{ testing_vpc.vpc.id }}" - cidr: "{{ subnet_cidr }}" - az: "{{ availability_zone }}" - <<: *aws_connection_info - register: testing_subnet - - - name: Find AMI to use - ec2_ami_info: - owners: 'amazon' - filters: - name: '{{ ec2_ami_name }}' - <<: *aws_connection_info - register: ec2_amis - - - name: Set fact with latest AMI - vars: - latest_ami: '{{ ec2_amis.images | sort(attribute="creation_date") | last }}' - set_fact: - ec2_ami_image: '{{ latest_ami.image_id }}' - - # ==== Cloudformation tests =============================================== - - # 1. Basic stack creation (check mode, actual run and idempotency) - # 2. Tags - # 3. cloudformation_info tests (basic + all_facts) - # 4. termination_protection - # 5. create_changeset + changeset_name - - # There is still scope to add tests for - - # 1. capabilities - # 2. stack_policy - # 3. on_create_failure (covered in unit tests) - # 4. Passing in a role - # 5. nested stacks? - - - - name: create a cloudformation stack (check mode) - cloudformation: - stack_name: "{{ stack_name }}" - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.nano" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: cf_stack - check_mode: yes - - - name: check task return attributes - assert: - that: - - cf_stack.changed - - "'msg' in cf_stack and 'New stack would be created' in cf_stack.msg" - - - name: create a cloudformation stack - cloudformation: - stack_name: "{{ stack_name }}" - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.nano" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: cf_stack - - - name: check task return attributes - assert: - that: - - cf_stack.changed - - "'events' in cf_stack" - - "'output' in cf_stack and 'Stack CREATE complete' in cf_stack.output" - - "'stack_outputs' in cf_stack and 'InstanceId' in cf_stack.stack_outputs" - - "'stack_resources' in cf_stack" - - - name: create a cloudformation stack (check mode) (idempotent) - cloudformation: - stack_name: "{{ stack_name }}" - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.nano" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: cf_stack - check_mode: yes - - - name: check task return attributes - assert: - that: - - not cf_stack.changed - - - name: create a cloudformation stack (idempotent) - cloudformation: - stack_name: "{{ stack_name }}" - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.nano" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: cf_stack - - - name: check task return attributes - assert: - that: - - not cf_stack.changed - - "'output' in cf_stack and 'Stack is already up-to-date.' in cf_stack.output" - - "'stack_outputs' in cf_stack and 'InstanceId' in cf_stack.stack_outputs" - - "'stack_resources' in cf_stack" - - - name: get stack details - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - - - name: assert stack info - assert: - that: - - "'cloudformation' in stack_info" - - "stack_info.cloudformation | length == 1" - - "stack_name in stack_info.cloudformation" - - "'stack_description' in stack_info.cloudformation[stack_name]" - - "'stack_outputs' in stack_info.cloudformation[stack_name]" - - "'stack_parameters' in stack_info.cloudformation[stack_name]" - - "'stack_tags' in stack_info.cloudformation[stack_name]" - - "stack_info.cloudformation[stack_name].stack_tags.Stack == stack_name" - - - name: get stack details (checkmode) - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - check_mode: yes - - - name: assert stack info - assert: - that: - - "'cloudformation' in stack_info" - - "stack_info.cloudformation | length == 1" - - "stack_name in stack_info.cloudformation" - - "'stack_description' in stack_info.cloudformation[stack_name]" - - "'stack_outputs' in stack_info.cloudformation[stack_name]" - - "'stack_parameters' in stack_info.cloudformation[stack_name]" - - "'stack_tags' in stack_info.cloudformation[stack_name]" - - "stack_info.cloudformation[stack_name].stack_tags.Stack == stack_name" - - - name: get stack details (all_facts) - cloudformation_info: - stack_name: "{{ stack_name }}" - all_facts: yes - register: stack_info - - - name: assert stack info - assert: - that: - - "'stack_events' in stack_info.cloudformation[stack_name]" - - "'stack_policy' in stack_info.cloudformation[stack_name]" - - "'stack_resource_list' in stack_info.cloudformation[stack_name]" - - "'stack_resources' in stack_info.cloudformation[stack_name]" - - "'stack_template' in stack_info.cloudformation[stack_name]" - - - name: get stack details (all_facts) (checkmode) - cloudformation_info: - stack_name: "{{ stack_name }}" - all_facts: yes - register: stack_info - check_mode: yes - - - name: assert stack info - assert: - that: - - "'stack_events' in stack_info.cloudformation[stack_name]" - - "'stack_policy' in stack_info.cloudformation[stack_name]" - - "'stack_resource_list' in stack_info.cloudformation[stack_name]" - - "'stack_resources' in stack_info.cloudformation[stack_name]" - - "'stack_template' in stack_info.cloudformation[stack_name]" - - # ==== Cloudformation tests (create changeset) ============================ - - # try to create a changeset by changing instance type - - name: create a changeset - cloudformation: - stack_name: "{{ stack_name }}" - create_changeset: yes - changeset_name: "test-changeset" - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.micro" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: create_changeset_result - - - name: assert changeset created - assert: - that: - - "create_changeset_result.changed" - - "'change_set_id' in create_changeset_result" - - "'Stack CREATE_CHANGESET complete' in create_changeset_result.output" - - - name: get stack details with changesets - cloudformation_info: - stack_name: "{{ stack_name }}" - stack_change_sets: True - register: stack_info - - - name: assert changesets in info - assert: - that: - - "'stack_change_sets' in stack_info.cloudformation[stack_name]" - - - name: get stack details with changesets (checkmode) - cloudformation_info: - stack_name: "{{ stack_name }}" - stack_change_sets: True - register: stack_info - check_mode: yes - - - name: assert changesets in info - assert: - that: - - "'stack_change_sets' in stack_info.cloudformation[stack_name]" - - # try to create an empty changeset by passing in unchanged template - - name: create a changeset - cloudformation: - stack_name: "{{ stack_name }}" - create_changeset: yes - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.nano" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: create_changeset_result - - - name: assert changeset created - assert: - that: - - "not create_changeset_result.changed" - - "'The created Change Set did not contain any changes to this stack and was deleted.' in create_changeset_result.output" - - # ==== Cloudformation tests (termination_protection) ====================== - - - name: set termination protection to true - cloudformation: - stack_name: "{{ stack_name }}" - termination_protection: yes - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.nano" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: cf_stack - -# This fails - #65592 -# - name: check task return attributes -# assert: -# that: -# - cf_stack.changed - - - name: get stack details - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - - - name: assert stack info - assert: - that: - - "stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" - - - name: get stack details (checkmode) - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - check_mode: yes - - - name: assert stack info - assert: - that: - - "stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" - - - name: set termination protection to false - cloudformation: - stack_name: "{{ stack_name }}" - termination_protection: no - template_body: "{{ lookup('file','cf_template.json') }}" - template_parameters: - InstanceType: "t3.nano" - ImageId: "{{ ec2_ami_image }}" - SubnetId: "{{ testing_subnet.subnet.id }}" - tags: - Stack: "{{ stack_name }}" - test: "{{ resource_prefix }}" - register: cf_stack - -# This fails - #65592 -# - name: check task return attributes -# assert: -# that: -# - cf_stack.changed - - - name: get stack details - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - - - name: assert stack info - assert: - that: - - "not stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" - - - name: get stack details (checkmode) - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - check_mode: yes - - - name: assert stack info - assert: - that: - - "not stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" - - # ==== Cloudformation tests (delete stack tests) ========================== - - - name: delete cloudformation stack (check mode) - cloudformation: - stack_name: "{{ stack_name }}" - state: absent - check_mode: yes - register: cf_stack - - - name: check task return attributes - assert: - that: - - cf_stack.changed - - "'msg' in cf_stack and 'Stack would be deleted' in cf_stack.msg" - - - name: delete cloudformation stack - cloudformation: - stack_name: "{{ stack_name }}" - state: absent - register: cf_stack - - - name: check task return attributes - assert: - that: - - cf_stack.changed - - "'output' in cf_stack and 'Stack Deleted' in cf_stack.output" - - - name: delete cloudformation stack (check mode) (idempotent) - cloudformation: - stack_name: "{{ stack_name }}" - state: absent - check_mode: yes - register: cf_stack - - - name: check task return attributes - assert: - that: - - not cf_stack.changed - - "'msg' in cf_stack" - - >- - "Stack doesn't exist" in cf_stack.msg - - - name: delete cloudformation stack (idempotent) - cloudformation: - stack_name: "{{ stack_name }}" - state: absent - register: cf_stack - - - name: check task return attributes - assert: - that: - - not cf_stack.changed - - "'output' in cf_stack and 'Stack not found.' in cf_stack.output" - - - name: get stack details - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - - - name: assert stack info - assert: - that: - - "not stack_info.cloudformation" - - - name: get stack details (checkmode) - cloudformation_info: - stack_name: "{{ stack_name }}" - register: stack_info - check_mode: yes - - - name: assert stack info - assert: - that: - - "not stack_info.cloudformation" - - # ==== Cleanup ============================================================ - - always: - - - name: delete stack - cloudformation: - stack_name: "{{ stack_name }}" - state: absent - ignore_errors: yes - - - name: Delete test subnet - ec2_vpc_subnet: - vpc_id: "{{ testing_vpc.vpc.id }}" - cidr: "{{ subnet_cidr }}" - state: absent - <<: *aws_connection_info - ignore_errors: yes - - - name: Delete test VPC - ec2_vpc_net: - name: "{{ vpc_name }}" - cidr_block: "{{ vpc_cidr }}" - state: absent - <<: *aws_connection_info - ignore_errors: yes diff --git a/test/integration/targets/incidental_flatpak_remote/aliases b/test/integration/targets/incidental_flatpak_remote/aliases deleted file mode 100644 index 32b7f55a..00000000 --- a/test/integration/targets/incidental_flatpak_remote/aliases +++ /dev/null @@ -1,8 +0,0 @@ -shippable/posix/incidental -destructive -skip/aix -skip/freebsd -skip/osx -skip/macos -skip/rhel -needs/root diff --git a/test/integration/targets/incidental_flatpak_remote/meta/main.yml b/test/integration/targets/incidental_flatpak_remote/meta/main.yml deleted file mode 100644 index a1c58bf1..00000000 --- a/test/integration/targets/incidental_flatpak_remote/meta/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - incidental_setup_flatpak_remote diff --git a/test/integration/targets/incidental_flatpak_remote/tasks/check_mode.yml b/test/integration/targets/incidental_flatpak_remote/tasks/check_mode.yml deleted file mode 100644 index 7ce89a8c..00000000 --- a/test/integration/targets/incidental_flatpak_remote/tasks/check_mode.yml +++ /dev/null @@ -1,101 +0,0 @@ -# - Tests with absent flatpak remote ------------------------------------------- - -# state=present - -- name: Test addition of absent flatpak remote (check mode) - flatpak_remote: - name: flatpak-test - flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo - state: present - register: addition_result - check_mode: true - -- name: Verify addition of absent flatpak remote test result (check mode) - assert: - that: - - "addition_result.changed == true" - msg: "Adding an absent flatpak remote shall mark module execution as changed" - -- name: Test non-existent idempotency of addition of absent flatpak remote (check mode) - flatpak_remote: - name: flatpak-test - flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo - state: present - register: double_addition_result - check_mode: true - -- name: > - Verify non-existent idempotency of addition of absent flatpak remote - test result (check mode) - assert: - that: - - "double_addition_result.changed == true" - msg: | - Adding an absent flatpak remote a second time shall still mark module execution - as changed in check mode - -# state=absent - -- name: Test removal of absent flatpak remote not doing anything in check mode - flatpak_remote: - name: flatpak-test - state: absent - register: removal_result - check_mode: true - -- name: Verify removal of absent flatpak remote test result (check mode) - assert: - that: - - "removal_result.changed == false" - msg: "Removing an absent flatpak remote shall mark module execution as not changed" - - -# - Tests with present flatpak remote ------------------------------------------- - -# state=present - -- name: Test addition of present flatpak remote (check mode) - flatpak_remote: - name: check-mode-test-remote - flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo - state: present - register: addition_result - check_mode: true - -- name: Verify addition of present flatpak remote test result (check mode) - assert: - that: - - "addition_result.changed == false" - msg: "Adding a present flatpak remote shall mark module execution as not changed" - -# state=absent - -- name: Test removal of present flatpak remote not doing anything in check mode - flatpak_remote: - name: check-mode-test-remote - state: absent - register: removal_result - check_mode: true - -- name: Verify removal of present flatpak remote test result (check mode) - assert: - that: - - "removal_result.changed == true" - msg: "Removing a present flatpak remote shall mark module execution as changed" - -- name: Test non-existent idempotency of removal of present flatpak remote (check mode) - flatpak_remote: - name: check-mode-test-remote - state: absent - register: double_removal_result - check_mode: true - -- name: > - Verify non-existent idempotency of removal of present flatpak remote - test result (check mode) - assert: - that: - - "double_removal_result.changed == true" - msg: | - Removing a present flatpak remote a second time shall still mark module execution - as changed in check mode diff --git a/test/integration/targets/incidental_flatpak_remote/tasks/main.yml b/test/integration/targets/incidental_flatpak_remote/tasks/main.yml deleted file mode 100644 index 9c3ec6d7..00000000 --- a/test/integration/targets/incidental_flatpak_remote/tasks/main.yml +++ /dev/null @@ -1,57 +0,0 @@ -# (c) 2018, Alexander Bethke <oolongbrothers@gmx.net> -# (c) 2018, Ansible Project - -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. - -- block: - - - import_tasks: setup.yml - become: true - - # executable override - - - name: Test executable override - flatpak_remote: - name: irrelevant - remote: irrelevant - state: present - executable: nothing-that-exists - ignore_errors: true - register: executable_override_result - - - name: Verify executable override test result - assert: - that: - - "executable_override_result.failed == true" - - "executable_override_result.changed == false" - msg: "Specifying non-existing executable shall fail module execution" - - - import_tasks: check_mode.yml - become: false - - - import_tasks: test.yml - become: false - vars: - method: user - - - import_tasks: test.yml - become: true - vars: - method: system - - when: | - ansible_distribution == 'Fedora' or - ansible_distribution == 'Ubuntu' and not ansible_distribution_major_version | int < 16 diff --git a/test/integration/targets/incidental_flatpak_remote/tasks/setup.yml b/test/integration/targets/incidental_flatpak_remote/tasks/setup.yml deleted file mode 100644 index b2fd2766..00000000 --- a/test/integration/targets/incidental_flatpak_remote/tasks/setup.yml +++ /dev/null @@ -1,27 +0,0 @@ -- name: Install flatpak on Fedora - dnf: - name: flatpak - state: present - - when: ansible_distribution == 'Fedora' - -- block: - - name: Activate flatpak ppa on Ubuntu versions older than 18.04/bionic - apt_repository: - repo: "ppa:alexlarsson/flatpak" - state: present - mode: 0644 - when: ansible_lsb.major_release | int < 18 - - - name: Install flatpak package on Ubuntu - apt: - name: flatpak - state: present - - when: ansible_distribution == 'Ubuntu' - -- name: Install flatpak remote for testing check mode - flatpak_remote: - name: check-mode-test-remote - flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo - state: present diff --git a/test/integration/targets/incidental_flatpak_remote/tasks/test.yml b/test/integration/targets/incidental_flatpak_remote/tasks/test.yml deleted file mode 100644 index 97a13f0c..00000000 --- a/test/integration/targets/incidental_flatpak_remote/tasks/test.yml +++ /dev/null @@ -1,72 +0,0 @@ -# state=present - -- name: Test addition - {{ method }} - flatpak_remote: - name: flatpak-test - flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo - state: present - method: "{{ method }}" - register: addition_result - -- name: Verify addition test result - {{ method }} - assert: - that: - - "addition_result.changed == true" - msg: "state=preset shall add flatpak when absent" - -- name: Test idempotency of addition - {{ method }} - flatpak_remote: - name: flatpak-test - flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo - state: present - method: "{{ method }}" - register: double_addition_result - -- name: Verify idempotency of addition test result - {{ method }} - assert: - that: - - "double_addition_result.changed == false" - msg: "state=present shall not do anything when flatpak is already present" - -- name: Test updating remote url does not do anything - {{ method }} - flatpak_remote: - name: flatpak-test - flatpakrepo_url: https://a.different/repo.flatpakrepo - state: present - method: "{{ method }}" - register: url_update_result - -- name: Verify updating remote url does not do anything - {{ method }} - assert: - that: - - "url_update_result.changed == false" - msg: "Trying to update the URL of an existing flatpak remote shall not do anything" - - -# state=absent - -- name: Test removal - {{ method }} - flatpak_remote: - name: flatpak-test - state: absent - method: "{{ method }}" - register: removal_result - -- name: Verify removal test result - {{ method }} - assert: - that: - - "removal_result.changed == true" - msg: "state=absent shall remove flatpak when present" - -- name: Test idempotency of removal - {{ method }} - flatpak_remote: - name: flatpak-test - state: absent - method: "{{ method }}" - register: double_removal_result - -- name: Verify idempotency of removal test result - {{ method }} - assert: - that: - - "double_removal_result.changed == false" - msg: "state=absent shall not do anything when flatpak is not present" diff --git a/test/integration/targets/incidental_lookup_rabbitmq/aliases b/test/integration/targets/incidental_lookup_rabbitmq/aliases deleted file mode 100644 index f89752b8..00000000 --- a/test/integration/targets/incidental_lookup_rabbitmq/aliases +++ /dev/null @@ -1,6 +0,0 @@ -destructive -shippable/posix/incidental -skip/aix -skip/osx -skip/freebsd -skip/rhel diff --git a/test/integration/targets/incidental_lookup_rabbitmq/meta/main.yml b/test/integration/targets/incidental_lookup_rabbitmq/meta/main.yml deleted file mode 100644 index 33fa97dc..00000000 --- a/test/integration/targets/incidental_lookup_rabbitmq/meta/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - incidental_setup_rabbitmq diff --git a/test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml b/test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml deleted file mode 100644 index 7c9553c5..00000000 --- a/test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# Rabbitmq lookup -- include: ubuntu.yml - when: - - ansible_distribution == 'Ubuntu' - - ansible_distribution_release not in ('trusty', 'focal') diff --git a/test/integration/targets/incidental_lookup_rabbitmq/tasks/ubuntu.yml b/test/integration/targets/incidental_lookup_rabbitmq/tasks/ubuntu.yml deleted file mode 100644 index 3b007ede..00000000 --- a/test/integration/targets/incidental_lookup_rabbitmq/tasks/ubuntu.yml +++ /dev/null @@ -1,138 +0,0 @@ -- name: Test failure without pika installed - set_fact: - rabbit_missing_pika: "{{ lookup('rabbitmq', url='amqp://guest:guest@192.168.250.1:5672/%2F', queue='hello', count=3) }}" - ignore_errors: yes - register: rabbitmq_missing_pika_error - -- assert: - that: - - "'pika python package is required' in rabbitmq_missing_pika_error.msg" - -- name: Install pika and requests - pip: - name: pika<1.0.0,requests - state: latest - -- name: Test that giving an incorrect amqp protocol in URL will error - set_fact: - rabbitmq_test_protocol: "{{ lookup('rabbitmq', url='zzzamqp://guest:guest@192.168.250.1:5672/%2F', queue='hello', count=3) }}" - ignore_errors: yes - register: rabbitmq_protocol_error - -- assert: - that: - - "rabbitmq_protocol_error is failed" - - "'URL malformed' in rabbitmq_protocol_error.msg" - -- name: Test that giving an incorrect IP address in URL will error - set_fact: - rabbitmq_test_protocol: "{{ lookup('rabbitmq', url='amqp://guest:guest@xxxxx192.112312368.250.1:5672/%2F', queue='hello', count=3) }}" - ignore_errors: yes - register: rabbitmq_ip_error - -- assert: - that: - - "rabbitmq_ip_error is failed" - - "'Connection issue' in rabbitmq_ip_error.msg" - -- name: Test missing parameters will error - set_fact: - rabbitmq_test_protocol: "{{ lookup('rabbitmq') }}" - ignore_errors: yes - register: rabbitmq_params_error - -- assert: - that: - - "rabbitmq_params_error is failed" - - "'URL is required for rabbitmq lookup.' in rabbitmq_params_error.msg" - -- name: Test missing queue will error - set_fact: - rabbitmq_queue_protocol: "{{ lookup('rabbitmq', url='amqp://guest:guest@192.168.250.1:5672/%2F') }}" - ignore_errors: yes - register: rabbitmq_queue_error - -- assert: - that: - - "rabbitmq_queue_error is failed" - - "'Queue is required for rabbitmq lookup' in rabbitmq_queue_error.msg" - -- name: Enables the rabbitmq_management plugin - rabbitmq_plugin: - names: rabbitmq_management - state: enabled - -- name: Setup test queue - rabbitmq_queue: - name: hello - -- name: Post test message to the exchange (string) - uri: - url: http://localhost:15672/api/exchanges/%2f/amq.default/publish - method: POST - body: '{"properties":{},"routing_key":"hello","payload":"ansible-test","payload_encoding":"string"}' - user: guest - password: guest - force_basic_auth: yes - return_content: yes - headers: - Content-Type: "application/json" - register: post_data - - -- name: Post test message to the exchange (json) - uri: - url: http://localhost:15672/api/exchanges/%2f/amq.default/publish - method: POST - body: '{"properties":{"content_type": "application/json"},"routing_key":"hello","payload":"{\"key\": \"value\" }","payload_encoding":"string"}' - user: guest - password: guest - force_basic_auth: yes - return_content: yes - headers: - Content-Type: "application/json" - register: post_data_json - -- name: Test retrieve messages - set_fact: - rabbitmq_msg: "{{ lookup('rabbitmq', url='amqp://guest:guest@localhost:5672/%2f/hello', queue='hello') }}" - ignore_errors: yes - register: rabbitmq_msg_error - -- name: Ensure two messages received - assert: - that: - - "rabbitmq_msg_error is not failed" - - rabbitmq_msg | length == 2 - -- name: Ensure first message is a string - assert: - that: - - rabbitmq_msg[0].msg == "ansible-test" - -- name: Ensure second message is json - assert: - that: - - rabbitmq_msg[1].json.key == "value" - -- name: Test missing vhost - set_fact: - rabbitmq_msg: "{{ lookup('rabbitmq', url='amqp://guest:guest@localhost:5672/missing/', queue='hello') }}" - ignore_errors: yes - register: rabbitmq_vhost_error - -- assert: - that: - - "rabbitmq_vhost_error is failed" - - "'NOT_ALLOWED' in rabbitmq_vhost_error.msg" - -# Tidy up -- name: Uninstall pika and requests - pip: - name: pika,requests - state: absent - -- name: Disable the rabbitmq_management plugin - rabbitmq_plugin: - names: rabbitmq_management - state: disabled diff --git a/test/integration/targets/incidental_lvg/aliases b/test/integration/targets/incidental_lvg/aliases deleted file mode 100644 index d5baa06d..00000000 --- a/test/integration/targets/incidental_lvg/aliases +++ /dev/null @@ -1,6 +0,0 @@ -destructive -needs/privileged -shippable/posix/incidental -skip/aix -skip/freebsd -skip/osx diff --git a/test/integration/targets/incidental_lvg/tasks/main.yml b/test/integration/targets/incidental_lvg/tasks/main.yml deleted file mode 100644 index a57f591b..00000000 --- a/test/integration/targets/incidental_lvg/tasks/main.yml +++ /dev/null @@ -1,15 +0,0 @@ -- name: Install required packages (Linux) - package: - name: lvm2 - state: present - when: ansible_system == 'Linux' - -- name: Test lvg module - block: - - import_tasks: setup.yml - - - import_tasks: test_indempotency.yml - - - import_tasks: test_grow_reduce.yml - always: - - import_tasks: teardown.yml diff --git a/test/integration/targets/incidental_lvg/tasks/setup.yml b/test/integration/targets/incidental_lvg/tasks/setup.yml deleted file mode 100644 index e63c2d64..00000000 --- a/test/integration/targets/incidental_lvg/tasks/setup.yml +++ /dev/null @@ -1,13 +0,0 @@ -- name: "Create files to use as a disk devices" - command: "dd if=/dev/zero of={{ remote_tmp_dir }}/img{{ item }} bs=1M count=10" - with_sequence: 'count=2' - -- name: "Create loop device for file" - command: "losetup --show -f {{ remote_tmp_dir }}/img{{ item }}" - with_sequence: 'count=2' - register: loop_devices - -- name: "Affect name on disk to work on" - set_fact: - loop_device1: "{{ loop_devices.results[0] }}" - loop_device2: "{{ loop_devices.results[1] }}" diff --git a/test/integration/targets/incidental_lvg/tasks/teardown.yml b/test/integration/targets/incidental_lvg/tasks/teardown.yml deleted file mode 100644 index ed662f1e..00000000 --- a/test/integration/targets/incidental_lvg/tasks/teardown.yml +++ /dev/null @@ -1,17 +0,0 @@ -- name: Remove test volume group - lvg: - vg: testvg - state: absent - -- name: Detach loop device - command: "losetup -d {{ item.stdout }}" - loop: "{{ loop_devices.results|default([]) }}" - when: - - item.stdout is defined - - item.stdout is match("/dev/.*") - -- name: Remove device files - file: - path: "{{ remote_tmp_dir }}/img{{ item }}" - state: absent - with_sequence: 'count={{ loop_devices.results|length }}' diff --git a/test/integration/targets/incidental_lvg/tasks/test_grow_reduce.yml b/test/integration/targets/incidental_lvg/tasks/test_grow_reduce.yml deleted file mode 100644 index 1e988045..00000000 --- a/test/integration/targets/incidental_lvg/tasks/test_grow_reduce.yml +++ /dev/null @@ -1,33 +0,0 @@ -- name: "Create volume group on first disk" - lvg: - vg: testvg - pvs: "{{ loop_device1.stdout }}" - -- name: "get lvm facts" - setup: - -- debug: var=ansible_lvm - -- name: "Assert the testvg span only on first disk" - assert: - that: - - ansible_lvm.pvs[loop_device1.stdout].vg == "testvg" - - 'loop_device2.stdout not in ansible_lvm.pvs or - ansible_lvm.pvs[loop_device2.stdout].vg == ""' - -- name: "Extend to second disk AND reduce from the first disk" - lvg: - vg: testvg - pvs: "{{ loop_device2.stdout }}" - -- name: "get lvm facts" - setup: - -- debug: var=ansible_lvm - -- name: "Assert the testvg span only on first disk" - assert: - that: - - 'loop_device1.stdout not in ansible_lvm.pvs or - ansible_lvm.pvs[loop_device1.stdout].vg == ""' - - ansible_lvm.pvs[loop_device2.stdout].vg == "testvg" diff --git a/test/integration/targets/incidental_lvg/tasks/test_indempotency.yml b/test/integration/targets/incidental_lvg/tasks/test_indempotency.yml deleted file mode 100644 index 5007e56a..00000000 --- a/test/integration/targets/incidental_lvg/tasks/test_indempotency.yml +++ /dev/null @@ -1,15 +0,0 @@ -- name: Create volume group on disk device - lvg: - vg: testvg - pvs: "{{ loop_device1.stdout }}" - -- name: Create the volume group again to verify idempotence - lvg: - vg: testvg - pvs: "{{ loop_device1.stdout }}" - register: repeat_vg_create - -- name: Do all assertions to verify expected results - assert: - that: - - repeat_vg_create is not changed diff --git a/test/integration/targets/incidental_postgresql_user/aliases b/test/integration/targets/incidental_postgresql_user/aliases deleted file mode 100644 index 78b47900..00000000 --- a/test/integration/targets/incidental_postgresql_user/aliases +++ /dev/null @@ -1,4 +0,0 @@ -destructive -shippable/posix/incidental -skip/aix -skip/osx diff --git a/test/integration/targets/incidental_postgresql_user/defaults/main.yml b/test/integration/targets/incidental_postgresql_user/defaults/main.yml deleted file mode 100644 index bc9ef19b..00000000 --- a/test/integration/targets/incidental_postgresql_user/defaults/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -db_name: 'ansible_db' -db_user1: 'ansible_db_user1' -db_user2: 'ansible_db_user2' diff --git a/test/integration/targets/incidental_postgresql_user/meta/main.yml b/test/integration/targets/incidental_postgresql_user/meta/main.yml deleted file mode 100644 index c2a0d561..00000000 --- a/test/integration/targets/incidental_postgresql_user/meta/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - incidental_setup_postgresql_db diff --git a/test/integration/targets/incidental_postgresql_user/tasks/main.yml b/test/integration/targets/incidental_postgresql_user/tasks/main.yml deleted file mode 100644 index d59ae635..00000000 --- a/test/integration/targets/incidental_postgresql_user/tasks/main.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Initial CI tests of postgresql_user module -- import_tasks: postgresql_user_initial.yml - when: postgres_version_resp.stdout is version('9.4', '>=') - -# General tests: -- import_tasks: postgresql_user_general.yml - when: postgres_version_resp.stdout is version('9.4', '>=') diff --git a/test/integration/targets/incidental_postgresql_user/tasks/postgresql_user_general.yml b/test/integration/targets/incidental_postgresql_user/tasks/postgresql_user_general.yml deleted file mode 100644 index 963f58ac..00000000 --- a/test/integration/targets/incidental_postgresql_user/tasks/postgresql_user_general.yml +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Integration tests for postgresql_user module. - -- vars: - test_user: hello.user.with.dots - test_user2: hello - test_group1: group1 - test_group2: group2 - test_table: test - test_comment1: 'comment1' - test_comment2: 'comment2' - task_parameters: &task_parameters - become_user: '{{ pg_user }}' - become: yes - register: result - pg_parameters: &pg_parameters - login_user: '{{ pg_user }}' - login_db: postgres - - block: - # - # Common tests - # - - name: Create role in check_mode - <<: *task_parameters - check_mode: yes - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: check that the user doesn't exist - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'" - - - assert: - that: - - result.rowcount == 0 - - - name: Create role in actual mode - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: check that the user exists - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'" - - - assert: - that: - - result.rowcount == 1 - - - name: Add a comment on the user - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - comment: '{{ test_comment1 }}' - - - assert: - that: - - result is changed - - result.queries == ["COMMENT ON ROLE \"{{ test_user }}\" IS '{{ test_comment1 }}'"] - - - name: check the comment - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT pg_catalog.shobj_description(r.oid, 'pg_authid') AS comment - FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user }}' - - - assert: - that: - - result.rowcount == 1 - - result.query_result[0].comment == '{{ test_comment1 }}' - - - name: Try to add the same comment on the user - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - comment: '{{ test_comment1 }}' - - - assert: - that: - - result is not changed - - - name: Try to add another comment on the user - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - comment: '{{ test_comment2 }}' - - - assert: - that: - - result is changed - - result.queries == ["COMMENT ON ROLE \"{{ test_user }}\" IS '{{ test_comment2 }}'"] - - - name: check the comment - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT pg_catalog.shobj_description(r.oid, 'pg_authid') AS comment - FROM pg_catalog.pg_roles r WHERE r.rolname = '{{ test_user }}' - - - assert: - that: - - result.rowcount == 1 - - result.query_result[0].comment == '{{ test_comment2 }}' - - - name: Try to create role again in check_mode - <<: *task_parameters - check_mode: yes - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - - name: check that the user exists - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'" - - - assert: - that: - - result.rowcount == 1 - - - name: Try to create role again - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - - name: check that the user exists - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'" - - - assert: - that: - - result.rowcount == 1 - - - name: Drop role in check_mode - <<: *task_parameters - check_mode: yes - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - state: absent - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: check that the user actually exists - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'" - - - assert: - that: - - result.rowcount == 1 - - - name: Drop role in actual mode - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - state: absent - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: check that the user doesn't exist - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_user }}'" - - - assert: - that: - - result.rowcount == 0 - - - name: Try to drop role in check mode again - <<: *task_parameters - check_mode: yes - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - state: absent - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - - name: Try to drop role in actual mode again - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - state: absent - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - # - # password, no_password_changes, encrypted, expires parameters - # - - - name: Create role with password, passed as hashed md5 - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - password: md59543f1d82624df2b31672ec0f7050460 - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: Check that the user exist with a proper password - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword = 'md59543f1d82624df2b31672ec0f7050460'" - - - assert: - that: - - result.rowcount == 1 - - - name: Test no_password_changes - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - password: u123 - no_password_changes: yes - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - - - name: Check that nothing changed - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword = 'md59543f1d82624df2b31672ec0f7050460'" - - - assert: - that: - - result.rowcount == 1 - - # Storing unencrypted passwords is not available from PostgreSQL 10 - - name: Change password, passed as unencrypted - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - password: myunencryptedpass - encrypted: no - when: postgres_version_resp.stdout is version('10', '<') - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - when: postgres_version_resp.stdout is version('10', '<') - - - name: Check that the user exist with the unencrypted password - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword = 'myunencryptedpass'" - when: postgres_version_resp.stdout is version('10', '<') - - - assert: - that: - - result.rowcount == 1 - when: postgres_version_resp.stdout is version('10', '<') - - - name: Change password, explicit encrypted=yes - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - password: myunencryptedpass - encrypted: yes - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: Check that the user exist with encrypted password - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' and rolpassword != 'myunencryptedpass'" - - - assert: - that: - - result.rowcount == 1 - - - name: Change rolvaliduntil attribute - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - expires: 'Jan 31 2020' - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: Check the prev step - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' - AND rolvaliduntil::text like '2020-01-31%' - - - assert: - that: - - result.rowcount == 1 - - - name: Try to set the same rolvaliduntil value again - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - expires: 'Jan 31 2020' - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - - name: Check that nothing changed - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' - AND rolvaliduntil::text like '2020-01-31%' - - - assert: - that: - - result.rowcount == 1 - - # - # role_attr_flags - # - - name: Set role attributes - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - role_attr_flags: CREATEROLE,CREATEDB - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: Check the prev step - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' - AND rolcreaterole = 't' and rolcreatedb = 't' - - - assert: - that: - - result.rowcount == 1 - - - name: Set the same role attributes again - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - role_attr_flags: CREATEROLE,CREATEDB - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - - name: Check the prev step - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' - AND rolcreaterole = 't' and rolcreatedb = 't' - - - name: Set role attributes - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - role_attr_flags: NOCREATEROLE,NOCREATEDB - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - - name: Check the prev step - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' - AND rolcreaterole = 'f' and rolcreatedb = 'f' - - - assert: - that: - - result.rowcount == 1 - - - name: Set role attributes - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - role_attr_flags: NOCREATEROLE,NOCREATEDB - - - assert: - that: - - result is not changed - - result.user == '{{ test_user }}' - - - name: Check the prev step - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: > - SELECT rolname FROM pg_authid WHERE rolname = '{{ test_user }}' - AND rolcreaterole = 'f' and rolcreatedb = 'f' - - # - # priv - # - - name: Create test table - <<: *task_parameters - postgresql_table: - <<: *pg_parameters - name: '{{ test_table }}' - columns: - - id int - - - name: Insert data to test table - <<: *task_parameters - postgresql_query: - query: "INSERT INTO {{ test_table }} (id) VALUES ('1')" - <<: *pg_parameters - - - name: Check that test_user is not allowed to read the data - <<: *task_parameters - postgresql_query: - db: postgres - login_user: '{{ pg_user }}' - session_role: '{{ test_user }}' - query: 'SELECT * FROM {{ test_table }}' - ignore_errors: yes - - - assert: - that: - - result is failed - - "'permission denied' in result.msg" - - - name: Grant privileges - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - priv: '{{ test_table }}:SELECT' - - - assert: - that: - - result is changed - - - name: Check that test_user is allowed to read the data - <<: *task_parameters - postgresql_query: - db: postgres - login_user: '{{ pg_user }}' - session_role: '{{ test_user }}' - query: 'SELECT * FROM {{ test_table }}' - - - assert: - that: - - result.rowcount == 1 - - - name: Grant the same privileges again - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - priv: '{{ test_table }}:SELECT' - - - assert: - that: - - result is not changed - - - name: Remove test table - <<: *task_parameters - postgresql_table: - <<: *pg_parameters - name: '{{ test_table }}' - state: absent - - # - # fail_on_user - # - - name: Create role for test - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user2 }}' - - - name: Create test table, set owner as test_user - <<: *task_parameters - postgresql_table: - <<: *pg_parameters - name: '{{ test_table }}' - owner: '{{ test_user2 }}' - - - name: Test fail_on_user - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user2 }}' - state: absent - ignore_errors: yes - - - assert: - that: - - result is failed - - result.msg == 'Unable to remove user' - - - name: Test fail_on_user - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - fail_on_user: no - - - assert: - that: - - result is not changed - - # - # Test groups parameter - # - - name: Create test group - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_group2 }}' - role_attr_flags: NOLOGIN - - - name: Create role test_group1 and grant test_group2 to test_group1 in check_mode - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_group1 }}' - groups: '{{ test_group2 }}' - role_attr_flags: NOLOGIN - check_mode: yes - - - assert: - that: - - result is changed - - result.user == '{{ test_group1 }}' - - result.queries == ['CREATE USER "{{ test_group1 }}" NOLOGIN', 'GRANT "{{ test_group2 }}" TO "{{ test_group1 }}"'] - - - name: check that the user doesn't exist - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_group1 }}'" - - - assert: - that: - - result.rowcount == 0 - - - name: check membership - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT grolist FROM pg_group WHERE groname = '{{ test_group2 }}' AND grolist != '{}'" - - - assert: - that: - - result.rowcount == 0 - - - name: Create role test_group1 and grant test_group2 to test_group1 - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_group1 }}' - groups: '{{ test_group2 }}' - role_attr_flags: NOLOGIN - - - assert: - that: - - result is changed - - result.user == '{{ test_group1 }}' - - result.queries == ['CREATE USER "{{ test_group1 }}" NOLOGIN', 'GRANT "{{ test_group2 }}" TO "{{ test_group1 }}"'] - - - name: check that the user exists - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT rolname FROM pg_roles WHERE rolname = '{{ test_group1 }}'" - - - assert: - that: - - result.rowcount == 1 - - - name: check membership - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT grolist FROM pg_group WHERE groname = '{{ test_group2 }}' AND grolist != '{}'" - - - assert: - that: - - result.rowcount == 1 - - - name: Grant test_group2 to test_group1 again - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_group1 }}' - groups: '{{ test_group2 }}' - - - assert: - that: - - result is not changed - - result.user == '{{ test_group1 }}' - - - name: check membership - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT grolist FROM pg_group WHERE groname = '{{ test_group2 }}' AND grolist != '{}'" - - - assert: - that: - - result.rowcount == 1 - - - name: Grant groups to existent role - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ test_user }}' - groups: - - '{{ test_group1 }}' - - '{{ test_group2 }}' - - - assert: - that: - - result is changed - - result.user == '{{ test_user }}' - - result.queries == ['GRANT "{{ test_group1 }}" TO "{{ test_user }}"', 'GRANT "{{ test_group2 }}" TO "{{ test_user }}"'] - - - name: check membership - <<: *task_parameters - postgresql_query: - <<: *pg_parameters - query: "SELECT * FROM pg_group WHERE groname in ('{{ test_group1 }}', '{{ test_group2 }}') AND grolist != '{}'" - - - assert: - that: - - result.rowcount == 2 - - always: - # - # Clean up - # - - name: Drop test table - <<: *task_parameters - postgresql_table: - <<: *pg_parameters - name: '{{ test_table }}' - state: absent - - - name: Drop test user - <<: *task_parameters - postgresql_user: - <<: *pg_parameters - name: '{{ item }}' - state: absent - loop: - - '{{ test_user }}' - - '{{ test_user2 }}' - - '{{ test_group1 }}' - - '{{ test_group2 }}' diff --git a/test/integration/targets/incidental_postgresql_user/tasks/postgresql_user_initial.yml b/test/integration/targets/incidental_postgresql_user/tasks/postgresql_user_initial.yml deleted file mode 100644 index ccd42847..00000000 --- a/test/integration/targets/incidental_postgresql_user/tasks/postgresql_user_initial.yml +++ /dev/null @@ -1,153 +0,0 @@ -# -# Create and destroy user, test 'password' and 'encrypted' parameters -# -# unencrypted values are not supported on newer versions -# do not run the encrypted: no tests if on 10+ -- set_fact: - encryption_values: - - 'yes' - -- set_fact: - encryption_values: '{{ encryption_values + ["no"]}}' - when: postgres_version_resp.stdout is version('10', '<=') - -- include_tasks: test_password.yml - vars: - encrypted: '{{ loop_item }}' - db_password1: 'secretù' # use UTF-8 - loop: '{{ encryption_values }}' - loop_control: - loop_var: loop_item - -# BYPASSRLS role attribute was introduced in PostgreSQL 9.5, so -# we want to test attribute management differently depending -# on the version. -- set_fact: - bypassrls_supported: "{{ postgres_version_resp.stdout is version('9.5.0', '>=') }}" - -# test 'no_password_change' and 'role_attr_flags' parameters -- include_tasks: test_no_password_change.yml - vars: - no_password_changes: '{{ loop_item }}' - loop: - - 'yes' - - 'no' - loop_control: - loop_var: loop_item - -### TODO: fail_on_user - -# -# Test login_user functionality -# -- name: Create a user to test login module parameters - become: yes - become_user: "{{ pg_user }}" - postgresql_user: - name: "{{ db_user1 }}" - state: "present" - encrypted: 'yes' - password: "password" - role_attr_flags: "CREATEDB,LOGIN,CREATEROLE" - login_user: "{{ pg_user }}" - db: postgres - -- name: Create db - postgresql_db: - name: "{{ db_name }}" - state: "present" - login_user: "{{ db_user1 }}" - login_password: "password" - login_host: "localhost" - -- name: Check that database created - become: yes - become_user: "{{ pg_user }}" - shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres - register: result - -- assert: - that: - - "result.stdout_lines[-1] == '(1 row)'" - -- name: Create a user - postgresql_user: - name: "{{ db_user2 }}" - state: "present" - encrypted: 'yes' - password: "md55c8ccfd9d6711fc69a7eae647fc54f51" - db: "{{ db_name }}" - login_user: "{{ db_user1 }}" - login_password: "password" - login_host: "localhost" - -- name: Check that it was created - become: yes - become_user: "{{ pg_user }}" - shell: echo "select * from pg_user where usename='{{ db_user2 }}';" | psql -d postgres - register: result - -- assert: - that: - - "result.stdout_lines[-1] == '(1 row)'" - -- name: Grant database privileges - postgresql_privs: - type: "database" - state: "present" - roles: "{{ db_user2 }}" - privs: "CREATE,connect" - objs: "{{ db_name }}" - db: "{{ db_name }}" - login: "{{ db_user1 }}" - password: "password" - host: "localhost" - -- name: Check that the user has the requested permissions (database) - become: yes - become_user: "{{ pg_user }}" - shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }} - register: result_database - -- assert: - that: - - "result_database.stdout_lines[-1] == '(1 row)'" - - "db_user2 ~ '=Cc' in result_database.stdout" - -- name: Remove user - postgresql_user: - name: "{{ db_user2 }}" - state: 'absent' - priv: "ALL" - db: "{{ db_name }}" - login_user: "{{ db_user1 }}" - login_password: "password" - login_host: "localhost" - -- name: Check that they were removed - become: yes - become_user: "{{ pg_user }}" - shell: echo "select * from pg_user where usename='{{ db_user2 }}';" | psql -d postgres - register: result - -- assert: - that: - - "result.stdout_lines[-1] == '(0 rows)'" - -- name: Destroy DB - postgresql_db: - state: absent - name: "{{ db_name }}" - login_user: "{{ db_user1 }}" - login_password: "password" - login_host: "localhost" - -- name: Check that database was destroyed - become: yes - become_user: "{{ pg_user }}" - shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql -d postgres - register: result - -- assert: - that: - - "result.stdout_lines[-1] == '(0 rows)'" diff --git a/test/integration/targets/incidental_postgresql_user/tasks/test_no_password_change.yml b/test/integration/targets/incidental_postgresql_user/tasks/test_no_password_change.yml deleted file mode 100644 index c296c0ea..00000000 --- a/test/integration/targets/incidental_postgresql_user/tasks/test_no_password_change.yml +++ /dev/null @@ -1,167 +0,0 @@ -- vars: - task_parameters: &task_parameters - become_user: "{{ pg_user }}" - become: yes - register: result - postgresql_parameters: ¶meters - db: postgres - name: "{{ db_user1 }}" - login_user: "{{ pg_user }}" - - block: - - - name: Create a user with all role attributes - <<: *task_parameters - postgresql_user: - <<: *parameters - state: "present" - role_attr_flags: "SUPERUSER,CREATEROLE,CREATEDB,INHERIT,LOGIN{{ bypassrls_supported | ternary(',BYPASSRLS', '') }}" - no_password_changes: '{{ no_password_changes }}' # no_password_changes is ignored when user doesn't already exist - - - name: Check that the user has the requested role attributes - <<: *task_parameters - shell: "echo \"select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin {{ bypassrls_supported | ternary(\", 'bypassrls:'||rolbypassrls\", '') }} from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres" - - - assert: - that: - - "result.stdout_lines[-1] == '(1 row)'" - - "'super:t' in result.stdout_lines[-2]" - - "'createrole:t' in result.stdout_lines[-2]" - - "'create:t' in result.stdout_lines[-2]" - - "'inherit:t' in result.stdout_lines[-2]" - - "'login:t' in result.stdout_lines[-2]" - - - block: - - name: Check that the user has the requested role attribute BYPASSRLS - <<: *task_parameters - shell: "echo \"select 'bypassrls:'||rolbypassrls from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres" - - - assert: - that: - - "not bypassrls_supported or 'bypassrls:t' in result.stdout_lines[-2]" - when: bypassrls_supported - - - name: Modify a user to have no role attributes - <<: *task_parameters - postgresql_user: - <<: *parameters - state: "present" - role_attr_flags: "NOSUPERUSER,NOCREATEROLE,NOCREATEDB,noinherit,NOLOGIN{{ bypassrls_supported | ternary(',NOBYPASSRLS', '') }}" - no_password_changes: '{{ no_password_changes }}' - - - name: Check that ansible reports it modified the role - assert: - that: - - result is changed - - - name: "Check that the user doesn't have any attribute" - <<: *task_parameters - shell: "echo \"select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres" - - - assert: - that: - - "result.stdout_lines[-1] == '(1 row)'" - - "'super:f' in result.stdout_lines[-2]" - - "'createrole:f' in result.stdout_lines[-2]" - - "'create:f' in result.stdout_lines[-2]" - - "'inherit:f' in result.stdout_lines[-2]" - - "'login:f' in result.stdout_lines[-2]" - - - block: - - name: Check that the user has the requested role attribute BYPASSRLS - <<: *task_parameters - shell: "echo \"select 'bypassrls:'||rolbypassrls from pg_roles where rolname='{{ db_user1 }}';\" | psql -d postgres" - - - assert: - that: - - "not bypassrls_supported or 'bypassrls:f' in result.stdout_lines[-2]" - when: bypassrls_supported - - - name: Try to add an invalid attribute - <<: *task_parameters - postgresql_user: - <<: *parameters - state: "present" - role_attr_flags: "NOSUPERUSER,NOCREATEROLE,NOCREATEDB,noinherit,NOLOGIN{{ bypassrls_supported | ternary(',NOBYPASSRLS', '') }},INVALID" - no_password_changes: '{{ no_password_changes }}' - ignore_errors: yes - - - name: Check that ansible reports failure - assert: - that: - - result is not changed - - result is failed - - "result.msg == 'Invalid role_attr_flags specified: INVALID'" - - - name: Modify a single role attribute on a user - <<: *task_parameters - postgresql_user: - <<: *parameters - state: "present" - role_attr_flags: "LOGIN" - no_password_changes: '{{ no_password_changes }}' - - - name: Check that ansible reports it modified the role - assert: - that: - - result is changed - - - name: Check the role attributes - <<: *task_parameters - shell: echo "select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin from pg_roles where rolname='{{ db_user1 }}';" | psql -d postgres - - - assert: - that: - - "result.stdout_lines[-1] == '(1 row)'" - - "'super:f' in result.stdout_lines[-2]" - - "'createrole:f' in result.stdout_lines[-2]" - - "'create:f' in result.stdout_lines[-2]" - - "'inherit:f' in result.stdout_lines[-2]" - - "'login:t' in result.stdout_lines[-2]" - - - block: - - name: Check the role attribute BYPASSRLS - <<: *task_parameters - shell: echo "select 'bypassrls:'||rolbypassrls from pg_roles where rolname='{{ db_user1 }}';" | psql -d postgres - - - assert: - that: - - "( postgres_version_resp.stdout is version('9.5.0', '<')) or 'bypassrls:f' in result.stdout_lines[-2]" - when: bypassrls_supported - - - name: Check that using same attribute a second time does nothing - <<: *task_parameters - postgresql_user: - <<: *parameters - state: "present" - role_attr_flags: "LOGIN" - no_password_changes: '{{ no_password_changes }}' - environment: - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - name: Check there isn't any update reported - assert: - that: - - result is not changed - - - name: Cleanup the user - <<: *task_parameters - postgresql_user: - <<: *parameters - state: 'absent' - no_password_changes: '{{ no_password_changes }}' # user deletion: no_password_changes is ignored - - - name: Check that user was removed - <<: *task_parameters - shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres - - - assert: - that: - - "result.stdout_lines[-1] == '(0 rows)'" - - always: - - name: Cleanup the user - <<: *task_parameters - postgresql_user: - <<: *parameters - state: 'absent' diff --git a/test/integration/targets/incidental_postgresql_user/tasks/test_password.yml b/test/integration/targets/incidental_postgresql_user/tasks/test_password.yml deleted file mode 100644 index be033a55..00000000 --- a/test/integration/targets/incidental_postgresql_user/tasks/test_password.yml +++ /dev/null @@ -1,336 +0,0 @@ -- vars: - task_parameters: &task_parameters - become_user: "{{ pg_user }}" - become: yes - register: result - postgresql_parameters: ¶meters - db: postgres - name: "{{ db_user1 }}" - login_user: "{{ pg_user }}" - - block: - - name: 'Check that PGOPTIONS environment variable is effective (1/2)' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '{{ db_password1 }}' - ignore_errors: true - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - name: 'Check that PGOPTIONS environment variable is effective (2/2)' - assert: - that: - - "{{ result is failed }}" - - - name: 'Create a user (password encrypted: {{ encrypted }})' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '{{ db_password1 }}' - encrypted: '{{ encrypted }}' - environment: - PGCLIENTENCODING: 'UTF8' - - - block: &changed # block is only used here in order to be able to define YAML anchor - - name: Check that ansible reports it was created - assert: - that: - - "{{ result is changed }}" - - - name: Check that it was created - <<: *task_parameters - shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres - - - assert: - that: - - "result.stdout_lines[-1] == '(1 row)'" - - - name: Check that creating user a second time does nothing - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '{{ db_password1 }}' - encrypted: '{{ encrypted }}' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - block: ¬_changed # block is only used here in order to be able to define YAML anchor - - name: Check that ansible reports no change - assert: - that: - - "{{ result is not changed }}" - - - name: 'Define an expiration time' - <<: *task_parameters - postgresql_user: - <<: *parameters - expires: '2025-01-01' - environment: - PGCLIENTENCODING: 'UTF8' - - - <<: *changed - - - name: 'Redefine the same expiration time' - <<: *task_parameters - postgresql_user: - expires: '2025-01-01' - <<: *parameters - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - block: - - - name: 'Using MD5-hashed password: check that password not changed when using cleartext password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '{{ db_password1 }}' - encrypted: 'yes' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: "Using MD5-hashed password: check that password not changed when using md5 hash with 'ENCRYPTED'" - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "md5{{ (db_password1 ~ db_user1) | hash('md5')}}" - encrypted: 'yes' - environment: - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: "Using MD5-hashed password: check that password not changed when using md5 hash with 'UNENCRYPTED'" - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "md5{{ (db_password1 ~ db_user1) | hash('md5')}}" - encrypted: 'no' - environment: - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Redefine the same expiration time and password (encrypted)' - <<: *task_parameters - postgresql_user: - <<: *parameters - encrypted: 'yes' - password: "md5{{ (db_password1 ~ db_user1) | hash('md5')}}" - expires: '2025-01-01' - environment: - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Using MD5-hashed password: check that password changed when using another cleartext password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: 'prefix{{ db_password1 }}' - encrypted: 'yes' - environment: - PGCLIENTENCODING: 'UTF8' - - - <<: *changed - - - name: "Using MD5-hashed password: check that password changed when using another md5 hash with 'ENCRYPTED'" - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "md5{{ ('prefix1' ~ db_password1 ~ db_user1) | hash('md5')}}" - encrypted: 'yes' - - - <<: *changed - - - name: "Using MD5-hashed password: check that password changed when using md5 hash with 'UNENCRYPTED'" - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "md5{{ ('prefix2' ~ db_password1 ~ db_user1) | hash('md5')}}" - encrypted: 'no' - register: change_pass_unencrypted - failed_when: - - change_pass_unencrypted is failed - # newer version of psycopg2 no longer supported unencrypted password, we ignore the error - - '"UNENCRYPTED PASSWORD is no longer supported" not in change_pass_unencrypted.msg' - - - <<: *changed - - - name: 'Using MD5-hashed password: check that password changed when clearing the password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '' - encrypted: 'yes' - environment: - PGCLIENTENCODING: 'UTF8' - - - <<: *changed - - - name: 'Using MD5-hashed password: check that password not changed when clearing the password again' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '' - encrypted: 'yes' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Using cleartext password: check that password not changed when clearing the password again' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '' - encrypted: 'no' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Using MD5-hashed password: check that password changed when using a cleartext password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '{{ db_password1 }}' - encrypted: 'yes' - environment: - PGCLIENTENCODING: 'UTF8' - - - <<: *changed - - when: encrypted == 'yes' - - - block: - - - name: 'Using cleartext password: check that password not changed when using cleartext password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "{{ db_password1 }}" - encrypted: 'no' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Redefine the same expiration time and password (not encrypted)' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "{{ db_password1 }}" - encrypted: 'no' - expires: '2025-01-01' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Using cleartext password: check that password changed when using another cleartext password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "changed{{ db_password1 }}" - encrypted: 'no' - environment: - PGCLIENTENCODING: 'UTF8' - - - <<: *changed - - - name: 'Using cleartext password: check that password changed when clearing the password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '' - encrypted: 'no' - environment: - PGCLIENTENCODING: 'UTF8' - - - <<: *changed - - - name: 'Using cleartext password: check that password not changed when clearing the password again' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '' - encrypted: 'no' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Using MD5-hashed password: check that password not changed when clearing the password again' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: '' - encrypted: 'yes' - environment: - PGCLIENTENCODING: 'UTF8' - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - - name: 'Using cleartext password: check that password changed when using cleartext password' - <<: *task_parameters - postgresql_user: - <<: *parameters - password: "{{ db_password1 }}" - encrypted: 'no' - environment: - PGCLIENTENCODING: 'UTF8' - - - <<: *changed - - when: encrypted == 'no' - - - name: Remove user - <<: *task_parameters - postgresql_user: - state: 'absent' - <<: *parameters - - - <<: *changed - - - name: Check that they were removed - <<: *task_parameters - shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql -d postgres - environment: - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - assert: - that: - - "result.stdout_lines[-1] == '(0 rows)'" - - - name: Check that removing user a second time does nothing - <<: *task_parameters - postgresql_user: - state: 'absent' - <<: *parameters - environment: - PGOPTIONS: '-c default_transaction_read_only=on' # ensure 'alter user' query isn't executed - - - <<: *not_changed - - always: - - name: Remove user - <<: *task_parameters - postgresql_user: - state: 'absent' - <<: *parameters diff --git a/test/integration/targets/incidental_setup_ec2/aliases b/test/integration/targets/incidental_setup_ec2/aliases deleted file mode 100644 index 136c05e0..00000000 --- a/test/integration/targets/incidental_setup_ec2/aliases +++ /dev/null @@ -1 +0,0 @@ -hidden diff --git a/test/integration/targets/incidental_setup_ec2/defaults/main.yml b/test/integration/targets/incidental_setup_ec2/defaults/main.yml deleted file mode 100644 index fb1f88b1..00000000 --- a/test/integration/targets/incidental_setup_ec2/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -resource_prefix: 'ansible-testing-' diff --git a/test/integration/targets/incidental_setup_ec2/tasks/common.yml b/test/integration/targets/incidental_setup_ec2/tasks/common.yml deleted file mode 100644 index bf23f539..00000000 --- a/test/integration/targets/incidental_setup_ec2/tasks/common.yml +++ /dev/null @@ -1,119 +0,0 @@ ---- - -# ============================================================ -- name: test with no parameters - action: "{{module_name}}" - register: result - ignore_errors: true - -- name: assert failure when called with no parameters - assert: - that: - - 'result.failed' - - 'result.msg == "missing required arguments: name"' - -# ============================================================ -- name: test with only name - action: "{{module_name}} name={{ec2_key_name}}" - register: result - ignore_errors: true - -- name: assert failure when called with only 'name' - assert: - that: - - 'result.failed' - - 'result.msg == "Either region or ec2_url must be specified"' - -# ============================================================ -- name: test invalid region parameter - action: "{{module_name}} name='{{ec2_key_name}}' region='asdf querty 1234'" - register: result - ignore_errors: true - -- name: assert invalid region parameter - assert: - that: - - 'result.failed' - - 'result.msg.startswith("value of region must be one of:")' - -# ============================================================ -- name: test valid region parameter - action: "{{module_name}} name='{{ec2_key_name}}' region='{{ec2_region}}'" - register: result - ignore_errors: true - -- name: assert valid region parameter - assert: - that: - - 'result.failed' - - 'result.msg.startswith("No handler was ready to authenticate.")' - -# ============================================================ -- name: test environment variable EC2_REGION - action: "{{module_name}} name='{{ec2_key_name}}'" - environment: - EC2_REGION: '{{ec2_region}}' - register: result - ignore_errors: true - -- name: assert environment variable EC2_REGION - assert: - that: - - 'result.failed' - - 'result.msg.startswith("No handler was ready to authenticate.")' - -# ============================================================ -- name: test invalid ec2_url parameter - action: "{{module_name}} name='{{ec2_key_name}}'" - environment: - EC2_URL: bogus.example.com - register: result - ignore_errors: true - -- name: assert invalid ec2_url parameter - assert: - that: - - 'result.failed' - - 'result.msg.startswith("No handler was ready to authenticate.")' - -# ============================================================ -- name: test valid ec2_url parameter - action: "{{module_name}} name='{{ec2_key_name}}'" - environment: - EC2_URL: '{{ec2_url}}' - register: result - ignore_errors: true - -- name: assert valid ec2_url parameter - assert: - that: - - 'result.failed' - - 'result.msg.startswith("No handler was ready to authenticate.")' - -# ============================================================ -- name: test credentials from environment - action: "{{module_name}} name='{{ec2_key_name}}'" - environment: - EC2_REGION: '{{ec2_region}}' - EC2_ACCESS_KEY: bogus_access_key - EC2_SECRET_KEY: bogus_secret_key - register: result - ignore_errors: true - -- name: assert ec2_key with valid ec2_url - assert: - that: - - 'result.failed' - - '"EC2ResponseError: 401 Unauthorized" in result.msg' - -# ============================================================ -- name: test credential parameters - action: "{{module_name}} name='{{ec2_key_name}}' ec2_region='{{ec2_region}}' ec2_access_key=bogus_access_key ec2_secret_key=bogus_secret_key" - register: result - ignore_errors: true - -- name: assert credential parameters - assert: - that: - - 'result.failed' - - '"EC2ResponseError: 401 Unauthorized" in result.msg' diff --git a/test/integration/targets/incidental_setup_ec2/vars/main.yml b/test/integration/targets/incidental_setup_ec2/vars/main.yml deleted file mode 100644 index 3d7209ef..00000000 --- a/test/integration/targets/incidental_setup_ec2/vars/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -ec2_url: ec2.amazonaws.com -ec2_region: us-east-1 diff --git a/test/integration/targets/incidental_setup_flatpak_remote/README.md b/test/integration/targets/incidental_setup_flatpak_remote/README.md deleted file mode 100644 index d7916c14..00000000 --- a/test/integration/targets/incidental_setup_flatpak_remote/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# Create a dummy flatpak repository remote - -This document describes how to create a local flatpak dummy repo. Just like the one contained in the `files/repo.tar.gxz` archive. - - -## Create a hello world app - -Prerequisites: - - - flathub - -Prepare the environment: - -``` -flatpak install --system flathub org.freedesktop.Platform//1.6 org.freedesktop.Sdk//1.6 -``` - -Create a hello world executable: - -``` -echo $'#!/bin/sh\necho hello world' > hello.sh -``` - -To create dummy flatpaks, run this (defining a unique NUM for every flatpak to add): - -``` -export NUM=1 -flatpak build-init appdir$NUM com.dummy.App$NUM org.freedesktop.Sdk org.freedesktop.Platform 1.6; -flatpak build appdir$NUM mkdir /app/bin; -flatpak build appdir$NUM install --mode=750 hello.sh /app/bin; -flatpak build-finish --command=hello.sh appdir$NUM -``` - -## Create a repo and/or add the app to it - -Create a repo and add the file to it in one command: - -``` -flatpak build-export repo appdir$NUM stable -``` - -## Create flatpak*-files - -Put a flatpakref file under the repo folder (`repo/com.dummy.App1.flatpakref`): - -``` -[Flatpak Ref] -Title=Dummy App$NUM -Name=com.dummy.App$NUM -Branch=stable -Url=file:///tmp/flatpak/repo -GPGKey={{ base64-encoded public KEY }} -IsRuntime=false -RuntimeRepo=https://flathub.org/repo/flathub.flatpakrepo -``` - -Add a `.flatpakrepo` file to the `repo` folder (`repo/dummy-repo.flatpakrepo`): - -``` -[Flatpak Repo] -Title=Dummy Repo -Url=file:///tmp/flatpak/repo -Comment=Dummy repo for ansible module integration testing -Description=Dummy repo for ansible module integration testing -GPGKey={{ base64-encoded public KEY }} -``` - -## Sign the repo - -Create a new key in a new gpg home folder (On RedHat systems, the executable needs to addressed as gpg2): - -``` -mkdir gpg -gpg --homedir gpg --quick-gen-key test@dummy.com -``` - -Sign the repo and summary file, you need to redo this when you update the repository: - -``` -flatpak build-sign repo --gpg-sign=KEY_ID --gpg-homedir=gpg -flatpak build-update-repo repo --gpg-sign=KEY_ID --gpg-homedir=gpg -``` - -Export the public key as a file: - -``` -gpg --homedir=gpg --export KEY_ID > dummy-repo.gpg -``` - -Create base64-encoded string from gpg-file for `GPGKey=` property in flatpak*-files: - -``` -base64 dummy-repo.gpg | tr -d '\n' -``` - -## How to use the repo - -Now you can add the `repo` folder as a local repo: - -``` -flatpak --system remote-add --gpg-import=/tmp/flatpak/repo/dummy-repo.gpg dummy-repo /tmp/flatpak/repo -``` - -Or, via `.flatpakrepo` file: - -``` -flatpak --system remote-add dummy-repo /tmp/flatpak/repo/dummy-repo.flatpakrepo -``` - -And install the hello world flatpaks like this: - -``` -flatpak --system install dummy-repo com.dummy.App$NUM -``` - -Or from flatpakref: - -``` -flatpak --system install --from /tmp/flatpak/repo/com.dummy.App$NUM.flatpakref -``` - -Run the app: - -``` -flatpak run com.dummy.App$NUM -``` - -To install an app without any runtime dependencies (the app will be broken, but it is enough to test flatpak installation): - -``` -flatpak --system install --no-deps dummy-repo com.dummy.App$NUM -``` - -## Sources: - -* https://blogs.gnome.org/alexl/2017/02/10/maintaining-a-flatpak-repository/ - -* http://docs.flatpak.org/en/latest/first-build.html diff --git a/test/integration/targets/incidental_setup_flatpak_remote/aliases b/test/integration/targets/incidental_setup_flatpak_remote/aliases deleted file mode 100644 index 136c05e0..00000000 --- a/test/integration/targets/incidental_setup_flatpak_remote/aliases +++ /dev/null @@ -1 +0,0 @@ -hidden diff --git a/test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xz b/test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xz Binary files differdeleted file mode 100644 index 544bf706..00000000 --- a/test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xz +++ /dev/null diff --git a/test/integration/targets/incidental_setup_flatpak_remote/handlers/main.yaml b/test/integration/targets/incidental_setup_flatpak_remote/handlers/main.yaml deleted file mode 100644 index 9380dee9..00000000 --- a/test/integration/targets/incidental_setup_flatpak_remote/handlers/main.yaml +++ /dev/null @@ -1,4 +0,0 @@ -- name: remove temporary flatpak link - file: - state: absent - path: /tmp/flatpak diff --git a/test/integration/targets/incidental_setup_flatpak_remote/meta/main.yaml b/test/integration/targets/incidental_setup_flatpak_remote/meta/main.yaml deleted file mode 100644 index 75ee4583..00000000 --- a/test/integration/targets/incidental_setup_flatpak_remote/meta/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - setup_remote_tmp_dir diff --git a/test/integration/targets/incidental_setup_flatpak_remote/tasks/main.yaml b/test/integration/targets/incidental_setup_flatpak_remote/tasks/main.yaml deleted file mode 100644 index c199d216..00000000 --- a/test/integration/targets/incidental_setup_flatpak_remote/tasks/main.yaml +++ /dev/null @@ -1,22 +0,0 @@ -- name: Set up dummy flatpak repository remote - block: - - - name: Copy repo into place - unarchive: - src: repo.tar.xz - dest: "{{ remote_tmp_dir }}" - owner: root - group: root - mode: 0644 - - - name: Create deterministic link to temp directory - file: - state: link - src: "{{ remote_tmp_dir }}/" - path: "/tmp/flatpak" - owner: root - group: root - mode: 0644 - notify: remove temporary flatpak link - - become: true diff --git a/test/integration/targets/incidental_setup_postgresql_db/aliases b/test/integration/targets/incidental_setup_postgresql_db/aliases deleted file mode 100644 index 136c05e0..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/aliases +++ /dev/null @@ -1 +0,0 @@ -hidden diff --git a/test/integration/targets/incidental_setup_postgresql_db/defaults/main.yml b/test/integration/targets/incidental_setup_postgresql_db/defaults/main.yml deleted file mode 100644 index aea02442..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/defaults/main.yml +++ /dev/null @@ -1,17 +0,0 @@ -postgresql_service: postgresql - -postgresql_packages: - - postgresql-server - - python-psycopg2 - -pg_user: postgres -pg_group: root - -locale_latin_suffix: -locale_utf8_suffix: - -# defaults for test SSL -ssl_db: 'ssl_db' -ssl_user: 'ssl_user' -ssl_pass: 'ssl_pass' -ssl_rootcert: '~{{ pg_user }}/root.crt' diff --git a/test/integration/targets/incidental_setup_postgresql_db/files/dummy--1.0.sql b/test/integration/targets/incidental_setup_postgresql_db/files/dummy--1.0.sql deleted file mode 100644 index 53c79666..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/files/dummy--1.0.sql +++ /dev/null @@ -1,2 +0,0 @@ -CREATE OR REPLACE FUNCTION dummy_display_ext_version() -RETURNS text LANGUAGE SQL AS 'SELECT (''1.0'')::text'; diff --git a/test/integration/targets/incidental_setup_postgresql_db/files/dummy--2.0.sql b/test/integration/targets/incidental_setup_postgresql_db/files/dummy--2.0.sql deleted file mode 100644 index 227ba1b4..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/files/dummy--2.0.sql +++ /dev/null @@ -1,2 +0,0 @@ -CREATE OR REPLACE FUNCTION dummy_display_ext_version() -RETURNS text LANGUAGE SQL AS 'SELECT (''2.0'')::text'; diff --git a/test/integration/targets/incidental_setup_postgresql_db/files/dummy--3.0.sql b/test/integration/targets/incidental_setup_postgresql_db/files/dummy--3.0.sql deleted file mode 100644 index 7d6a60e5..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/files/dummy--3.0.sql +++ /dev/null @@ -1,2 +0,0 @@ -CREATE OR REPLACE FUNCTION dummy_display_ext_version() -RETURNS text LANGUAGE SQL AS 'SELECT (''3.0'')::text'; diff --git a/test/integration/targets/incidental_setup_postgresql_db/files/dummy.control b/test/integration/targets/incidental_setup_postgresql_db/files/dummy.control deleted file mode 100644 index 4f8553c2..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/files/dummy.control +++ /dev/null @@ -1,3 +0,0 @@ -comment = 'dummy extension used to test postgresql_ext Ansible module' -default_version = '3.0' -relocatable = true diff --git a/test/integration/targets/incidental_setup_postgresql_db/files/pg_hba.conf b/test/integration/targets/incidental_setup_postgresql_db/files/pg_hba.conf deleted file mode 100644 index 58de3607..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/files/pg_hba.conf +++ /dev/null @@ -1,10 +0,0 @@ -# !!! This file managed by Ansible. Any local changes may be overwritten. !!! - -# Database administrative login by UNIX sockets -# note: you may wish to restrict this further later -local all {{ pg_user }} trust - -# TYPE DATABASE USER CIDR-ADDRESS METHOD -local all all md5 -host all all 127.0.0.1/32 md5 -host all all ::1/128 md5 diff --git a/test/integration/targets/incidental_setup_postgresql_db/tasks/main.yml b/test/integration/targets/incidental_setup_postgresql_db/tasks/main.yml deleted file mode 100644 index 2e969c31..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/tasks/main.yml +++ /dev/null @@ -1,222 +0,0 @@ -- name: python 2 - set_fact: - python_suffix: "" - when: ansible_python_version is version('3', '<') - -- name: python 3 - set_fact: - python_suffix: "-py3" - when: ansible_python_version is version('3', '>=') - -- name: Include distribution and Python version specific variables - include_vars: "{{ lookup('first_found', params) }}" - vars: - params: - files: - - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}{{ python_suffix }}.yml' - - '{{ ansible_distribution }}-{{ ansible_distribution_version }}{{ python_suffix }}.yml' - - '{{ ansible_os_family }}{{ python_suffix }}.yml' - - 'default{{ python_suffix }}.yml' - paths: - - "{{ role_path }}/vars" - -- name: make sure the dbus service is started under systemd - systemd: - name: dbus - state: started - when: ansible_service_mgr == 'systemd' and ansible_distribution == 'Fedora' - -# Make sure we start fresh -- name: stop postgresql service - service: name={{ postgresql_service }} state=stopped - ignore_errors: True - -- name: remove old db (RedHat or Suse) - file: - path: "{{ pg_dir }}" - state: absent - ignore_errors: True - when: ansible_os_family == "RedHat" or ansible_os_family == "Suse" - -- name: remove old db (FreeBSD) - file: - path: "{{ pg_dir }}" - state: absent - ignore_errors: True - when: ansible_os_family == "FreeBSD" - -# Theoretically, pg_dropcluster should work but it doesn't so remove files -- name: remove old db config and files (debian) - file: - path: '{{ loop_item }}' - state: absent - ignore_errors: True - when: ansible_os_family == "Debian" - loop: - - /etc/postgresql - - /var/lib/postgresql - loop_control: - loop_var: loop_item - -- name: install dependencies for postgresql test - package: - name: "{{ postgresql_package_item }}" - state: present - with_items: "{{ postgresql_packages }}" - loop_control: - loop_var: postgresql_package_item - -- name: initialize postgres (FreeBSD) - command: /usr/local/etc/rc.d/postgresql oneinitdb - when: ansible_os_family == "FreeBSD" - -- name: Initialize postgres (RedHat systemd) - command: postgresql-setup initdb - when: ansible_os_family == "RedHat" and ansible_service_mgr == "systemd" - -- name: Initialize postgres (RedHat sysv) - command: /sbin/service postgresql initdb - when: ansible_os_family == "RedHat" and ansible_service_mgr != "systemd" - -- name: Initialize postgres (Debian) - shell: '. /usr/share/postgresql-common/maintscripts-functions && set_system_locale && /usr/bin/pg_createcluster -u postgres {{ pg_ver }} main' - args: - creates: "/etc/postgresql/{{ pg_ver }}/" - when: ansible_os_family == 'Debian' - -- name: Initialize postgres (Suse) - service: name=postgresql state=restarted - when: ansible_os_family == 'Suse' - -- name: Copy pg_hba into place - template: - src: files/pg_hba.conf - dest: "{{ pg_hba_location }}" - owner: "{{ pg_user }}" - group: "{{ pg_group }}" - mode: "0644" - -- name: Generate locales (Debian) - locale_gen: - name: '{{ item }}' - state: present - with_items: - - pt_BR - - es_ES - when: ansible_os_family == 'Debian' - -# Suse: locales are installed by default (glibc-locale package). -# Fedora 23: locales are installed by default (glibc-common package) -# CentOS: all locales are installed by default (glibc-common package) but some -# RPM macros could prevent their installation (for example when using anaconda -# instLangs parameter). - -- block: - - name: Install langpacks (RHEL8) - yum: - name: - - glibc-langpack-es - - glibc-langpack-pt - - glibc-all-langpacks - state: present - when: ansible_distribution_major_version is version('8', '>=') - - - name: Check if locales need to be generated (RedHat) - shell: "localedef --list-archive | grep -a -q '^{{ locale }}$'" - register: locale_present - ignore_errors: True - with_items: - - es_ES - - pt_BR - loop_control: - loop_var: locale - - - name: Reinstall internationalization files - shell: 'yum -y reinstall glibc-common || yum -y install glibc-common' - args: - warn: no - when: locale_present is failed - - - name: Generate locale (RedHat) - command: 'localedef -f ISO-8859-1 -i {{ item.locale }} {{ item.locale }}' - when: item is failed - with_items: '{{ locale_present.results }}' - when: ansible_os_family == 'RedHat' and ansible_distribution != 'Fedora' - -- name: Install glibc langpacks (Fedora >= 24) - package: - name: '{{ item }}' - state: 'latest' - with_items: - - glibc-langpack-es - - glibc-langpack-pt - when: ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('24', '>=') - -- name: enable postgresql service (FreeBSD) - lineinfile: - path: /etc/rc.conf - line: 'postgresql_enable="YES"' - when: ansible_os_family == "FreeBSD" - -- name: stop postgresql service - service: - name: "{{ postgresql_service }}" - state: stopped - -- name: pause between stop and start of postgresql service - pause: - seconds: 3 - -- name: start postgresql service - service: - name: "{{ postgresql_service }}" - state: started - -######################## -# Setup dummy extension: -- name: copy control file for dummy ext - copy: - src: dummy.control - dest: "/usr/share/postgresql/{{ pg_ver }}/extension/dummy.control" - mode: 0444 - when: ansible_os_family == 'Debian' - -- name: copy version files for dummy ext - copy: - src: "{{ item }}" - dest: "/usr/share/postgresql/{{ pg_ver }}/extension/{{ item }}" - mode: 0444 - with_items: - - dummy--1.0.sql - - dummy--2.0.sql - - dummy--3.0.sql - when: ansible_os_family == 'Debian' - -- name: add update paths - file: - path: "/usr/share/postgresql/{{ pg_ver }}/extension/{{ item }}" - mode: 0444 - state: touch - with_items: - - dummy--1.0--2.0.sql - - dummy--2.0--3.0.sql - when: ansible_os_family == 'Debian' - -- name: Get PostgreSQL version - become_user: "{{ pg_user }}" - become: yes - shell: "echo 'SHOW SERVER_VERSION' | psql --tuples-only --no-align --dbname postgres" - register: postgres_version_resp - -- name: Print PostgreSQL server version - debug: - msg: "{{ postgres_version_resp.stdout }}" - -# SSL configuration. -# Restricted using Debian family because of there are errors on other distributions -# that not related with PostgreSQL or psycopg2 SSL support. -# The tests key point is to be sure that ssl options work in general -- import_tasks: ssl.yml - when: - - ansible_os_family == 'Debian' - - postgres_version_resp.stdout is version('9.4', '>=') diff --git a/test/integration/targets/incidental_setup_postgresql_db/tasks/ssl.yml b/test/integration/targets/incidental_setup_postgresql_db/tasks/ssl.yml deleted file mode 100644 index bc45ec6f..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/tasks/ssl.yml +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# The aim of this test is to be sure that SSL options work in general -# and preparing the environment for testing these options in -# the following PostgreSQL modules (ssl_db, ssl_user, certs). -# Configured by https://www.postgresql.org/docs/current/ssl-tcp.html - -#################### -# Prepare for tests: - -- name: postgresql SSL - create database - become_user: "{{ pg_user }}" - become: yes - postgresql_db: - name: "{{ ssl_db }}" - -- name: postgresql SSL - create role - become_user: "{{ pg_user }}" - become: yes - postgresql_user: - name: "{{ ssl_user }}" - role_attr_flags: SUPERUSER - password: "{{ ssl_pass }}" - -- name: postgresql SSL - install openssl - become: yes - package: name=openssl state=present - -- name: postgresql SSL - create certs 1 - become_user: root - become: yes - shell: 'openssl req -new -nodes -text -out ~{{ pg_user }}/root.csr \ - -keyout ~{{ pg_user }}/root.key -subj "/CN=localhost.local"' - -- name: postgresql SSL - create certs 2 - become_user: root - become: yes - shell: 'openssl x509 -req -in ~{{ pg_user }}/root.csr -text -days 3650 \ - -extensions v3_ca -signkey ~{{ pg_user }}/root.key -out ~{{ pg_user }}/root.crt' - -- name: postgresql SSL - create certs 3 - become_user: root - become: yes - shell: 'openssl req -new -nodes -text -out ~{{ pg_user }}/server.csr \ - -keyout ~{{ pg_user }}/server.key -subj "/CN=localhost.local"' - -- name: postgresql SSL - create certs 4 - become_user: root - become: yes - shell: 'openssl x509 -req -in ~{{ pg_user }}/server.csr -text -days 365 \ - -CA ~{{ pg_user }}/root.crt -CAkey ~{{ pg_user }}/root.key -CAcreateserial -out server.crt' - -- name: postgresql SSL - set right permissions to files - become_user: root - become: yes - file: - path: '{{ item }}' - mode: 0600 - owner: '{{ pg_user }}' - group: '{{ pg_user }}' - with_items: - - '~{{ pg_user }}/root.key' - - '~{{ pg_user }}/server.key' - - '~{{ pg_user }}/root.crt' - - '~{{ pg_user }}/server.csr' - -- name: postgresql SSL - enable SSL - become_user: "{{ pg_user }}" - become: yes - postgresql_set: - login_user: "{{ pg_user }}" - db: postgres - name: ssl - value: on - -- name: postgresql SSL - reload PostgreSQL to enable ssl on - become: yes - service: - name: "{{ postgresql_service }}" - state: reloaded diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Debian-8.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Debian-8.yml deleted file mode 100644 index c5c6795e..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/Debian-8.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql" - - "postgresql-common" - - "python-psycopg2" - -pg_hba_location: "/etc/postgresql/9.4/main/pg_hba.conf" -pg_dir: "/var/lib/postgresql/9.4/main" -pg_ver: 9.4 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-11-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-11-py3.yml deleted file mode 100644 index 2f6b0d98..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-11-py3.yml +++ /dev/null @@ -1,12 +0,0 @@ -postgresql_packages: - - postgresql95-server - - py36-psycopg2 - -pg_dir: /usr/local/pgsql/data -pg_hba_location: "{{ pg_dir }}/pg_hba.conf" -pg_ver: 9.5 -pg_user: pgsql -pg_group: pgsql - -locale_latin_suffix: .ISO8859-1 -locale_utf8_suffix: .UTF-8 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-11.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-11.yml deleted file mode 100644 index efb0603b..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-11.yml +++ /dev/null @@ -1,12 +0,0 @@ -postgresql_packages: - - postgresql95-server - - py27-psycopg2 - -pg_dir: /usr/local/pgsql/data -pg_hba_location: "{{ pg_dir }}/pg_hba.conf" -pg_ver: 9.5 -pg_user: pgsql -pg_group: pgsql - -locale_latin_suffix: .ISO8859-1 -locale_utf8_suffix: .UTF-8 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.0-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.0-py3.yml deleted file mode 100644 index 2f6b0d98..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.0-py3.yml +++ /dev/null @@ -1,12 +0,0 @@ -postgresql_packages: - - postgresql95-server - - py36-psycopg2 - -pg_dir: /usr/local/pgsql/data -pg_hba_location: "{{ pg_dir }}/pg_hba.conf" -pg_ver: 9.5 -pg_user: pgsql -pg_group: pgsql - -locale_latin_suffix: .ISO8859-1 -locale_utf8_suffix: .UTF-8 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.0.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.0.yml deleted file mode 100644 index 1fe66782..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.0.yml +++ /dev/null @@ -1,12 +0,0 @@ -postgresql_packages: - - postgresql96-server - - py27-psycopg2 - -pg_dir: /usr/local/pgsql/data -pg_hba_location: "{{ pg_dir }}/pg_hba.conf" -pg_ver: 9.6 -pg_user: pgsql -pg_group: pgsql - -locale_latin_suffix: .ISO8859-1 -locale_utf8_suffix: .UTF-8 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.1-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.1-py3.yml deleted file mode 100644 index cd7c83a4..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.1-py3.yml +++ /dev/null @@ -1,12 +0,0 @@ -postgresql_packages: - - postgresql11-server - - py36-psycopg2 - -pg_dir: /var/db/postgres/data11 -pg_hba_location: "{{ pg_dir }}/pg_hba.conf" -pg_ver: 11 -pg_user: postgres -pg_group: postgres - -locale_latin_suffix: .ISO8859-1 -locale_utf8_suffix: .UTF-8 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.1.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.1.yml deleted file mode 100644 index 0b1ab5b2..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/FreeBSD-12.1.yml +++ /dev/null @@ -1,12 +0,0 @@ -postgresql_packages: - - postgresql11-server - - py27-psycopg2 - -pg_dir: /var/db/postgres/data11 -pg_hba_location: "{{ pg_dir }}/pg_hba.conf" -pg_ver: 11 -pg_user: postgres -pg_group: postgres - -locale_latin_suffix: .ISO8859-1 -locale_utf8_suffix: .UTF-8 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/RedHat-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/RedHat-py3.yml deleted file mode 100644 index ee083722..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/RedHat-py3.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql-server" - - "python3-psycopg2" - - "bzip2" - - "xz" - -pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" -pg_dir: "/var/lib/pgsql/data" diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/RedHat.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/RedHat.yml deleted file mode 100644 index 20c4b1f5..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/RedHat.yml +++ /dev/null @@ -1,7 +0,0 @@ -postgresql_packages: - - "postgresql-server" - - "python-psycopg2" - - "bzip2" - -pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" -pg_dir: "/var/lib/pgsql/data" diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-12.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-12.yml deleted file mode 100644 index 4b6e744b..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-12.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql" - - "postgresql-common" - - "python-psycopg2" - -pg_hba_location: "/etc/postgresql/9.1/main/pg_hba.conf" -pg_dir: "/var/lib/postgresql/9.1/main" -pg_ver: 9.1 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-14.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-14.yml deleted file mode 100644 index ffcc8dd4..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-14.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql" - - "postgresql-common" - - "python-psycopg2" - -pg_hba_location: "/etc/postgresql/9.3/main/pg_hba.conf" -pg_dir: "/var/lib/postgresql/9.3/main" -pg_ver: 9.3 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-16-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-16-py3.yml deleted file mode 100644 index b088c310..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-16-py3.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql" - - "postgresql-common" - - "python3-psycopg2" - -pg_hba_location: "/etc/postgresql/9.5/main/pg_hba.conf" -pg_dir: "/var/lib/postgresql/9.5/main" -pg_ver: 9.5 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-16.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-16.yml deleted file mode 100644 index 897efd2c..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-16.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql" - - "postgresql-common" - - "python-psycopg2" - -pg_hba_location: "/etc/postgresql/9.5/main/pg_hba.conf" -pg_dir: "/var/lib/postgresql/9.5/main" -pg_ver: 9.5 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-18-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-18-py3.yml deleted file mode 100644 index 10453bdf..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-18-py3.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql" - - "postgresql-common" - - "python3-psycopg2" - -pg_hba_location: "/etc/postgresql/10/main/pg_hba.conf" -pg_dir: "/var/lib/postgresql/10/main" -pg_ver: 10 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-20-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-20-py3.yml deleted file mode 100644 index 7322bcb2..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-20-py3.yml +++ /dev/null @@ -1,8 +0,0 @@ -postgresql_packages: - - "postgresql" - - "postgresql-common" - - "python3-psycopg2" - -pg_hba_location: "/etc/postgresql/12/main/pg_hba.conf" -pg_dir: "/var/lib/postgresql/12/main" -pg_ver: 12 diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/default-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/default-py3.yml deleted file mode 100644 index 19152a64..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/default-py3.yml +++ /dev/null @@ -1,6 +0,0 @@ -postgresql_packages: - - "postgresql-server" - - "python3-psycopg2" - -pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" -pg_dir: "/var/lib/pgsql/data" diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/default.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/default.yml deleted file mode 100644 index ab36dd9f..00000000 --- a/test/integration/targets/incidental_setup_postgresql_db/vars/default.yml +++ /dev/null @@ -1,6 +0,0 @@ -postgresql_packages: - - "postgresql-server" - - "python-psycopg2" - -pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" -pg_dir: "/var/lib/pgsql/data" diff --git a/test/integration/targets/incidental_setup_rabbitmq/aliases b/test/integration/targets/incidental_setup_rabbitmq/aliases deleted file mode 100644 index 136c05e0..00000000 --- a/test/integration/targets/incidental_setup_rabbitmq/aliases +++ /dev/null @@ -1 +0,0 @@ -hidden diff --git a/test/integration/targets/incidental_setup_rabbitmq/files/rabbitmq.conf b/test/integration/targets/incidental_setup_rabbitmq/files/rabbitmq.conf deleted file mode 100644 index 1e602175..00000000 --- a/test/integration/targets/incidental_setup_rabbitmq/files/rabbitmq.conf +++ /dev/null @@ -1,8 +0,0 @@ -listeners.ssl.default = 5671 - -ssl_options.cacertfile = /tls/ca_certificate.pem -ssl_options.certfile = /tls/server_certificate.pem -ssl_options.keyfile = /tls/server_key.pem -ssl_options.password = bunnies -ssl_options.verify = verify_peer -ssl_options.fail_if_no_peer_cert = false diff --git a/test/integration/targets/incidental_setup_rabbitmq/meta/main.yml b/test/integration/targets/incidental_setup_rabbitmq/meta/main.yml deleted file mode 100644 index 7a6c3e01..00000000 --- a/test/integration/targets/incidental_setup_rabbitmq/meta/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -dependencies: - - incidental_setup_tls diff --git a/test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml b/test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml deleted file mode 100644 index ad401fb3..00000000 --- a/test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- include: ubuntu.yml - when: - - ansible_distribution == 'Ubuntu' - - ansible_distribution_release != 'focal' diff --git a/test/integration/targets/incidental_setup_rabbitmq/tasks/ubuntu.yml b/test/integration/targets/incidental_setup_rabbitmq/tasks/ubuntu.yml deleted file mode 100644 index 6d7a3ef0..00000000 --- a/test/integration/targets/incidental_setup_rabbitmq/tasks/ubuntu.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- -# https://www.rabbitmq.com/install-debian.html#apt-pinning -- name: Pin erlang version that rabbitmq supports - copy: - dest: /etc/apt/preferences.d/erlang - content: | - Package: erlang* - Pin: version 1:20.3.8.18-1 - Pin-Priority: 1000 - - Package: esl-erlang - Pin: version 1:20.3.6 - Pin-Priority: 1000 - -- name: Install https transport for apt - apt: - name: apt-transport-https - state: latest - force: yes - -- name: Add RabbitMQ release signing key - apt_key: - url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/setup_rabbitmq/rabbitmq-release-signing-key.asc - state: present - -- name: Add RabbitMQ Erlang repository - apt_repository: - repo: "deb https://dl.bintray.com/rabbitmq-erlang/debian {{ ansible_distribution_release }} erlang-20.x" - filename: 'rabbitmq-erlang' - state: present - update_cache: yes - -# Required by the rabbitmq modules that uses the management API -- name: Install requests - pip: - name: requests - -- name: Install RabbitMQ Server - apt: - deb: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/setup_rabbitmq/rabbitmq-server_3.7.14-1_all.deb - -- name: Install RabbitMQ TLS dependencies - apt: - name: "{{ item }}" - state: latest - loop: - - erlang-asn1 - - erlang-crypto - - erlang-public-key - - erlang-ssl - -- name: Ensure TLS config - copy: - src: rabbitmq.conf - dest: /etc/rabbitmq/rabbitmq.conf - -- name: Start RabbitMQ service - service: - name: rabbitmq-server - state: started - -- name: Enable management - command: rabbitmq-plugins enable --online rabbitmq_management diff --git a/test/integration/targets/incidental_synchronize/aliases b/test/integration/targets/incidental_synchronize/aliases deleted file mode 100644 index 31c6a8b4..00000000 --- a/test/integration/targets/incidental_synchronize/aliases +++ /dev/null @@ -1 +0,0 @@ -shippable/posix/incidental diff --git a/test/integration/targets/incidental_synchronize/files/bar.txt b/test/integration/targets/incidental_synchronize/files/bar.txt deleted file mode 100644 index 3e96db9b..00000000 --- a/test/integration/targets/incidental_synchronize/files/bar.txt +++ /dev/null @@ -1 +0,0 @@ -templated_var_loaded diff --git a/test/integration/targets/incidental_synchronize/files/foo.txt b/test/integration/targets/incidental_synchronize/files/foo.txt deleted file mode 100644 index 3e96db9b..00000000 --- a/test/integration/targets/incidental_synchronize/files/foo.txt +++ /dev/null @@ -1 +0,0 @@ -templated_var_loaded diff --git a/test/integration/targets/incidental_synchronize/tasks/main.yml b/test/integration/targets/incidental_synchronize/tasks/main.yml deleted file mode 100644 index 80e052a6..00000000 --- a/test/integration/targets/incidental_synchronize/tasks/main.yml +++ /dev/null @@ -1,273 +0,0 @@ -# test code for the synchronize module -# (c) 2014, James Tanner <tanner.jc@gmail.com> - -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. - -- name: install rsync - package: - name: rsync - when: ansible_distribution != "MacOSX" - -- name: cleanup old files - shell: rm -rf {{output_dir}}/* - -- name: create test new files - copy: dest={{output_dir}}/{{item}} mode=0644 content="hello world" - with_items: - - foo.txt - - bar.txt - -- name: synchronize file to new filename - synchronize: src={{output_dir}}/foo.txt dest={{output_dir}}/foo.result - register: sync_result - -- assert: - that: - - "'changed' in sync_result" - - "sync_result.changed == true" - - "'cmd' in sync_result" - - "'rsync' in sync_result.cmd" - - "'msg' in sync_result" - - "sync_result.msg.startswith('>f+')" - - "sync_result.msg.endswith('+ foo.txt\n')" - -- name: test that the file was really copied over - stat: - path: "{{ output_dir }}/foo.result" - register: stat_result - -- assert: - that: - - "stat_result.stat.exists == True" - - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" - -- name: test that the file is not copied a second time - synchronize: src={{output_dir}}/foo.txt dest={{output_dir}}/foo.result - register: sync_result - -- assert: - that: - - "sync_result.changed == False" - -- name: Cleanup - file: - state: absent - path: "{{output_dir}}/{{item}}" - with_items: - - foo.result - - bar.result - -- name: Synchronize using the mode=push param - synchronize: - src: "{{output_dir}}/foo.txt" - dest: "{{output_dir}}/foo.result" - mode: push - register: sync_result - -- assert: - that: - - "'changed' in sync_result" - - "sync_result.changed == true" - - "'cmd' in sync_result" - - "'rsync' in sync_result.cmd" - - "'msg' in sync_result" - - "sync_result.msg.startswith('>f+')" - - "sync_result.msg.endswith('+ foo.txt\n')" - -- name: test that the file was really copied over - stat: - path: "{{ output_dir }}/foo.result" - register: stat_result - -- assert: - that: - - "stat_result.stat.exists == True" - - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" - -- name: test that the file is not copied a second time - synchronize: - src: "{{output_dir}}/foo.txt" - dest: "{{output_dir}}/foo.result" - mode: push - register: sync_result - -- assert: - that: - - "sync_result.changed == False" - -- name: Cleanup - file: - state: absent - path: "{{output_dir}}/{{item}}" - with_items: - - foo.result - - bar.result - -- name: Synchronize using the mode=pull param - synchronize: - src: "{{output_dir}}/foo.txt" - dest: "{{output_dir}}/foo.result" - mode: pull - register: sync_result - -- assert: - that: - - "'changed' in sync_result" - - "sync_result.changed == true" - - "'cmd' in sync_result" - - "'rsync' in sync_result.cmd" - - "'msg' in sync_result" - - "sync_result.msg.startswith('>f+')" - - "sync_result.msg.endswith('+ foo.txt\n')" - -- name: test that the file was really copied over - stat: - path: "{{ output_dir }}/foo.result" - register: stat_result - -- assert: - that: - - "stat_result.stat.exists == True" - - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" - -- name: test that the file is not copied a second time - synchronize: - src: "{{output_dir}}/foo.txt" - dest: "{{output_dir}}/foo.result" - mode: pull - register: sync_result - -- assert: - that: - - "sync_result.changed == False" - -- name: Cleanup - file: - state: absent - path: "{{output_dir}}/{{item}}" - with_items: - - foo.result - - bar.result - -- name: synchronize files using with_items (issue#5965) - synchronize: src={{output_dir}}/{{item}} dest={{output_dir}}/{{item}}.result - with_items: - - foo.txt - - bar.txt - register: sync_result - -- assert: - that: - - "sync_result.changed" - - "sync_result.msg == 'All items completed'" - - "'results' in sync_result" - - "sync_result.results|length == 2" - - "sync_result.results[0].msg.endswith('+ foo.txt\n')" - - "sync_result.results[1].msg.endswith('+ bar.txt\n')" - -- name: Cleanup - file: - state: absent - path: "{{output_dir}}/{{item}}.result" - with_items: - - foo.txt - - bar.txt - -- name: synchronize files using rsync_path (issue#7182) - synchronize: src={{output_dir}}/foo.txt dest={{output_dir}}/foo.rsync_path rsync_path="sudo rsync" - register: sync_result - -- assert: - that: - - "'changed' in sync_result" - - "sync_result.changed == true" - - "'cmd' in sync_result" - - "'rsync' in sync_result.cmd" - - "'rsync_path' in sync_result.cmd" - - "'msg' in sync_result" - - "sync_result.msg.startswith('>f+')" - - "sync_result.msg.endswith('+ foo.txt\n')" - -- name: Cleanup - file: - state: absent - path: "{{output_dir}}/{{item}}" - with_items: - - foo.rsync_path - -- name: add subdirectories for link-dest test - file: - path: "{{output_dir}}/{{item}}/" - state: directory - mode: 0755 - with_items: - - directory_a - - directory_b - -- name: copy foo.txt into the first directory - synchronize: - src: "{{output_dir}}/foo.txt" - dest: "{{output_dir}}/{{item}}/foo.txt" - with_items: - - directory_a - -- name: synchronize files using link_dest - synchronize: - src: "{{output_dir}}/directory_a/foo.txt" - dest: "{{output_dir}}/directory_b/foo.txt" - link_dest: - - "{{output_dir}}/directory_a" - register: sync_result - -- name: get stat information for directory_a - stat: - path: "{{ output_dir }}/directory_a/foo.txt" - register: stat_result_a - -- name: get stat information for directory_b - stat: - path: "{{ output_dir }}/directory_b/foo.txt" - register: stat_result_b - -- assert: - that: - - "'changed' in sync_result" - - "sync_result.changed == true" - - "stat_result_a.stat.inode == stat_result_b.stat.inode" - -- name: synchronize files using link_dest that would be recursive - synchronize: - src: "{{output_dir}}/foo.txt" - dest: "{{output_dir}}/foo.result" - link_dest: - - "{{output_dir}}" - register: sync_result - ignore_errors: yes - -- assert: - that: - - sync_result is not changed - - sync_result is failed - -- name: Cleanup - file: - state: absent - path: "{{output_dir}}/{{item}}" - with_items: - - "directory_b/foo.txt" - - "directory_a/foo.txt" - - "directory_a" - - "directory_b" diff --git a/test/integration/targets/incidental_timezone/aliases b/test/integration/targets/incidental_timezone/aliases deleted file mode 100644 index 834cafc9..00000000 --- a/test/integration/targets/incidental_timezone/aliases +++ /dev/null @@ -1,5 +0,0 @@ -destructive -shippable/posix/incidental -skip/aix -skip/osx -skip/macos diff --git a/test/integration/targets/incidental_timezone/tasks/main.yml b/test/integration/targets/incidental_timezone/tasks/main.yml deleted file mode 100644 index 247ad6cf..00000000 --- a/test/integration/targets/incidental_timezone/tasks/main.yml +++ /dev/null @@ -1,57 +0,0 @@ -# Because hwclock usually isn't available inside Docker containers in Shippable -# these tasks will detect if hwclock works and only run hwclock tests if it is -# supported. That is why it is recommended to run these tests locally with -# `--docker-privileged` on centos6, centos7 and ubuntu1404 images. Example -# command to run on centos6: -# -# ansible-test integration --docker centos6 --docker-privileged -v timezone - -## -## set path to timezone config files -## - -- name: set config file path on Debian - set_fact: - timezone_config_file: '/etc/timezone' - when: ansible_os_family == 'Debian' - -- name: set config file path on RedHat - set_fact: - timezone_config_file: '/etc/sysconfig/clock' - when: ansible_os_family == 'RedHat' - -## -## set path to hwclock config files -## - -- name: set config file path on Debian - set_fact: - hwclock_config_file: '/etc/default/rcS' - when: ansible_os_family == 'Debian' - -- name: set config file path on RedHat - set_fact: - hwclock_config_file: '/etc/sysconfig/clock' - when: ansible_os_family == 'RedHat' - -- name: Run tests - # Skip tests on Fedora because dbus fails to start unless the container is run in priveleged mode. - # Even then, it starts unreliably. This may be due to the move to cgroup v2 in Fedora 31. - # https://www.redhat.com/sysadmin/fedora-31-control-group-v2 - # Just skip Fedora rather than version-limiting because F30 goes EOL within a month of this writing - # and that is the oldest version we currently test in CI. F31+ are affected by the issue - # and making the tests work on them is something to deal with in community.general, not here. - when: ansible_distribution != 'Fedora' - block: - - name: set timezone to Etc/UTC - timezone: - name: Etc/UTC - register: original_timezone - - - block: - - include_tasks: test.yml - always: - - name: Restore original system timezone - {{ original_timezone.diff.before.name }} - timezone: - name: "{{ original_timezone.diff.before.name }}" - when: original_timezone is changed diff --git a/test/integration/targets/incidental_timezone/tasks/test.yml b/test/integration/targets/incidental_timezone/tasks/test.yml deleted file mode 100644 index ec0d854d..00000000 --- a/test/integration/targets/incidental_timezone/tasks/test.yml +++ /dev/null @@ -1,607 +0,0 @@ -## -## test setting timezone, idempotency and checkmode -## - -- name: set timezone to Australia/Brisbane (checkmode) - timezone: - name: Australia/Brisbane - check_mode: yes - register: timezone_set_checkmode - -- name: ensure timezone reported as changed in checkmode - assert: - that: - - timezone_set_checkmode.changed - - timezone_set_checkmode.diff.after.name == 'Australia/Brisbane' - - timezone_set_checkmode.diff.before.name == 'Etc/UTC' - -- name: ensure checkmode didn't change the timezone - command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane - register: result - failed_when: result is not failed - changed_when: no - -- name: ensure that checkmode didn't update the timezone in the config file - command: egrep '^(TIME)?ZONE="Etc/UTC"' {{ timezone_config_file }} - when: - - ansible_service_mgr != 'systemd' - - ansible_os_family == 'RedHat' - -- name: ensure that checkmode didn't update the timezone in the config file - command: egrep '^Etc/UTC' {{ timezone_config_file }} - when: - - ansible_service_mgr != 'systemd' - - ansible_os_family == 'Debian' - -- name: set timezone to Australia/Brisbane - timezone: - name: Australia/Brisbane - register: timezone_set - -- name: ensure timezone changed - assert: - that: - - timezone_set.changed - - timezone_set.diff.after.name == 'Australia/Brisbane' - - timezone_set.diff.before.name == 'Etc/UTC' - -- name: ensure that the timezone is actually set - command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane - changed_when: no - -- name: ensure that the timezone is updated in the config file - command: egrep '^(TIME)?ZONE="Australia/Brisbane"' {{ timezone_config_file }} - when: - - ansible_service_mgr != 'systemd' - - ansible_os_family == 'RedHat' - -- name: ensure that the timezone is updated in the config file - command: egrep '^Australia/Brisbane' {{ timezone_config_file }} - when: - - ansible_service_mgr != 'systemd' - - ansible_os_family == 'Debian' - -- name: set timezone to Australia/Brisbane again - timezone: - name: Australia/Brisbane - register: timezone_again - -- name: ensure timezone idempotency - assert: - that: - - not timezone_again.changed - -- name: set timezone to Australia/Brisbane again in checkmode - timezone: - name: Australia/Brisbane - register: timezone_again_checkmode - -- name: set timezone idempotency (checkmode) - assert: - that: - - not timezone_again_checkmode.changed - -## -## tests for same timezones with different names -## - -- name: check dpkg-reconfigure - shell: type dpkg-reconfigure - register: check_dpkg_reconfigure - ignore_errors: yes - changed_when: no - -- name: check timedatectl - shell: type timedatectl && timedatectl - register: check_timedatectl - ignore_errors: yes - changed_when: no - -- block: - - name: set timezone to Etc/UTC - timezone: - name: Etc/UTC - - - name: change timezone from Etc/UTC to UTC - timezone: - name: UTC - register: timezone_etcutc_to_utc - - - name: check timezone changed from Etc/UTC to UTC - assert: - that: - - timezone_etcutc_to_utc.changed - - timezone_etcutc_to_utc.diff.before.name == 'Etc/UTC' - - timezone_etcutc_to_utc.diff.after.name == 'UTC' - - - name: change timezone from UTC to Etc/UTC - timezone: - name: Etc/UTC - register: timezone_utc_to_etcutc - - - name: check timezone changed from UTC to Etc/UTC - assert: - that: - - timezone_utc_to_etcutc.changed - - timezone_utc_to_etcutc.diff.before.name == 'UTC' - - timezone_utc_to_etcutc.diff.after.name == 'Etc/UTC' - - when: - # FIXME: Due to the bug of the dpkg-reconfigure, those tests failed on non-systemd debian - - check_dpkg_reconfigure.rc != 0 or check_timedatectl.rc == 0 - -## -## no systemd tests for timezone -## - -- block: - ## - ## test with empty config file - ## - - - name: empty config file - command: cp /dev/null {{ timezone_config_file }} - - - name: set timezone to Europe/Belgrade (empty config file) - timezone: - name: Europe/Belgrade - register: timezone_empty_conf - - - name: check if timezone set (empty config file) - assert: - that: - - timezone_empty_conf.changed - - timezone_empty_conf.diff.after.name == 'Europe/Belgrade' - - timezone_empty_conf.diff.before.name == 'n/a' - - - name: check if the timezone is actually set (empty config file) - command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade - changed_when: no - - - ## - ## test with deleted config file - ## - - - name: remove config file - file: - path: '{{ timezone_config_file }}' - state: absent - - - name: set timezone to Europe/Belgrade (no config file) - timezone: - name: Europe/Belgrade - register: timezone_missing_conf - - - name: check if timezone set (no config file) - assert: - that: - - timezone_missing_conf.changed - - timezone_missing_conf.diff.after.name == 'Europe/Belgrade' - - timezone_missing_conf.diff.before.name == 'n/a' - - - name: check if the timezone is actually set (no config file) - command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade - changed_when: no - - - ## - ## test with /etc/localtime as symbolic link to a zoneinfo file - ## - - - name: create symlink /etc/locatime -> /usr/share/zoneinfo/Etc/UTC - file: - src: /usr/share/zoneinfo/Etc/UTC - dest: /etc/localtime - state: link - force: yes - - - name: set timezone to Europe/Belgrade (over symlink) - timezone: - name: Europe/Belgrade - register: timezone_symllink - - - name: check if timezone set (over symlink) - assert: - that: - - timezone_symllink.changed - - timezone_symllink.diff.after.name == 'Europe/Belgrade' - - timezone_symllink.diff.before.name == 'Etc/UTC' - - - name: check if the timezone is actually set (over symlink) - command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade - changed_when: no - - - ## - ## test with /etc/localtime as broken symbolic link - ## - - - name: set timezone to a broken symlink - file: - src: /tmp/foo - dest: /etc/localtime - state: link - force: yes - - - name: set timezone to Europe/Belgrade (over broken symlink) - timezone: - name: Europe/Belgrade - register: timezone_symllink_broken - - - name: check if timezone set (over broken symlink) - assert: - that: - - timezone_symllink_broken.changed - - timezone_symllink_broken.diff.after.name == 'Europe/Belgrade' - - timezone_symllink_broken.diff.before.name == 'n/a' - - - name: check if the timezone is actually set (over broken symlink) - command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade - changed_when: no - - - ## - ## test with /etc/localtime set manually using copy - ## - - - name: set timezone manually by coping zone info file to /etc/localtime - copy: - src: /usr/share/zoneinfo/Etc/UTC - dest: /etc/localtime - remote_src: yes - - - name: set timezone to Europe/Belgrade (over copied file) - timezone: - name: Europe/Belgrade - register: timezone_copied - - - name: check if timezone set (over copied file) - assert: - that: - - timezone_copied.changed - - timezone_copied.diff.after.name == 'Europe/Belgrade' - - timezone_copied.diff.before.name == 'n/a' - - - name: check if the timezone is actually set (over copied file) - command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade - changed_when: no - when: - - ansible_service_mgr != 'systemd' - - timezone_config_file is defined - - -#### -#### hwclock tests -#### - -- name: check if hwclock is supported in the environment - command: hwclock --test - register: hwclock_test - ignore_errors: yes - -- name: check if timedatectl works in the environment - command: timedatectl - register: timedatectl_test - ignore_errors: yes - -- name: - set_fact: - hwclock_supported: '{{ hwclock_test is successful or timedatectl_test is successful }}' -## -## test set hwclock, idempotency and checkmode -## - -- block: - - name: set hwclock to local - timezone: - hwclock: local - - - name: set hwclock to UTC (checkmode) - timezone: - hwclock: UTC - check_mode: yes - register: hwclock_set_checkmode - - - name: ensure hwclock reported as changed (checkmode) - assert: - that: - - hwclock_set_checkmode.changed - - hwclock_set_checkmode.diff.after.hwclock == 'UTC' - - hwclock_set_checkmode.diff.before.hwclock == 'local' - - - block: - - name: ensure that checkmode didn't update hwclock in /etc/adjtime - command: grep ^UTC /etc/adjtime - register: result - failed_when: result is not failed - - - name: ensure that checkmode didn't update hwclock the config file - command: grep ^UTC=no {{ hwclock_config_file }} - when: ansible_service_mgr != 'systemd' - - - name: set hwclock to UTC - timezone: - hwclock: UTC - register: hwclock_set - - - name: ensure hwclock changed - assert: - that: - - hwclock_set.changed - - hwclock_set.diff.after.hwclock == 'UTC' - - hwclock_set.diff.before.hwclock == 'local' - - - block: - - name: ensure that hwclock is updated in /etc/adjtime - command: grep ^UTC /etc/adjtime - - - name: ensure that hwclock is updated in the config file - command: grep ^UTC=yes {{ hwclock_config_file }} - when: ansible_service_mgr != 'systemd' - - - name: set hwclock to RTC again - timezone: - hwclock: UTC - register: hwclock_again - - - name: set hwclock idempotency - assert: - that: - - not hwclock_again.changed - - - name: set hwclock to RTC again (checkmode) - timezone: - hwclock: UTC - check_mode: yes - register: hwclock_again_checkmode - - - name: set hwclock idempotency (checkmode) - assert: - that: - - not hwclock_again_checkmode.changed - - - ## - ## no systemd tests for hwclock - ## - - - block: - ## - ## test set hwclock with both /etc/adjtime and conf file deleted - ## - - - name: remove /etc/adjtime and conf file - file: - path: '{{ item }}' - state: absent - with_items: - - /etc/adjtime - - '{{ hwclock_config_file }}' - - - name: set hwclock to UTC with deleted /etc/adjtime and conf file - timezone: - hwclock: UTC - register: hwclock_set_utc_deleted_adjtime_and_conf - - - name: ensure hwclock changed with deleted /etc/adjtime and conf - assert: - that: - - hwclock_set_utc_deleted_adjtime_and_conf.changed - - hwclock_set_utc_deleted_adjtime_and_conf.diff.after.hwclock == 'UTC' - - hwclock_set_utc_deleted_adjtime_and_conf.diff.before.hwclock == 'n/a' - - - ## - ## test set hwclock with /etc/adjtime deleted - ## - - - name: remove /etc/adjtime - file: - path: '{{ item }}' - state: absent - with_items: - - /etc/adjtime - - - name: set hwclock to UTC with deleted /etc/adjtime - timezone: - hwclock: UTC - register: hwclock_set_utc_deleted_adjtime_utc - - - name: ensure hwclock changed with deleted /etc/adjtime - assert: - that: - - not hwclock_set_utc_deleted_adjtime_utc.changed - - hwclock_set_utc_deleted_adjtime_utc.diff.after.hwclock == 'UTC' - - hwclock_set_utc_deleted_adjtime_utc.diff.before.hwclock == 'UTC' - - - name: set hwclock to LOCAL with deleted /etc/adjtime - timezone: - hwclock: local - register: hwclock_set_local_deleted_adjtime_local - - - name: ensure hwclock changed to LOCAL with deleted /etc/adjtime - assert: - that: - - hwclock_set_local_deleted_adjtime_local.changed - - hwclock_set_local_deleted_adjtime_local.diff.after.hwclock == 'local' - - hwclock_set_local_deleted_adjtime_local.diff.before.hwclock == 'UTC' - - - ## - ## test set hwclock with conf file deleted - ## - - - name: remove conf file - file: - path: '{{ item }}' - state: absent - with_items: - - '{{ hwclock_config_file }}' - - - name: set hwclock to UTC with deleted conf - timezone: - hwclock: UTC - register: hwclock_set_utc_deleted_conf - - - name: ensure hwclock changed with deleted /etc/adjtime - assert: - that: - - hwclock_set_utc_deleted_conf.changed - - hwclock_set_utc_deleted_conf.diff.after.hwclock == 'UTC' - - hwclock_set_utc_deleted_conf.diff.before.hwclock == 'n/a' - - - ## - ## test set hwclock with /etc/adjtime missing UTC/LOCAL strings - ## - - - name: create /etc/adjtime without UTC/LOCAL - copy: - content: '0.0 0 0\n0' - dest: /etc/adjtime - - - name: set hwclock to UTC with broken /etc/adjtime - timezone: - hwclock: UTC - register: hwclock_set_utc_broken_adjtime - - - name: ensure hwclock doesn't report changed with broken /etc/adjtime - assert: - that: - - not hwclock_set_utc_broken_adjtime.changed - - hwclock_set_utc_broken_adjtime.diff.after.hwclock == 'UTC' - - hwclock_set_utc_broken_adjtime.diff.before.hwclock == 'UTC' - - - name: set hwclock to LOCAL with broken /etc/adjtime - timezone: - hwclock: local - register: hwclock_set_local_broken_adjtime - - - name: ensure hwclock changed to LOCAL with broken /etc/adjtime - assert: - that: - - hwclock_set_local_broken_adjtime.changed - - hwclock_set_local_broken_adjtime.diff.after.hwclock == 'local' - - hwclock_set_local_broken_adjtime.diff.before.hwclock == 'UTC' - when: - - ansible_service_mgr != 'systemd' - - hwclock_config_file is defined - - #### - #### timezone + hwclock tests - #### - - ## - ## test set timezone and hwclock, idempotency and checkmode - ## - - - name: set timezone to Etc/UTC and hwclock to local - timezone: - name: Etc/UTC - hwclock: local - - - name: set timezone to Europe/Belgrade and hwclock to UTC (checkmode) - timezone: - name: Europe/Belgrade - hwclock: UTC - check_mode: yes - register: tzclock_set_checkmode - - - name: ensure timezone and hwclock reported as changed in checkmode - assert: - that: - - tzclock_set_checkmode.changed - - tzclock_set_checkmode.diff.after.name == 'Europe/Belgrade' - - tzclock_set_checkmode.diff.before.name == 'Etc/UTC' - - tzclock_set_checkmode.diff.after.hwclock == 'UTC' - - tzclock_set_checkmode.diff.before.hwclock == 'local' - - - name: ensure checkmode didn't change the timezone - command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane - register: result - failed_when: result is not failed - changed_when: no - - - block: - - name: ensure that checkmode didn't update the timezone in the config file - command: egrep '^(TIME)?ZONE="Etc/UTC"' {{ timezone_config_file }} - when: - - ansible_os_family == 'RedHat' - - - name: ensure that checkmode didn't update the timezone in the config file - command: egrep '^Etc/UTC' {{ timezone_config_file }} - when: - - ansible_os_family == 'Debian' - - - name: ensure that checkmode didn't update hwclock in /etc/adjtime - command: grep ^UTC /etc/adjtime - register: result - failed_when: result is not failed - - - name: ensure that checkmode didn't update hwclock the config file - command: grep ^UTC=no {{ hwclock_config_file }} - when: ansible_service_mgr != 'systemd' - - - name: set timezone to Europe/Belgrade and hwclock to UTC - timezone: - name: Europe/Belgrade - hwclock: UTC - register: tzclock_set - - - name: ensure timezone and hwclock changed - assert: - that: - - tzclock_set.changed - - tzclock_set.diff.after.name == 'Europe/Belgrade' - - tzclock_set.diff.before.name == 'Etc/UTC' - - tzclock_set.diff.after.hwclock == 'UTC' - - tzclock_set.diff.before.hwclock == 'local' - - - name: ensure that the timezone is actually set - command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade - changed_when: no - - - block: - - name: ensure that the timezone is updated in the config file - command: egrep '^(TIME)?ZONE="Europe/Belgrade"' {{ timezone_config_file }} - when: - - ansible_os_family == 'RedHat' - - - name: ensure that the timezone is updated in the config file - command: egrep 'Europe/Belgrade' {{ timezone_config_file }} - when: - - ansible_os_family == 'Debian' - - - name: ensure that hwclock is updated in /etc/adjtime - command: grep ^UTC /etc/adjtime - - - name: ensure that hwclock is updated in the config file - command: grep ^UTC=yes {{ hwclock_config_file }} - when: ansible_service_mgr != 'systemd' - - - name: set timezone to Europe/Belgrade and hwclock to UTC again - timezone: - name: Europe/Belgrade - hwclock: UTC - register: tzclock_set_again - - - name: set timezone and hwclock idempotency - assert: - that: - - not tzclock_set_again.changed - - - name: set timezone to Europe/Belgrade and hwclock to UTC again (checkmode) - timezone: - name: Europe/Belgrade - hwclock: UTC - register: tzclock_set_again_checkmode - - - name: set timezone and hwclock idempotency in checkmode - assert: - that: - - not tzclock_set_again_checkmode.changed - - when: - - ansible_system == 'Linux' - - hwclock_supported diff --git a/test/integration/targets/incidental_win_psexec/aliases b/test/integration/targets/incidental_win_psexec/aliases deleted file mode 100644 index a5fc90dc..00000000 --- a/test/integration/targets/incidental_win_psexec/aliases +++ /dev/null @@ -1,2 +0,0 @@ -shippable/windows/incidental -windows diff --git a/test/integration/targets/incidental_win_psexec/tasks/main.yml b/test/integration/targets/incidental_win_psexec/tasks/main.yml deleted file mode 100644 index 27783f9e..00000000 --- a/test/integration/targets/incidental_win_psexec/tasks/main.yml +++ /dev/null @@ -1,80 +0,0 @@ -# Would use [] but this has troubles with PATH and trying to find the executable so just resort to keeping a space -- name: record special path for tests - set_fact: - testing_dir: '{{ remote_tmp_dir }}\ansible win_psexec' - -- name: create special path testing dir - win_file: - path: '{{ testing_dir }}' - state: directory - -- name: Download PsExec - win_get_url: - url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_psexec/PsExec.exe - dest: '{{ testing_dir }}\PsExec.exe' - -- name: Get the existing PATH env var - win_shell: '$env:PATH' - register: system_path - changed_when: False - -- name: Run whoami - win_psexec: - command: whoami.exe - nobanner: true - register: whoami - environment: - PATH: '{{ testing_dir }};{{ system_path.stdout | trim }}' - -- name: Test whoami - assert: - that: - - whoami.rc == 0 - - whoami.stdout == '' - # FIXME: Standard output does not work or is truncated - #- whoami.stdout == '{{ ansible_hostname|lower }}' - -- name: Run whoami as SYSTEM - win_psexec: - command: whoami.exe - system: yes - nobanner: true - executable: '{{ testing_dir }}\PsExec.exe' - register: whoami_as_system - # Seems to be a bug with PsExec where the stdout can be empty, just retry the task to make this test a bit more stable - until: whoami_as_system.rc == 0 and whoami_as_system.stdout == 'nt authority\system' - retries: 3 - delay: 2 - -# FIXME: Behaviour is not consistent on all Windows systems -#- name: Run whoami as ELEVATED -# win_psexec: -# command: whoami.exe -# elevated: yes -# register: whoami_as_elevated -# -## Ensure we have basic facts -#- setup: -# -#- debug: -# msg: '{{ whoami_as_elevated.stdout|lower }} == {{ ansible_hostname|lower }}\{{ ansible_user_id|lower }}' -# -#- name: Test whoami -# assert: -# that: -# - whoami_as_elevated.rc == 0 -# - whoami_as_elevated.stdout|lower == '{{ ansible_hostname|lower }}\{{ ansible_user_id|lower }}' - -- name: Run command with multiple arguments - win_psexec: - command: powershell.exe -NonInteractive "exit 1" - ignore_errors: yes - register: whoami_multiple_args - environment: - PATH: '{{ testing_dir }};{{ system_path.stdout | trim }}' - -- name: Test command with multiple argumetns - assert: - that: - - whoami_multiple_args.rc == 1 - - whoami_multiple_args.psexec_command == "psexec.exe -accepteula powershell.exe -NonInteractive \"exit 1\"" diff --git a/test/integration/targets/incidental_xml/aliases b/test/integration/targets/incidental_xml/aliases deleted file mode 100644 index fc0963c1..00000000 --- a/test/integration/targets/incidental_xml/aliases +++ /dev/null @@ -1,4 +0,0 @@ -destructive -shippable/posix/incidental -skip/aix -skip/power/centos diff --git a/test/integration/targets/incidental_xml/fixtures/ansible-xml-beers-unicode.xml b/test/integration/targets/incidental_xml/fixtures/ansible-xml-beers-unicode.xml deleted file mode 100644 index d0e3e39a..00000000 --- a/test/integration/targets/incidental_xml/fixtures/ansible-xml-beers-unicode.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Толстый бар</name> - <beers> - <beer>Окское</beer> - <beer>Невское</beer> - </beers> - <rating subjective="да">десять</rating> - <website> - <mobilefriendly/> - <address>http://tolstyybar.com</address> - </website> -</business> diff --git a/test/integration/targets/incidental_xml/fixtures/ansible-xml-beers.xml b/test/integration/targets/incidental_xml/fixtures/ansible-xml-beers.xml deleted file mode 100644 index 5afc7974..00000000 --- a/test/integration/targets/incidental_xml/fixtures/ansible-xml-beers.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/fixtures/ansible-xml-namespaced-beers.xml b/test/integration/targets/incidental_xml/fixtures/ansible-xml-namespaced-beers.xml deleted file mode 100644 index 61747d4b..00000000 --- a/test/integration/targets/incidental_xml/fixtures/ansible-xml-namespaced-beers.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar"> - <name>Tasty Beverage Co.</name> - <beers xmlns="http://test.beers"> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating xmlns="http://test.rating" attr:subjective="true">10</rating> - <website xmlns="http://test.website"> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-add-children-elements-unicode.xml b/test/integration/targets/incidental_xml/results/test-add-children-elements-unicode.xml deleted file mode 100644 index 525330c2..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-children-elements-unicode.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer>Окское</beer></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-add-children-elements.xml b/test/integration/targets/incidental_xml/results/test-add-children-elements.xml deleted file mode 100644 index f9ff2517..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-children-elements.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer>Old Rasputin</beer></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-add-children-from-groupvars.xml b/test/integration/targets/incidental_xml/results/test-add-children-from-groupvars.xml deleted file mode 100644 index 565ba402..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-children-from-groupvars.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer>Natty Lite</beer><beer>Miller Lite</beer><beer>Coors Lite</beer></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-add-children-insertafter.xml b/test/integration/targets/incidental_xml/results/test-add-children-insertafter.xml deleted file mode 100644 index 8da96336..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-children-insertafter.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Old Rasputin</beer> - <beer>Old Motor Oil</beer> - <beer>Old Curmudgeon</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business> diff --git a/test/integration/targets/incidental_xml/results/test-add-children-insertbefore.xml b/test/integration/targets/incidental_xml/results/test-add-children-insertbefore.xml deleted file mode 100644 index c409e54b..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-children-insertbefore.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>Old Rasputin</beer> - <beer>Old Motor Oil</beer> - <beer>Old Curmudgeon</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business> diff --git a/test/integration/targets/incidental_xml/results/test-add-children-with-attributes-unicode.xml b/test/integration/targets/incidental_xml/results/test-add-children-with-attributes-unicode.xml deleted file mode 100644 index 37465224..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-children-with-attributes-unicode.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer name="Окское" type="экстра"/></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-add-children-with-attributes.xml b/test/integration/targets/incidental_xml/results/test-add-children-with-attributes.xml deleted file mode 100644 index 5a3907f6..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-children-with-attributes.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer name="Ansible Brew" type="light"/></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-add-element-implicitly.yml b/test/integration/targets/incidental_xml/results/test-add-element-implicitly.yml deleted file mode 100644 index fa1ddfca..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-element-implicitly.yml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer color="red">George Killian's Irish Red</beer> - <beer origin="CZ" color="blonde">Pilsner Urquell</beer> - </beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - <validxhtml validateon=""/> - </website> - <phonenumber>555-555-1234</phonenumber> - <owner dob="1976-04-12"> - <name> - <last>Smith</last> - <first>John</first> - <middle>Q</middle> - </name> - </owner> - <website_bis> - <validxhtml validateon=""/> - </website_bis> - <testnormalelement>xml tag with no special characters</testnormalelement> - <test-with-dash>xml tag with dashes</test-with-dash> - <test-with-dash.and.dot>xml tag with dashes and dots</test-with-dash.and.dot> - <test-with.dash_and.dot_and-underscores>xml tag with dashes, dots and underscores</test-with.dash_and.dot_and-underscores> -</business> diff --git a/test/integration/targets/incidental_xml/results/test-add-namespaced-children-elements.xml b/test/integration/targets/incidental_xml/results/test-add-namespaced-children-elements.xml deleted file mode 100644 index 3d27e8aa..00000000 --- a/test/integration/targets/incidental_xml/results/test-add-namespaced-children-elements.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar"> - <name>Tasty Beverage Co.</name> - <beers xmlns="http://test.beers"> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer>Old Rasputin</beer></beers> - <rating xmlns="http://test.rating" attr:subjective="true">10</rating> - <website xmlns="http://test.website"> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-pretty-print-only.xml b/test/integration/targets/incidental_xml/results/test-pretty-print-only.xml deleted file mode 100644 index f47909ac..00000000 --- a/test/integration/targets/incidental_xml/results/test-pretty-print-only.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business> diff --git a/test/integration/targets/incidental_xml/results/test-pretty-print.xml b/test/integration/targets/incidental_xml/results/test-pretty-print.xml deleted file mode 100644 index b5c38262..00000000 --- a/test/integration/targets/incidental_xml/results/test-pretty-print.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - <beer>Old Rasputin</beer> - </beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business> diff --git a/test/integration/targets/incidental_xml/results/test-remove-attribute.xml b/test/integration/targets/incidental_xml/results/test-remove-attribute.xml deleted file mode 100644 index 8a621cf1..00000000 --- a/test/integration/targets/incidental_xml/results/test-remove-attribute.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating>10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-remove-element.xml b/test/integration/targets/incidental_xml/results/test-remove-element.xml deleted file mode 100644 index 454d905c..00000000 --- a/test/integration/targets/incidental_xml/results/test-remove-element.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-remove-namespaced-attribute.xml b/test/integration/targets/incidental_xml/results/test-remove-namespaced-attribute.xml deleted file mode 100644 index 732a0ed2..00000000 --- a/test/integration/targets/incidental_xml/results/test-remove-namespaced-attribute.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar"> - <name>Tasty Beverage Co.</name> - <beers xmlns="http://test.beers"> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating xmlns="http://test.rating">10</rating> - <website xmlns="http://test.website"> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-remove-namespaced-element.xml b/test/integration/targets/incidental_xml/results/test-remove-namespaced-element.xml deleted file mode 100644 index 16df98e2..00000000 --- a/test/integration/targets/incidental_xml/results/test-remove-namespaced-element.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar"> - <name>Tasty Beverage Co.</name> - <beers xmlns="http://test.beers"> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <website xmlns="http://test.website"> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-attribute-value-unicode.xml b/test/integration/targets/incidental_xml/results/test-set-attribute-value-unicode.xml deleted file mode 100644 index de3bc3f6..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-attribute-value-unicode.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="нет">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-attribute-value.xml b/test/integration/targets/incidental_xml/results/test-set-attribute-value.xml deleted file mode 100644 index 143fe7bf..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-attribute-value.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="false">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-children-elements-level.xml b/test/integration/targets/incidental_xml/results/test-set-children-elements-level.xml deleted file mode 100644 index 0ef2b7e6..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-children-elements-level.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer alcohol="0.5" name="90 Minute IPA"><Water liter="0.2" quantity="200g"/><Starch quantity="10g"/><Hops quantity="50g"/><Yeast quantity="20g"/></beer><beer alcohol="0.3" name="Harvest Pumpkin Ale"><Water liter="0.2" quantity="200g"/><Hops quantity="25g"/><Yeast quantity="20g"/></beer></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-children-elements-unicode.xml b/test/integration/targets/incidental_xml/results/test-set-children-elements-unicode.xml deleted file mode 100644 index f19d5356..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-children-elements-unicode.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Окское</beer><beer>Невское</beer></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-children-elements.xml b/test/integration/targets/incidental_xml/results/test-set-children-elements.xml deleted file mode 100644 index be313a5a..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-children-elements.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>90 Minute IPA</beer><beer>Harvest Pumpkin Ale</beer></beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-element-value-empty.xml b/test/integration/targets/incidental_xml/results/test-set-element-value-empty.xml deleted file mode 100644 index 785beb64..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-element-value-empty.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="true">10</rating> - <website> - <mobilefriendly/> - <address></address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-element-value-unicode.xml b/test/integration/targets/incidental_xml/results/test-set-element-value-unicode.xml deleted file mode 100644 index 734fe6db..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-element-value-unicode.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="true">пять</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -<rating>пять</rating></business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-element-value.xml b/test/integration/targets/incidental_xml/results/test-set-element-value.xml deleted file mode 100644 index fc97ec3b..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-element-value.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business type="bar"> - <name>Tasty Beverage Co.</name> - <beers> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating subjective="true">5</rating> - <website> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -<rating>5</rating></business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-namespaced-attribute-value.xml b/test/integration/targets/incidental_xml/results/test-set-namespaced-attribute-value.xml deleted file mode 100644 index 44abda43..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-namespaced-attribute-value.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar"> - <name>Tasty Beverage Co.</name> - <beers xmlns="http://test.beers"> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating xmlns="http://test.rating" attr:subjective="false">10</rating> - <website xmlns="http://test.website"> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/results/test-set-namespaced-element-value.xml b/test/integration/targets/incidental_xml/results/test-set-namespaced-element-value.xml deleted file mode 100644 index 0cc8a79e..00000000 --- a/test/integration/targets/incidental_xml/results/test-set-namespaced-element-value.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar"> - <name>Tasty Beverage Co.</name> - <beers xmlns="http://test.beers"> - <beer>Rochefort 10</beer> - <beer>St. Bernardus Abbot 12</beer> - <beer>Schlitz</beer> - </beers> - <rating xmlns="http://test.rating" attr:subjective="true">11</rating> - <website xmlns="http://test.website"> - <mobilefriendly/> - <address>http://tastybeverageco.com</address> - </website> -</business>
\ No newline at end of file diff --git a/test/integration/targets/incidental_xml/tasks/main.yml b/test/integration/targets/incidental_xml/tasks/main.yml deleted file mode 100644 index 9b8f2c36..00000000 --- a/test/integration/targets/incidental_xml/tasks/main.yml +++ /dev/null @@ -1,67 +0,0 @@ -- name: Gather facts - setup: - -- name: Install lxml (FreeBSD) - package: - name: '{{ "py27-lxml" if ansible_python.version.major == 2 else "py36-lxml" }}' - state: present - when: ansible_os_family == "FreeBSD" - -# Needed for MacOSX ! -- name: Install lxml - pip: - name: lxml - state: present -# when: ansible_os_family == "Darwin" - -- name: Get lxml version - command: "{{ ansible_python_interpreter }} -c 'from lxml import etree; print(\".\".join(str(v) for v in etree.LXML_VERSION))'" - register: lxml_version - -- name: Set lxml capabilities as variables - set_fact: - # NOTE: Some tests require predictable element attribute order, - # which is only guaranteed starting from lxml v3.0alpha1 - lxml_predictable_attribute_order: '{{ lxml_version.stdout is version("3", ">=") }}' - - # NOTE: The xml module requires at least lxml v2.3.0 - lxml_xpath_attribute_result_attrname: '{{ lxml_version.stdout is version("2.3.0", ">=") }}' - -- name: Only run the tests when lxml v2.3.0+ - when: lxml_xpath_attribute_result_attrname - block: - - - include_tasks: test-add-children-elements.yml - - include_tasks: test-add-children-from-groupvars.yml - - include_tasks: test-add-children-insertafter.yml - - include_tasks: test-add-children-insertbefore.yml - - include_tasks: test-add-children-with-attributes.yml - - include_tasks: test-add-element-implicitly.yml - - include_tasks: test-count.yml - - include_tasks: test-mutually-exclusive-attributes.yml - - include_tasks: test-remove-attribute.yml - - include_tasks: test-remove-element.yml - - include_tasks: test-set-attribute-value.yml - - include_tasks: test-set-children-elements.yml - - include_tasks: test-set-children-elements-level.yml - - include_tasks: test-set-element-value.yml - - include_tasks: test-set-element-value-empty.yml - - include_tasks: test-pretty-print.yml - - include_tasks: test-pretty-print-only.yml - - include_tasks: test-add-namespaced-children-elements.yml - - include_tasks: test-remove-namespaced-attribute.yml - - include_tasks: test-set-namespaced-attribute-value.yml - - include_tasks: test-set-namespaced-element-value.yml - - include_tasks: test-set-namespaced-children-elements.yml - - include_tasks: test-get-element-content.yml - - include_tasks: test-xmlstring.yml - - include_tasks: test-children-elements-xml.yml - - # Unicode tests - - include_tasks: test-add-children-elements-unicode.yml - - include_tasks: test-add-children-with-attributes-unicode.yml - - include_tasks: test-set-attribute-value-unicode.yml - - include_tasks: test-count-unicode.yml - - include_tasks: test-get-element-content.yml - - include_tasks: test-set-children-elements-unicode.yml - - include_tasks: test-set-element-value-unicode.yml diff --git a/test/integration/targets/incidental_xml/tasks/test-add-children-elements-unicode.yml b/test/integration/targets/incidental_xml/tasks/test-add-children-elements-unicode.yml deleted file mode 100644 index 8ad91501..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-children-elements-unicode.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - add_children: - - beer: Окское - register: add_children_elements_unicode - - - name: Compare to expected result - copy: - src: results/test-add-children-elements-unicode.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - add_children_elements_unicode.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-add-children-elements-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-add-children-elements.yml b/test/integration/targets/incidental_xml/tasks/test-add-children-elements.yml deleted file mode 100644 index 8d9b0686..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-children-elements.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - add_children: - - beer: Old Rasputin - register: add_children_elements - - - name: Compare to expected result - copy: - src: results/test-add-children-elements.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - add_children_elements.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-add-children-from-groupvars.yml b/test/integration/targets/incidental_xml/tasks/test-add-children-from-groupvars.yml deleted file mode 100644 index e062de8d..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-children-from-groupvars.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - add_children: '{{ bad_beers }}' - register: add_children_from_groupvars - - - name: Compare to expected result - copy: - src: results/test-add-children-from-groupvars.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - add_children_from_groupvars.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-add-children-from-groupvars.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-add-children-insertafter.yml b/test/integration/targets/incidental_xml/tasks/test-add-children-insertafter.yml deleted file mode 100644 index 2d42e2d5..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-children-insertafter.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]' - insertafter: yes - add_children: - - beer: Old Rasputin - - beer: Old Motor Oil - - beer: Old Curmudgeon - pretty_print: yes - register: add_children_insertafter - - - name: Compare to expected result - copy: - src: results/test-add-children-insertafter.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - add_children_insertafter.changed == true - - comparison.changed == false # identical diff --git a/test/integration/targets/incidental_xml/tasks/test-add-children-insertbefore.yml b/test/integration/targets/incidental_xml/tasks/test-add-children-insertbefore.yml deleted file mode 100644 index 8550f12c..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-children-insertbefore.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]' - insertbefore: yes - add_children: - - beer: Old Rasputin - - beer: Old Motor Oil - - beer: Old Curmudgeon - pretty_print: yes - register: add_children_insertbefore - - - name: Compare to expected result - copy: - src: results/test-add-children-insertbefore.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - add_children_insertbefore.changed == true - - comparison.changed == false # identical diff --git a/test/integration/targets/incidental_xml/tasks/test-add-children-with-attributes-unicode.yml b/test/integration/targets/incidental_xml/tasks/test-add-children-with-attributes-unicode.yml deleted file mode 100644 index d4a2329f..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-children-with-attributes-unicode.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - add_children: - - beer: - name: Окское - type: экстра - register: add_children_with_attributes_unicode - - - name: Compare to expected result - copy: - src: results/test-add-children-with-attributes-unicode.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - add_children_with_attributes_unicode.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-add-children-with-attributes-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-add-children-with-attributes.yml b/test/integration/targets/incidental_xml/tasks/test-add-children-with-attributes.yml deleted file mode 100644 index 91e92637..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-children-with-attributes.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - add_children: - - beer: - name: Ansible Brew - type: light - register: add_children_with_attributes - - - name: Compare to expected result - copy: - src: results/test-add-children-with-attributes.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - # NOTE: This test may fail if lxml does not support predictable element attribute order - # So we filter the failure out for these platforms (e.g. CentOS 6) - # The module still works fine, we simply are not comparing as smart as we should. - - name: Test expected result - assert: - that: - - add_children_with_attributes.changed == true - - comparison.changed == false # identical - when: lxml_predictable_attribute_order - #command: diff -u {{ role_path }}/results/test-add-children-with-attributes.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-add-element-implicitly.yml b/test/integration/targets/incidental_xml/tasks/test-add-element-implicitly.yml deleted file mode 100644 index db674ba4..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-element-implicitly.yml +++ /dev/null @@ -1,237 +0,0 @@ ---- -- name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers-implicit.xml - - -- name: Add a phonenumber element to the business element. Implicit mkdir -p behavior where applicable - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/phonenumber - value: 555-555-1234 - -- name: Add a owner element to the business element, testing implicit mkdir -p behavior 1/2 - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/owner/name/last - value: Smith - -- name: Add a owner element to the business element, testing implicit mkdir -p behavior 2/2 - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/owner/name/first - value: John - -- name: Add a validxhtml element to the website element. Note that ensure is present by default and while value defaults to null for elements, if one doesn't specify it we don't know what to do. - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/website/validxhtml - -- name: Add an empty validateon attribute to the validxhtml element. This actually makes the previous example redundant because of the implicit parent-node creation behavior. - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/website/validxhtml/@validateon - -- name: Add an empty validateon attribute to the validxhtml element. Actually verifies the implicit parent-node creation behavior. - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/website_bis/validxhtml/@validateon - -- name: Add an attribute with a value - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/owner/@dob='1976-04-12' - -- name: Add an element with a value, alternate syntax - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/beers/beer/text()="George Killian's Irish Red" # note the quote within an XPath string thing - -- name: Add an element without special characters - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/testnormalelement - value: xml tag with no special characters - pretty_print: yes - -- name: Add an element with dash - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/test-with-dash - value: xml tag with dashes - pretty_print: yes - -- name: Add an element with dot - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/test-with-dash.and.dot - value: xml tag with dashes and dots - pretty_print: yes - -- name: Add an element with underscore - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/test-with.dash_and.dot_and-underscores - value: xml tag with dashes, dots and underscores - pretty_print: yes - -- name: Add an attribute on a conditional element - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/beers/beer[text()="George Killian's Irish Red"]/@color='red' - -- name: Add two attributes on a conditional element - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/beers/beer[text()="Pilsner Urquell" and @origin='CZ']/@color='blonde' - -- name: Add a owner element to the business element, testing implicit mkdir -p behavior 3/2 -- complex lookup - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/owner/name[first/text()='John']/middle - value: Q - -- name: Pretty Print this! - xml: - file: /tmp/ansible-xml-beers-implicit.xml - pretty_print: yes - -- name: Compare to expected result - copy: - src: results/test-add-element-implicitly.yml - dest: /tmp/ansible-xml-beers-implicit.xml - check_mode: yes - diff: yes - register: comparison - -- name: Test expected result - assert: - that: - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-add-element-implicitly.yml /tmp/ansible-xml-beers-implicit.xml - - -# Now we repeat the same, just to ensure proper use of namespaces -- name: Add a phonenumber element to the business element. Implicit mkdir -p behavior where applicable - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:phonenumber - value: 555-555-1234 - namespaces: - a: http://example.com/some/namespace - -- name: Add a owner element to the business element, testing implicit mkdir -p behavior 1/2 - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:owner/a:name/a:last - value: Smith - namespaces: - a: http://example.com/some/namespace - -- name: Add a owner element to the business element, testing implicit mkdir -p behavior 2/2 - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:owner/a:name/a:first - value: John - namespaces: - a: http://example.com/some/namespace - -- name: Add a validxhtml element to the website element. Note that ensure is present by default and while value defaults to null for elements, if one doesn't specify it we don't know what to do. - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:website/a:validxhtml - namespaces: - a: http://example.com/some/namespace - -- name: Add an empty validateon attribute to the validxhtml element. This actually makes the previous example redundant because of the implicit parent-node creation behavior. - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:website/a:validxhtml/@a:validateon - namespaces: - a: http://example.com/some/namespace - -- name: Add an empty validateon attribute to the validxhtml element. Actually verifies the implicit parent-node creation behavior. - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:website_bis/a:validxhtml/@a:validateon - namespaces: - a: http://example.com/some/namespace - -- name: Add an attribute with a value - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:owner/@a:dob='1976-04-12' - namespaces: - a: http://example.com/some/namespace - -- name: Add an element with a value, alternate syntax - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:beers/a:beer/text()="George Killian's Irish Red" # note the quote within an XPath string thing - namespaces: - a: http://example.com/some/namespace - -- name: Add an attribute on a conditional element - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:beers/a:beer[text()="George Killian's Irish Red"]/@a:color='red' - namespaces: - a: http://example.com/some/namespace - -- name: Add two attributes on a conditional element - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:beers/a:beer[text()="Pilsner Urquell" and @a:origin='CZ']/@a:color='blonde' - namespaces: - a: http://example.com/some/namespace - -- name: Add a owner element to the business element, testing implicit mkdir -p behavior 3/2 -- complex lookup - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/a:owner/a:name[a:first/text()='John']/a:middle - value: Q - namespaces: - a: http://example.com/some/namespace - -- name: Add an element without special characters - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/testnormalelement - value: xml tag with no special characters - pretty_print: yes - namespaces: - a: http://example.com/some/namespace - - -- name: Add an element with dash - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/test-with-dash - value: xml tag with dashes - pretty_print: yes - namespaces: - a: http://example.com/some/namespace - -- name: Add an element with dot - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/test-with-dash.and.dot - value: xml tag with dashes and dots - pretty_print: yes - namespaces: - a: http://example.com/some/namespace - -- name: Add an element with underscore - xml: - file: /tmp/ansible-xml-beers-implicit.xml - xpath: /business/test-with.dash_and.dot_and-underscores - value: xml tag with dashes, dots and underscores - pretty_print: yes - namespaces: - a: http://example.com/some/namespace - -- name: Pretty Print this! - xml: - file: /tmp/ansible-xml-beers-implicit.xml - pretty_print: yes diff --git a/test/integration/targets/incidental_xml/tasks/test-add-namespaced-children-elements.yml b/test/integration/targets/incidental_xml/tasks/test-add-namespaced-children-elements.yml deleted file mode 100644 index 25eca47f..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-add-namespaced-children-elements.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - - - - name: Add namespaced child element - xml: - path: /tmp/ansible-xml-namespaced-beers.xml - xpath: /bus:business/ber:beers - namespaces: - bus: http://test.business - ber: http://test.beers - add_children: - - beer: Old Rasputin - register: add_namespaced_children_elements - - - name: Compare to expected result - copy: - src: results/test-add-namespaced-children-elements.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - add_namespaced_children_elements.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-add-namespaced-children-elements.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-children-elements-xml.yml b/test/integration/targets/incidental_xml/tasks/test-children-elements-xml.yml deleted file mode 100644 index e63100c4..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-children-elements-xml.yml +++ /dev/null @@ -1,30 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element with xml format - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - input_type: xml - add_children: - - '<beer>Old Rasputin</beer>' - register: children_elements - - - name: Compare to expected result - copy: - src: results/test-add-children-elements.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - children_elements.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-count-unicode.yml b/test/integration/targets/incidental_xml/tasks/test-count-unicode.yml deleted file mode 100644 index 47a806bf..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-count-unicode.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers-unicode.xml - dest: /tmp/ansible-xml-beers-unicode.xml - - - - name: Count child element - xml: - path: /tmp/ansible-xml-beers-unicode.xml - xpath: /business/beers/beer - count: yes - register: beers - - - name: Test expected result - assert: - that: - - beers.changed == false - - beers.count == 2 diff --git a/test/integration/targets/incidental_xml/tasks/test-count.yml b/test/integration/targets/incidental_xml/tasks/test-count.yml deleted file mode 100644 index cbc97e32..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-count.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add child element - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers/beer - count: yes - register: beers - - - name: Test expected result - assert: - that: - - beers.changed == false - - beers.count == 3 diff --git a/test/integration/targets/incidental_xml/tasks/test-get-element-content-unicode.yml b/test/integration/targets/incidental_xml/tasks/test-get-element-content-unicode.yml deleted file mode 100644 index 73ae9667..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-get-element-content-unicode.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers-unicode.xml - dest: /tmp/ansible-xml-beers-unicode.xml - - - - name: Get element attributes - xml: - path: /tmp/ansible-xml-beers-unicode.xml - xpath: /business/rating - content: attribute - register: get_element_attribute - - - name: Test expected result - assert: - that: - - get_element_attribute.changed == false - - get_element_attribute.matches[0]['rating'] is defined and get_element_attribute.matches[0]['rating']['subjective'] == 'да' - - - name: Get element text - xml: - path: /tmp/ansible-xml-beers-unicode.xml - xpath: /business/rating - content: text - register: get_element_text - - - name: Test expected result - assert: - that: - - get_element_text.changed == false - - get_element_text.matches[0]['rating'] == 'десять' diff --git a/test/integration/targets/incidental_xml/tasks/test-get-element-content.yml b/test/integration/targets/incidental_xml/tasks/test-get-element-content.yml deleted file mode 100644 index 58ca7767..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-get-element-content.yml +++ /dev/null @@ -1,52 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Get element attributes - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - content: attribute - register: get_element_attribute - - - name: Test expected result - assert: - that: - - get_element_attribute.changed == false - - get_element_attribute.matches[0]['rating'] is defined - - get_element_attribute.matches[0]['rating']['subjective'] == 'true' - - # TODO: Remove this in Ansible v2.12 when this incorrect use of attribute is deprecated - - name: Get element attributes - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - content: attribute - attribute: subjective - register: get_element_attribute_wrong - - - name: Test expected result - assert: - that: - - get_element_attribute_wrong.changed == false - - get_element_attribute_wrong.matches[0]['rating'] is defined - - get_element_attribute_wrong.matches[0]['rating']['subjective'] == 'true' - - get_element_attribute_wrong.deprecations is defined - - get_element_attribute_wrong.deprecations[0].msg == "Parameter 'attribute=subjective' is ignored when using 'content=attribute' only 'xpath' is used. Please remove entry." - - get_element_attribute_wrong.deprecations[0].version == '2.12' - - - name: Get element text - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - content: text - register: get_element_text - - - name: Test expected result - assert: - that: - - get_element_text.changed == false - - get_element_text.matches[0]['rating'] == '10' diff --git a/test/integration/targets/incidental_xml/tasks/test-mutually-exclusive-attributes.yml b/test/integration/targets/incidental_xml/tasks/test-mutually-exclusive-attributes.yml deleted file mode 100644 index 3f24b0ac..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-mutually-exclusive-attributes.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Specify both children to add and a value - xml: - path: /tmp/ansible-xml-beers.xml - add_children: - - child01 - - child02 - value: conflict! - register: module_output - ignore_errors: yes - - - name: Test expected result - assert: - that: - - module_output.changed == false - - module_output.failed == true diff --git a/test/integration/targets/incidental_xml/tasks/test-pretty-print-only.yml b/test/integration/targets/incidental_xml/tasks/test-pretty-print-only.yml deleted file mode 100644 index 7c0f7d5f..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-pretty-print-only.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml.orig - - - name: Remove spaces from test fixture - shell: sed 's/^[ ]*//g' < /tmp/ansible-xml-beers.xml.orig > /tmp/ansible-xml-beers.xml - - - name: Pretty print without modification - xml: - path: /tmp/ansible-xml-beers.xml - pretty_print: yes - register: pretty_print_only - - - name: Compare to expected result - copy: - src: results/test-pretty-print-only.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - pretty_print_only.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-pretty-print.yml b/test/integration/targets/incidental_xml/tasks/test-pretty-print.yml deleted file mode 100644 index 88b618b2..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-pretty-print.yml +++ /dev/null @@ -1,30 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Pretty print - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - pretty_print: yes - add_children: - - beer: Old Rasputin - register: pretty_print - - - name: Compare to expected result - copy: - src: results/test-pretty-print.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - pretty_print.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-remove-attribute.yml b/test/integration/targets/incidental_xml/tasks/test-remove-attribute.yml deleted file mode 100644 index 9aa395e6..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-remove-attribute.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Remove '/business/rating/@subjective' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating/@subjective - state: absent - register: remove_attribute - - - name: Compare to expected result - copy: - src: results/test-remove-attribute.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - remove_attribute.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-remove-attribute.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-remove-element.yml b/test/integration/targets/incidental_xml/tasks/test-remove-element.yml deleted file mode 100644 index f2e20ea2..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-remove-element.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Remove '/business/rating' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - state: absent - register: remove_element - - - name: Compare to expected result - copy: - src: results/test-remove-element.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - remove_element.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-remove-namespaced-attribute.yml b/test/integration/targets/incidental_xml/tasks/test-remove-namespaced-attribute.yml deleted file mode 100644 index 36682b22..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-remove-namespaced-attribute.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - - - - name: Remove namespaced '/bus:business/rat:rating/@attr:subjective' - xml: - path: /tmp/ansible-xml-namespaced-beers.xml - xpath: /bus:business/rat:rating/@attr:subjective - namespaces: - bus: http://test.business - ber: http://test.beers - rat: http://test.rating - attr: http://test.attribute - state: absent - register: remove_namespaced_attribute - - - name: Compare to expected result - copy: - src: results/test-remove-namespaced-attribute.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - remove_element.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-remove-namespaced-attribute.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-remove-namespaced-element.yml b/test/integration/targets/incidental_xml/tasks/test-remove-namespaced-element.yml deleted file mode 100644 index be78af68..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-remove-namespaced-element.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - - - - name: Remove namespaced '/bus:business/rat:rating' - xml: - path: /tmp/ansible-xml-namespaced-beers.xml - xpath: /bus:business/rat:rating - namespaces: - bus: http://test.business - ber: http://test.beers - rat: http://test.rating - attr: http://test.attribute - state: absent - register: remove_namespaced_element - - - name: Compare to expected result - copy: - src: results/test-remove-element.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - remove_namespaced_element.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-attribute-value-unicode.yml b/test/integration/targets/incidental_xml/tasks/test-set-attribute-value-unicode.yml deleted file mode 100644 index dabf72a1..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-attribute-value-unicode.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Set '/business/rating/@subjective' to 'нет' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - attribute: subjective - value: нет - register: set_attribute_value_unicode - - - name: Compare to expected result - copy: - src: results/test-set-attribute-value-unicode.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_attribute_value_unicode.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-attribute-value-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-attribute-value.yml b/test/integration/targets/incidental_xml/tasks/test-set-attribute-value.yml deleted file mode 100644 index 2aa39fe2..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-attribute-value.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Set '/business/rating/@subjective' to 'false' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - attribute: subjective - value: 'false' - register: set_attribute_value - - - name: Compare to expected result - copy: - src: results/test-set-attribute-value.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_attribute_value.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-attribute-value.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-children-elements-level.yml b/test/integration/targets/incidental_xml/tasks/test-set-children-elements-level.yml deleted file mode 100644 index 3e2c0adb..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-children-elements-level.yml +++ /dev/null @@ -1,74 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Set child elements - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - set_children: &children - - beer: - alcohol: "0.5" - name: 90 Minute IPA - _: - - Water: - liter: "0.2" - quantity: 200g - - Starch: - quantity: 10g - - Hops: - quantity: 50g - - Yeast: - quantity: 20g - - beer: - alcohol: "0.3" - name: Harvest Pumpkin Ale - _: - - Water: - liter: "0.2" - quantity: 200g - - Hops: - quantity: 25g - - Yeast: - quantity: 20g - register: set_children_elements_level - - - name: Compare to expected result - copy: - src: results/test-set-children-elements-level.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_children_elements_level.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-children-elements-level.xml /tmp/ansible-xml-beers.xml - - - - name: Set child elements (again) - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - set_children: *children - register: set_children_again - - - name: Compare to expected result - copy: - src: results/test-set-children-elements-level.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_children_again.changed == false - - comparison.changed == false # identical diff --git a/test/integration/targets/incidental_xml/tasks/test-set-children-elements-unicode.yml b/test/integration/targets/incidental_xml/tasks/test-set-children-elements-unicode.yml deleted file mode 100644 index 240b894a..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-children-elements-unicode.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Set child elements - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - set_children: &children - - beer: Окское - - beer: Невское - register: set_children_elements_unicode - - - name: Compare to expected result - copy: - src: results/test-set-children-elements-unicode.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_children_elements_unicode.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml - - - - name: Compare to expected result - copy: - src: results/test-set-children-elements-unicode.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_children_again.changed == false - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-children-elements.yml b/test/integration/targets/incidental_xml/tasks/test-set-children-elements.yml deleted file mode 100644 index 7b0f3247..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-children-elements.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Set child elements - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - set_children: &children - - beer: 90 Minute IPA - - beer: Harvest Pumpkin Ale - register: set_children_elements - - - name: Compare to expected result - copy: - src: results/test-set-children-elements.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_children_elements.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml - - - - name: Set child elements (again) - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/beers - set_children: *children - register: set_children_again - - - name: Compare to expected result - copy: - src: results/test-set-children-elements.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_children_again.changed == false - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-element-value-empty.yml b/test/integration/targets/incidental_xml/tasks/test-set-element-value-empty.yml deleted file mode 100644 index 5814803c..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-element-value-empty.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Set '/business/website/address' to empty string. - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/website/address - value: '' - register: set_element_value_empty - - - name: Compare to expected result - copy: - src: results/test-set-element-value-empty.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_element_value_empty.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-element-value-empty.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-element-value-unicode.yml b/test/integration/targets/incidental_xml/tasks/test-set-element-value-unicode.yml deleted file mode 100644 index c3a40b7d..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-element-value-unicode.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add 2nd '/business/rating' with value 'пять' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business - add_children: - - rating: пять - - - name: Set '/business/rating' to 'пять' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - value: пять - register: set_element_first_run - - - name: Set '/business/rating' to 'false'... again - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - value: пять - register: set_element_second_run - - - name: Compare to expected result - copy: - src: results/test-set-element-value-unicode.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_element_first_run.changed == true - - set_element_second_run.changed == false - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-element-value-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-element-value.yml b/test/integration/targets/incidental_xml/tasks/test-set-element-value.yml deleted file mode 100644 index dbd070f1..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-element-value.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-beers.xml - dest: /tmp/ansible-xml-beers.xml - - - - name: Add 2nd '/business/rating' with value '5' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business - add_children: - - rating: '5' - - - name: Set '/business/rating' to '5' - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - value: '5' - register: set_element_first_run - - - name: Set '/business/rating' to '5'... again - xml: - path: /tmp/ansible-xml-beers.xml - xpath: /business/rating - value: '5' - register: set_element_second_run - - - name: Compare to expected result - copy: - src: results/test-set-element-value.xml - dest: /tmp/ansible-xml-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_element_first_run.changed == true - - set_element_second_run.changed == false - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-element-value.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-namespaced-attribute-value.yml b/test/integration/targets/incidental_xml/tasks/test-set-namespaced-attribute-value.yml deleted file mode 100644 index e0086efe..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-namespaced-attribute-value.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - - - - name: Set namespaced '/bus:business/rat:rating/@attr:subjective' to 'false' - xml: - path: /tmp/ansible-xml-namespaced-beers.xml - xpath: /bus:business/rat:rating - namespaces: - bus: http://test.business - ber: http://test.beers - rat: http://test.rating - attr: http://test.attribute - attribute: attr:subjective - value: 'false' - register: set_namespaced_attribute_value - - - name: Compare to expected result - copy: - src: results/test-set-namespaced-attribute-value.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - set_namespaced_attribute_value.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-set-namespaced-attribute-value.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/test/integration/targets/incidental_xml/tasks/test-set-namespaced-children-elements.yml b/test/integration/targets/incidental_xml/tasks/test-set-namespaced-children-elements.yml deleted file mode 100644 index 8e66e70e..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-namespaced-children-elements.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers-xml.xml - - - name: Set child elements - xml: - path: /tmp/ansible-xml-namespaced-beers-xml.xml - xpath: /bus:business/ber:beers - namespaces: - bus: http://test.business - ber: http://test.beers - set_children: - - beer: 90 Minute IPA - - beer: Harvest Pumpkin Ale - - - name: Copy state after first set_children - copy: - src: /tmp/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers-1.xml - remote_src: yes - - - name: Set child elements again - xml: - path: /tmp/ansible-xml-namespaced-beers-xml.xml - xpath: /bus:business/ber:beers - namespaces: - bus: http://test.business - ber: http://test.beers - set_children: - - beer: 90 Minute IPA - - beer: Harvest Pumpkin Ale - register: set_children_again - - - name: Copy state after second set_children - copy: - src: /tmp/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers-2.xml - remote_src: yes - - - name: Compare to expected result - copy: - src: /tmp/ansible-xml-namespaced-beers-1.xml - dest: /tmp/ansible-xml-namespaced-beers-2.xml - remote_src: yes - check_mode: yes - diff: yes - register: comparison - #command: diff /tmp/ansible-xml-namespaced-beers-1.xml /tmp/ansible-xml-namespaced-beers-2.xml - - - name: Test expected result - assert: - that: - - set_children_again.changed == false # idempotency - - set_namespaced_attribute_value.changed == true - - comparison.changed == false # identical diff --git a/test/integration/targets/incidental_xml/tasks/test-set-namespaced-element-value.yml b/test/integration/targets/incidental_xml/tasks/test-set-namespaced-element-value.yml deleted file mode 100644 index f77d7537..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-set-namespaced-element-value.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- - - name: Setup test fixture - copy: - src: fixtures/ansible-xml-namespaced-beers.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - - - - name: Set namespaced '/bus:business/rat:rating' to '11' - xml: - path: /tmp/ansible-xml-namespaced-beers.xml - namespaces: - bus: http://test.business - ber: http://test.beers - rat: http://test.rating - attr: http://test.attribute - xpath: /bus:business/rat:rating - value: '11' - register: set_element_first_run - - - name: Set namespaced '/bus:business/rat:rating' to '11' again - xml: - path: /tmp/ansible-xml-namespaced-beers.xml - namespaces: - bus: http://test.business - ber: http://test.beers - rat: http://test.rating - attr: http://test.attribute - xpath: /bus:business/rat:rating - value: '11' - register: set_element_second_run - - - name: Compare to expected result - copy: - src: results/test-set-namespaced-element-value.xml - dest: /tmp/ansible-xml-namespaced-beers.xml - check_mode: yes - diff: yes - register: comparison - #command: diff -u {{ role_path }}/results/test-set-namespaced-element-value.xml /tmp/ansible-xml-namespaced-beers.xml - - - name: Test expected result - assert: - that: - - set_element_first_run.changed == true - - set_element_second_run.changed == false - - comparison.changed == false # identical diff --git a/test/integration/targets/incidental_xml/tasks/test-xmlstring.yml b/test/integration/targets/incidental_xml/tasks/test-xmlstring.yml deleted file mode 100644 index 4620d984..00000000 --- a/test/integration/targets/incidental_xml/tasks/test-xmlstring.yml +++ /dev/null @@ -1,81 +0,0 @@ ---- - - name: Copy expected results to remote - copy: - src: "results/{{ item }}" - dest: "/tmp/{{ item }}" - with_items: - - test-pretty-print.xml - - test-pretty-print-only.xml - - # NOTE: Jinja2 templating eats trailing newlines - - name: Read from xmlstring (not using pretty_print) - xml: - xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}" - xpath: . - register: xmlresponse - - - name: Compare to expected result - copy: - content: "{{ xmlresponse.xmlstring }}\n" - dest: '/tmp/test-pretty-print-only.xml' - check_mode: yes - diff: yes - register: comparison - - - name: Test expected result - assert: - that: - - xmlresponse.changed == false - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml - - - # NOTE: Jinja2 templating eats trailing newlines - - name: Read from xmlstring (using pretty_print) - xml: - xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}" - pretty_print: yes - register: xmlresponse - - - name: Compare to expected result - copy: - content: '{{ xmlresponse.xmlstring }}' - dest: '/tmp/test-pretty-print-only.xml' - check_mode: yes - diff: yes - register: comparison - - # FIXME: This change is related to the newline added by pretty_print - - name: Test expected result - assert: - that: - - xmlresponse.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml - - - # NOTE: Jinja2 templating eats trailing newlines - - name: Read from xmlstring - xml: - xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}" - xpath: /business/beers - pretty_print: yes - add_children: - - beer: Old Rasputin - register: xmlresponse_modification - - - name: Compare to expected result - copy: - content: '{{ xmlresponse_modification.xmlstring }}' - dest: '/tmp/test-pretty-print.xml' - check_mode: yes - diff: yes - register: comparison - - # FIXME: This change is related to the newline added by pretty_print - - name: Test expected result - assert: - that: - - xmlresponse_modification.changed == true - - comparison.changed == false # identical - #command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml diff --git a/test/integration/targets/incidental_xml/vars/main.yml b/test/integration/targets/incidental_xml/vars/main.yml deleted file mode 100644 index 7c5675bd..00000000 --- a/test/integration/targets/incidental_xml/vars/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# -*- mode: yaml -* ---- -bad_beers: -- beer: "Natty Lite" -- beer: "Miller Lite" -- beer: "Coors Lite" diff --git a/test/integration/targets/include_import/apply/include_apply_65710.yml b/test/integration/targets/include_import/apply/include_apply_65710.yml new file mode 100644 index 00000000..457aab81 --- /dev/null +++ b/test/integration/targets/include_import/apply/include_apply_65710.yml @@ -0,0 +1,11 @@ +- hosts: localhost + gather_facts: false + tasks: + - include_tasks: + file: include_tasks.yml + apply: + tags: always + + - assert: + that: + - include_tasks_result is defined diff --git a/test/integration/targets/include_import/runme.sh b/test/integration/targets/include_import/runme.sh index 68b12a1f..f2633032 100755 --- a/test/integration/targets/include_import/runme.sh +++ b/test/integration/targets/include_import/runme.sh @@ -5,7 +5,7 @@ set -eux export ANSIBLE_ROLES_PATH=./roles function gen_task_files() { - for i in $(seq -f '%03g' 1 39); do + for i in $(printf "%03d " {1..39}); do echo -e "- name: Hello Message\n debug:\n msg: Task file ${i}" > "tasks/hello/tasks-file-${i}.yml" done } @@ -87,6 +87,9 @@ if [[ -z "$OUT" ]]; then exit 1 fi +ANSIBLE_STRATEGY='linear' ANSIBLE_PLAYBOOK_VARS_ROOT=all ansible-playbook apply/include_apply_65710.yml -i inventory "$@" +ANSIBLE_STRATEGY='free' ANSIBLE_PLAYBOOK_VARS_ROOT=all ansible-playbook apply/include_apply_65710.yml -i inventory "$@" + # Test that duplicate items in loop are not deduped ANSIBLE_STRATEGY='linear' ansible-playbook tasks/test_include_dupe_loop.yml -i inventory "$@" | tee test_include_dupe_loop.out test "$(grep -c '"item=foo"' test_include_dupe_loop.out)" = 3 @@ -121,4 +124,5 @@ test "$(grep -c 'ok=3' test_allow_single_role_dup.out)" = 1 # https://github.com/ansible/ansible/issues/66764 ANSIBLE_HOST_PATTERN_MISMATCH=error ansible-playbook empty_group_warning/playbook.yml +ansible-playbook test_include_loop.yml "$@" ansible-playbook test_include_loop_fqcn.yml "$@" diff --git a/test/integration/targets/include_import/test_include_loop.yml b/test/integration/targets/include_import/test_include_loop.yml new file mode 100644 index 00000000..33775d14 --- /dev/null +++ b/test/integration/targets/include_import/test_include_loop.yml @@ -0,0 +1,17 @@ +- hosts: localhost + gather_facts: false + tasks: + - name: skipped include undefined loop + include_tasks: doesnt_matter.yml + loop: '{{ lkjsdflkjsdlfkjsdlfkjsdf }}' + when: false + register: skipped_include + + - debug: + var: skipped_include + + - assert: + that: + - skipped_include.results is undefined + - skipped_include.skip_reason is defined + - skipped_include is skipped diff --git a/test/integration/targets/includes/inherit_notify.yml b/test/integration/targets/includes/inherit_notify.yml new file mode 100644 index 00000000..f868be16 --- /dev/null +++ b/test/integration/targets/includes/inherit_notify.yml @@ -0,0 +1,18 @@ +- hosts: localhost + gather_facts: false + pre_tasks: + - include_tasks: + file: tasks/trigger_change.yml + apply: + notify: hello + + handlers: + - name: hello + set_fact: hello=world + + tasks: + - name: ensure handler ran + assert: + that: + - hello is defined + - "hello == 'world'" diff --git a/test/integration/targets/includes/runme.sh b/test/integration/targets/includes/runme.sh index dff40029..70ff105b 100755 --- a/test/integration/targets/includes/runme.sh +++ b/test/integration/targets/includes/runme.sh @@ -3,3 +3,5 @@ set -eux ansible-playbook test_includes.yml -i ../../inventory "$@" + +ansible-playbook inherit_notify.yml "$@" diff --git a/test/integration/targets/includes/tasks/trigger_change.yml b/test/integration/targets/includes/tasks/trigger_change.yml new file mode 100644 index 00000000..6ee45515 --- /dev/null +++ b/test/integration/targets/includes/tasks/trigger_change.yml @@ -0,0 +1,2 @@ +- debug: msg="I trigger changed!" + changed_when: true diff --git a/test/integration/targets/infra/library/test.py b/test/integration/targets/infra/library/test.py index 93860575..dbc4b610 100644 --- a/test/integration/targets/infra/library/test.py +++ b/test/integration/targets/infra/library/test.py @@ -1,6 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + from ansible.module_utils.basic import AnsibleModule diff --git a/test/integration/targets/interpreter_discovery_python/tasks/main.yml b/test/integration/targets/interpreter_discovery_python/tasks/main.yml index be15186f..b8bafd15 100644 --- a/test/integration/targets/interpreter_discovery_python/tasks/main.yml +++ b/test/integration/targets/interpreter_discovery_python/tasks/main.yml @@ -140,8 +140,11 @@ - name: debian assertions assert: that: - - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python3' - when: distro == 'debian' and distro_version is version('10', '>=') + # Debian 8 and older + - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python' and distro_version is version('8', '<=') or distro_version is version('8', '>') + # Debian 10 and newer + - auto_out.ansible_facts.discovered_interpreter_python == '/usr/bin/python3' and distro_version is version('10', '>=') or distro_version is version('10', '<') + when: distro == 'debian' - name: fedora assertions assert: diff --git a/test/integration/targets/inventory/1/2/3/extra_vars_relative.yml b/test/integration/targets/inventory/1/2/3/extra_vars_relative.yml new file mode 100644 index 00000000..fa50a5ac --- /dev/null +++ b/test/integration/targets/inventory/1/2/3/extra_vars_relative.yml @@ -0,0 +1,19 @@ +- hosts: localhost + gather_facts: false + vars: + conditions: + - my is defined + - my == 'var' + - "'webservers' in groups" + - "'web_host.example.com' in groups['webservers']" + tasks: + - name: Make sure all is loaded + assert: + that: '{{conditions}}' + + - name: Reload inventory, forces extra vars re-eval from diff basedir + meta: refresh_inventory + + - name: Make sure all is loaded, again + assert: + that: '{{conditions}}' diff --git a/test/integration/targets/inventory/1/2/inventory.yml b/test/integration/targets/inventory/1/2/inventory.yml new file mode 100644 index 00000000..5082cef2 --- /dev/null +++ b/test/integration/targets/inventory/1/2/inventory.yml @@ -0,0 +1,3 @@ +plugin: constructed +groups: + webservers: inventory_hostname.startswith('web') diff --git a/test/integration/targets/inventory/1/vars.yml b/test/integration/targets/inventory/1/vars.yml new file mode 100644 index 00000000..c1145843 --- /dev/null +++ b/test/integration/targets/inventory/1/vars.yml @@ -0,0 +1 @@ +my: var diff --git a/test/integration/targets/inventory/extra_vars_constructed.yml b/test/integration/targets/inventory/extra_vars_constructed.yml new file mode 100644 index 00000000..66bee863 --- /dev/null +++ b/test/integration/targets/inventory/extra_vars_constructed.yml @@ -0,0 +1,5 @@ +plugin: constructed +strict: true +use_extra_vars: True +compose: + example: " 'hello' + from_extras" diff --git a/test/integration/targets/inventory/runme.sh b/test/integration/targets/inventory/runme.sh index 87bef447..a5e40d55 100755 --- a/test/integration/targets/inventory/runme.sh +++ b/test/integration/targets/inventory/runme.sh @@ -41,6 +41,9 @@ ansible-playbook -i ../../inventory "$@" strategy.yml ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS=always ansible-playbook -i ../../inventory "$@" strategy.yml ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS=never ansible-playbook -i ../../inventory "$@" strategy.yml +# test extra vars +ansible-inventory -i testhost, -i ./extra_vars_constructed.yml --list -e 'from_extras=hey ' "$@"|grep '"example": "hellohey"' + # Do not fail when all inventories fail to parse. # Do not fail when any inventory fails to parse. ANSIBLE_INVENTORY_UNPARSED_FAILED=False ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=False ansible -m ping localhost -i /idontexist "$@" @@ -83,4 +86,11 @@ if ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=True ansible -m ping localhost -i "$ exit 1 fi +# ensure we don't traceback on inventory due to variables with int as key ansible-inventory -i inv_with_int.yml --list "$@" + +# test in subshell relative paths work mid play for extra vars in inventory refresh +{ + cd 1/2 + ansible-playbook -e @../vars.yml -i 'web_host.example.com,' -i inventory.yml 3/extra_vars_relative.yml "$@" +} diff --git a/test/integration/targets/inventory_constructed/aliases b/test/integration/targets/inventory_constructed/aliases new file mode 100644 index 00000000..b5983214 --- /dev/null +++ b/test/integration/targets/inventory_constructed/aliases @@ -0,0 +1 @@ +shippable/posix/group3 diff --git a/test/integration/targets/inventory_constructed/constructed.yml b/test/integration/targets/inventory_constructed/constructed.yml new file mode 100644 index 00000000..baeea323 --- /dev/null +++ b/test/integration/targets/inventory_constructed/constructed.yml @@ -0,0 +1,19 @@ +plugin: constructed +keyed_groups: + - key: hostvar0 + - key: hostvar1 + - key: hostvar2 + + - key: hostvar0 + separator: 'separator' + - key: hostvar1 + separator: 'separator' + - key: hostvar2 + separator: 'separator' + + - key: hostvar0 + prefix: 'prefix' + - key: hostvar1 + prefix: 'prefix' + - key: hostvar2 + prefix: 'prefix' diff --git a/test/integration/targets/inventory_constructed/invs/1/group_vars/stuff.yml b/test/integration/targets/inventory_constructed/invs/1/group_vars/stuff.yml new file mode 100644 index 00000000..a67b90f4 --- /dev/null +++ b/test/integration/targets/inventory_constructed/invs/1/group_vars/stuff.yml @@ -0,0 +1 @@ +iamdefined: group4testing diff --git a/test/integration/targets/inventory_constructed/invs/1/host_vars/testing.yml b/test/integration/targets/inventory_constructed/invs/1/host_vars/testing.yml new file mode 100644 index 00000000..0ffe3821 --- /dev/null +++ b/test/integration/targets/inventory_constructed/invs/1/host_vars/testing.yml @@ -0,0 +1 @@ +hola: lola diff --git a/test/integration/targets/inventory_constructed/invs/1/one.yml b/test/integration/targets/inventory_constructed/invs/1/one.yml new file mode 100644 index 00000000..ad5a5e0a --- /dev/null +++ b/test/integration/targets/inventory_constructed/invs/1/one.yml @@ -0,0 +1,5 @@ +all: + children: + stuff: + hosts: + testing: diff --git a/test/integration/targets/inventory_constructed/invs/2/constructed.yml b/test/integration/targets/inventory_constructed/invs/2/constructed.yml new file mode 100644 index 00000000..7c62ef1d --- /dev/null +++ b/test/integration/targets/inventory_constructed/invs/2/constructed.yml @@ -0,0 +1,7 @@ +plugin: constructed +use_vars_plugins: true +keyed_groups: + - key: iamdefined + prefix: c + - key: hola + prefix: c diff --git a/test/integration/targets/inventory_constructed/no_leading_separator_constructed.yml b/test/integration/targets/inventory_constructed/no_leading_separator_constructed.yml new file mode 100644 index 00000000..5f35de14 --- /dev/null +++ b/test/integration/targets/inventory_constructed/no_leading_separator_constructed.yml @@ -0,0 +1,20 @@ +plugin: constructed +keyed_groups: + - key: hostvar0 + - key: hostvar1 + - key: hostvar2 + + - key: hostvar0 + separator: 'separator' + - key: hostvar1 + separator: 'separator' + - key: hostvar2 + separator: 'separator' + + - key: hostvar0 + prefix: 'prefix' + - key: hostvar1 + prefix: 'prefix' + - key: hostvar2 + prefix: 'prefix' +leading_separator: False diff --git a/test/integration/targets/inventory_constructed/runme.sh b/test/integration/targets/inventory_constructed/runme.sh new file mode 100755 index 00000000..0cd1a293 --- /dev/null +++ b/test/integration/targets/inventory_constructed/runme.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -ex + +ansible-inventory -i static_inventory.yml -i constructed.yml --graph | tee out.txt + +grep '@_hostvalue1' out.txt +grep '@_item0' out.txt +grep '@_key0_value0' out.txt +grep '@prefix_hostvalue1' out.txt +grep '@prefix_item0' out.txt +grep '@prefix_key0_value0' out.txt +grep '@separatorhostvalue1' out.txt +grep '@separatoritem0' out.txt +grep '@separatorkey0separatorvalue0' out.txt + +ansible-inventory -i static_inventory.yml -i no_leading_separator_constructed.yml --graph | tee out.txt + +grep '@hostvalue1' out.txt +grep '@item0' out.txt +grep '@key0_value0' out.txt +grep '@key0separatorvalue0' out.txt +grep '@prefix_hostvalue1' out.txt +grep '@prefix_item0' out.txt +grep '@prefix_key0_value0' out.txt + + +# test using use_vars_plugins +ansible-inventory -i invs/1/one.yml -i invs/2/constructed.yml --graph | tee out.txt + +grep '@c_lola' out.txt +grep '@c_group4testing' out.txt diff --git a/test/integration/targets/inventory_constructed/static_inventory.yml b/test/integration/targets/inventory_constructed/static_inventory.yml new file mode 100644 index 00000000..d050df4f --- /dev/null +++ b/test/integration/targets/inventory_constructed/static_inventory.yml @@ -0,0 +1,8 @@ +all: + hosts: + host0: + hostvar0: + key0: value0 + hostvar1: hostvalue1 + hostvar2: + - item0 diff --git a/test/integration/targets/json_cleanup/aliases b/test/integration/targets/json_cleanup/aliases new file mode 100644 index 00000000..765b70da --- /dev/null +++ b/test/integration/targets/json_cleanup/aliases @@ -0,0 +1 @@ +shippable/posix/group2 diff --git a/test/integration/targets/json_cleanup/library/bad_json b/test/integration/targets/json_cleanup/library/bad_json new file mode 100644 index 00000000..1df8c725 --- /dev/null +++ b/test/integration/targets/json_cleanup/library/bad_json @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eu + +echo 'this stuff should be ignored' + +echo '[ looks like a json list]' + +echo '{"changed": false, "failed": false, "msg": "good json response"}' + +echo 'moar garbage' diff --git a/test/integration/targets/json_cleanup/module_output_cleaning.yml b/test/integration/targets/json_cleanup/module_output_cleaning.yml new file mode 100644 index 00000000..165352aa --- /dev/null +++ b/test/integration/targets/json_cleanup/module_output_cleaning.yml @@ -0,0 +1,26 @@ +- name: ensure we clean module output well + hosts: localhost + gather_facts: false + tasks: + - name: call module that spews extra stuff + bad_json: + register: clean_json + ignore_errors: true + + - name: all expected is there + assert: + that: + - clean_json is success + - clean_json is not changed + - "clean_json['msg'] == 'good json response'" + + - name: all non wanted is not there + assert: + that: + - item not in clean_json.values() + loop: + - this stuff should be ignored + - [ looks like a json list] + - '[ looks like a json list]' + - ' looks like a json list' + - moar garbage diff --git a/test/integration/targets/json_cleanup/runme.sh b/test/integration/targets/json_cleanup/runme.sh new file mode 100755 index 00000000..2de3bd0e --- /dev/null +++ b/test/integration/targets/json_cleanup/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook module_output_cleaning.yml "$@" diff --git a/test/integration/targets/lineinfile/files/teststring.conf b/test/integration/targets/lineinfile/files/teststring.conf new file mode 100644 index 00000000..15404cd6 --- /dev/null +++ b/test/integration/targets/lineinfile/files/teststring.conf @@ -0,0 +1,5 @@ +[section_one] + +[section_two] + +[section_three] diff --git a/test/integration/targets/lineinfile/files/teststring.txt b/test/integration/targets/lineinfile/files/teststring.txt new file mode 100644 index 00000000..b2044945 --- /dev/null +++ b/test/integration/targets/lineinfile/files/teststring.txt @@ -0,0 +1,5 @@ +This is line 1 +This is line 2 +(\\w)(\\s+)([\\.,]) +This is line 4 +<FilesMatch ".py[45]?$"> diff --git a/test/integration/targets/lineinfile/files/teststring_58923.txt b/test/integration/targets/lineinfile/files/teststring_58923.txt new file mode 100644 index 00000000..34579fde --- /dev/null +++ b/test/integration/targets/lineinfile/files/teststring_58923.txt @@ -0,0 +1,4 @@ +#!/bin/sh + +case "`uname`" in + Darwin*) if [ -z "$JAVA_HOME" ] ; then diff --git a/test/integration/targets/lineinfile/tasks/main.yml b/test/integration/targets/lineinfile/tasks/main.yml index 840051cf..cad926b3 100644 --- a/test/integration/targets/lineinfile/tasks/main.yml +++ b/test/integration/targets/lineinfile/tasks/main.yml @@ -253,6 +253,8 @@ that: - "result.stat.checksum == 'ab56c210ea82839a54487464800fed4878cb2608'" +- import_tasks: test_string01.yml + - name: use create=yes lineinfile: dest: "{{ output_dir }}/new_test.txt" @@ -806,7 +808,8 @@ - oneline_insbefore_test2 is not changed - oneline_insbefore_file.stat.checksum == '4dca56d05a21f0d018cd311f43e134e4501cf6d9' -################################################################### +- import_tasks: test_string02.yml + # Issue 29443 # When using an empty regexp, replace the last line (since it matches every line) # but also provide a warning. @@ -849,6 +852,49 @@ If this is desired, use '^' to match every line in the file and avoid this warning. ################################################################### +# When using an empty search string, replace the last line (since it matches every line) +# but also provide a warning. + +- name: Deploy the test file for lineinfile + copy: + src: teststring.txt + dest: "{{ output_dir }}/teststring.txt" + register: result + +- name: Assert that the test file was deployed + assert: + that: + - result is changed + - result.checksum == '481c2b73fe062390afdd294063a4f8285d69ac85' + - result.state == 'file' + +- name: Insert a line in the file using an empty string as a search string + lineinfile: + path: "{{ output_dir }}/teststring.txt" + search_string: '' + line: This is line 6 + register: insert_empty_literal + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring.txt" + register: result + +- name: Assert that the file contents match what is expected and a warning was displayed + assert: + that: + - insert_empty_literal is changed + - warning_message in insert_empty_literal.warnings + - result.stat.checksum == 'eaa79f878557d4bd8d96787a850526a0facab342' + vars: + warning_message: >- + The search string is an empty string, which will match every line in the file. + This may have unintended consequences, such as replacing the last line in the file rather than appending. + +- name: meta + meta: end_play + +################################################################### ## Issue #58923 ## Using firstmatch with insertafter and ensure multiple lines are not inserted @@ -1126,6 +1172,120 @@ - insertbefore_test5 is not changed - insertbefore_test5_file.stat.checksum == '3c6630b9d44f561ea9ad999be56a7504cadc12f7' +######################################################################################## +# Same tests for literal + +# Test insertafter with literal +- name: Deploy the test file + copy: + src: teststring_58923.txt + dest: "{{ output_dir }}/teststring_58923.txt" + register: initial_file + +- name: Assert that the test file was deployed + assert: + that: + - initial_file is changed + - initial_file.checksum == 'b6379ba43261c451a62102acb2c7f438a177c66e' + - initial_file.state == 'file' + +# Regarding the documentation: +# If the search string is passed to both search_string and +# insertafter, insertafter is only honored if no match for search_string is found. +# Therefore, +# when search_string expressions are passed to both search_string and insertafter, then: +# 1. search_string was found -> ignore insertafter, replace the founded line +# 2. search_string was not found -> insert the line after 'insertafter' line + +# literal is not present in the file, so the line must be inserted after ^#!/bin/sh +- name: Add the line using firstmatch, regexp, and insertafter + lineinfile: + path: "{{ output_dir }}/teststring_58923.txt" + insertafter: '^#!/bin/sh' + search_string: export FISHEYE_OPTS + firstmatch: true + line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m" + register: insertafter_test1 + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring_58923.txt" + register: insertafter_test1_file + +- name: Add the line using firstmatch, literal, and insertafter again + lineinfile: + path: "{{ output_dir }}/teststring_58923.txt" + insertafter: '^#!/bin/sh' + search_string: export FISHEYE_OPTS + firstmatch: true + line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m" + register: insertafter_test2 + +# Check of the prev step. +# We tried to add the same line with the same playbook, +# so nothing has been added: +- name: Stat the file again + stat: + path: "{{ output_dir }}/teststring_58923.txt" + register: insertafter_test2_file + +- name: Assert insertafter tests gave the expected results + assert: + that: + - insertafter_test1 is changed + - insertafter_test1_file.stat.checksum == '9232aed6fe88714964d9e29d13e42cd782070b08' + - insertafter_test2 is not changed + - insertafter_test2_file.stat.checksum == '9232aed6fe88714964d9e29d13e42cd782070b08' + +# Test insertbefore with literal +- name: Deploy the test file + copy: + src: teststring_58923.txt + dest: "{{ output_dir }}/teststring_58923.txt" + register: initial_file + +- name: Assert that the test file was deployed + assert: + that: + - initial_file is changed + - initial_file.checksum == 'b6379ba43261c451a62102acb2c7f438a177c66e' + - initial_file.state == 'file' + +- name: Add the line using literal, firstmatch, and insertbefore + lineinfile: + path: "{{ output_dir }}/teststring_58923.txt" + insertbefore: '^#!/bin/sh' + search_string: export FISHEYE_OPTS + firstmatch: true + line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m" + register: insertbefore_test1 + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring_58923.txt" + register: insertbefore_test1_file + +- name: Add the line using literal, firstmatch, and insertbefore again + lineinfile: + path: "{{ output_dir }}/teststring_58923.txt" + insertbefore: '^#!/bin/sh' + search_string: export FISHEYE_OPTS + firstmatch: true + line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m" + register: insertbefore_test2 + +- name: Stat the file again + stat: + path: "{{ output_dir }}/teststring_58923.txt" + register: insertbefore_test2_file + +- name: Assert insertbefore with literal tests gave the expected results + assert: + that: + - insertbefore_test1 is changed + - insertbefore_test1_file.stat.checksum == '3c6630b9d44f561ea9ad999be56a7504cadc12f7' + - insertbefore_test2 is not changed + - insertbefore_test2_file.stat.checksum == '3c6630b9d44f561ea9ad999be56a7504cadc12f7' # Test inserting a line at the end of the file using regexp with insertafter # https://github.com/ansible/ansible/issues/63684 @@ -1155,3 +1315,32 @@ - testend1 is changed - testend2 is changed - testend_file.stat.checksum == 'ef36116966836ce04f6b249fd1837706acae4e19' + +# Test inserting a line at the end of the file using search_string with insertafter + +- name: Create a file by inserting a line + lineinfile: + path: "{{ output_dir }}/testendliteral.txt" + create: yes + line: testline + register: testend1 + +- name: Insert a line at the end of the file + lineinfile: + path: "{{ output_dir }}/testendliteral.txt" + insertafter: testline + search_string: line at the end + line: line at the end + register: testend2 + +- name: Stat the file + stat: + path: "{{ output_dir }}/testendliteral.txt" + register: testend_file + +- name: Assert inserting at the end gave the expected results. + assert: + that: + - testend1 is changed + - testend2 is changed + - testend_file.stat.checksum == 'ef36116966836ce04f6b249fd1837706acae4e19' diff --git a/test/integration/targets/lineinfile/tasks/test_string01.yml b/test/integration/targets/lineinfile/tasks/test_string01.yml new file mode 100644 index 00000000..6e0c12c3 --- /dev/null +++ b/test/integration/targets/lineinfile/tasks/test_string01.yml @@ -0,0 +1,142 @@ +--- +################################################################### +# 1st search_string tests + +- name: deploy the test file for lineinfile string + copy: + src: teststring.txt + dest: "{{ output_dir }}/teststring.txt" + register: result + +- name: assert that the test file was deployed + assert: + that: + - result is changed + - "result.checksum == '481c2b73fe062390afdd294063a4f8285d69ac85'" + - "result.state == 'file'" + +- name: insert a line at the beginning of the file, and back it up + lineinfile: + dest: "{{ output_dir }}/teststring.txt" + state: present + line: "New line at the beginning" + insertbefore: "BOF" + backup: yes + register: result1 + +- name: insert a line at the beginning of the file again + lineinfile: + dest: "{{ output_dir }}/teststring.txt" + state: present + line: "New line at the beginning" + insertbefore: "BOF" + register: result2 + +- name: Replace a line using string + lineinfile: + dest: "{{ output_dir }}/teststring.txt" + state: present + line: "Thi$ i^ [ine 3" + search_string: (\\w)(\\s+)([\\.,]) + register: backrefs_result1 + +- name: Replace a line again using string + lineinfile: + dest: "{{ output_dir }}/teststring.txt" + state: present + line: "Thi$ i^ [ine 3" + search_string: (\\w)(\\s+)([\\.,]) + register: backrefs_result2 + +- command: cat {{ output_dir }}/teststring.txt + +- name: assert that the line with backrefs was changed + assert: + that: + - backrefs_result1 is changed + - backrefs_result2 is not changed + - "backrefs_result1.msg == 'line replaced'" + +- name: stat the test after the backref line was replaced + stat: + path: "{{ output_dir }}/teststring.txt" + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == '8084519b53e268920a46592a112297715951f167'" + +- name: remove the middle line using string + lineinfile: + dest: "{{ output_dir }}/teststring.txt" + state: absent + search_string: "Thi$ i^ [ine 3" + register: result + +- name: assert that the line was removed + assert: + that: + - result is changed + - "result.msg == '1 line(s) removed'" + +- name: stat the test after the middle line was removed + stat: + path: "{{ output_dir }}/teststring.txt" + register: result + +- name: assert test checksum matches after the middle line was removed + assert: + that: + - "result.stat.checksum == '89919ef2ef91e48ad02e0ca2bcb76dfc2a86d516'" + +- name: run a validation script that succeeds using string + lineinfile: + dest: "{{ output_dir }}/teststring.txt" + state: absent + search_string: <FilesMatch ".py[45]?$"> + validate: "true %s" + register: result + +- name: assert that the file validated after removing a line + assert: + that: + - result is changed + - "result.msg == '1 line(s) removed'" + +- name: stat the test after the validation succeeded + stat: + path: "{{ output_dir }}/teststring.txt" + register: result + +- name: assert test checksum matches after the validation succeeded + assert: + that: + - "result.stat.checksum == 'ba9600b34febbc88bfb3ca99cd6b57f1010c19a4'" + +- name: run a validation script that fails using string + lineinfile: + dest: "{{ output_dir }}/teststring.txt" + state: absent + search_string: "This is line 1" + validate: "/bin/false %s" + register: result + ignore_errors: yes + +- name: assert that the validate failed + assert: + that: + - "result.failed == true" + +- name: stat the test after the validation failed + stat: + path: "{{ output_dir }}/teststring.txt" + register: result + +- name: assert test checksum matches the previous after the validation failed + assert: + that: + - "result.stat.checksum == 'ba9600b34febbc88bfb3ca99cd6b57f1010c19a4'" + +# End of string tests +################################################################### diff --git a/test/integration/targets/lineinfile/tasks/test_string02.yml b/test/integration/targets/lineinfile/tasks/test_string02.yml new file mode 100644 index 00000000..886b290d --- /dev/null +++ b/test/integration/targets/lineinfile/tasks/test_string02.yml @@ -0,0 +1,166 @@ +--- +################################################################### +# 2nd search_string tests + +- name: Deploy the teststring.conf file + copy: + src: teststring.conf + dest: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the teststring.conf file was deployed + assert: + that: + - result is changed + - result.checksum == '6037f13e419b132eb3fd20a89e60c6c87a6add38' + - result.state == 'file' + +# Test instertafter +- name: Insert lines after with string + lineinfile: + path: "{{ output_dir }}/teststring.conf" + search_string: "{{ item.regexp }}" + line: "{{ item.line }}" + insertafter: "{{ item.after }}" + with_items: "{{ test_befaf_regexp }}" + register: _multitest_5 + +- name: Do the same thing again and check for changes + lineinfile: + path: "{{ output_dir }}/teststring.conf" + search_string: "{{ item.regexp }}" + line: "{{ item.line }}" + insertafter: "{{ item.after }}" + with_items: "{{ test_befaf_regexp }}" + register: _multitest_6 + +- name: Assert that the file was changed the first time but not the second time + assert: + that: + - item.0 is changed + - item.1 is not changed + with_together: + - "{{ _multitest_5.results }}" + - "{{ _multitest_6.results }}" + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the file contents match what is expected + assert: + that: + - result.stat.checksum == '06e2c456e5028dd7bcd0b117b5927a1139458c82' + +- name: Do the same thing a third time without string and check for changes + lineinfile: + path: "{{ output_dir }}/teststring.conf" + line: "{{ item.line }}" + insertafter: "{{ item.after }}" + with_items: "{{ test_befaf_regexp }}" + register: _multitest_7 + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the file was changed when no string was provided + assert: + that: + - item is not changed + with_items: "{{ _multitest_7.results }}" + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the file contents match what is expected + assert: + that: + - result.stat.checksum == '06e2c456e5028dd7bcd0b117b5927a1139458c82' + +# Test insertbefore +- name: Deploy the test.conf file + copy: + src: teststring.conf + dest: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the teststring.conf file was deployed + assert: + that: + - result is changed + - result.checksum == '6037f13e419b132eb3fd20a89e60c6c87a6add38' + - result.state == 'file' + +- name: Insert lines before with string + lineinfile: + path: "{{ output_dir }}/teststring.conf" + search_string: "{{ item.regexp }}" + line: "{{ item.line }}" + insertbefore: "{{ item.before }}" + with_items: "{{ test_befaf_regexp }}" + register: _multitest_8 + +- name: Do the same thing again and check for changes + lineinfile: + path: "{{ output_dir }}/teststring.conf" + search_string: "{{ item.regexp }}" + line: "{{ item.line }}" + insertbefore: "{{ item.before }}" + with_items: "{{ test_befaf_regexp }}" + register: _multitest_9 + +- name: Assert that the file was changed the first time but not the second time + assert: + that: + - item.0 is changed + - item.1 is not changed + with_together: + - "{{ _multitest_8.results }}" + - "{{ _multitest_9.results }}" + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the file contents match what is expected + assert: + that: + - result.stat.checksum == 'c3be9438a07c44d4c256cebfcdbca15a15b1db91' + +- name: Do the same thing a third time without string and check for changes + lineinfile: + path: "{{ output_dir }}/teststring.conf" + line: "{{ item.line }}" + insertbefore: "{{ item.before }}" + with_items: "{{ test_befaf_regexp }}" + register: _multitest_10 + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the file was changed when no string was provided + assert: + that: + - item is not changed + with_items: "{{ _multitest_10.results }}" + +- name: Stat the file + stat: + path: "{{ output_dir }}/teststring.conf" + register: result + +- name: Assert that the file contents match what is expected + assert: + that: + - result.stat.checksum == 'c3be9438a07c44d4c256cebfcdbca15a15b1db91' + +# End of string tests +################################################################### diff --git a/test/integration/targets/lookup_config/tasks/main.yml b/test/integration/targets/lookup_config/tasks/main.yml index be185197..cda9aedc 100644 --- a/test/integration/targets/lookup_config/tasks/main.yml +++ b/test/integration/targets/lookup_config/tasks/main.yml @@ -42,7 +42,7 @@ - name: Verify lookup_config assert: that: - - '"meow" in lookup("config", "ANSIBLE_COW_WHITELIST")' + - '"meow" in lookup("config", "ANSIBLE_COW_ACCEPTLIST")' - lookup_config_1 is failed - '"Unable to find setting" in lookup_config_1.msg' - lookup_config_2 is failed diff --git a/test/integration/targets/lookup_csvfile/aliases b/test/integration/targets/lookup_csvfile/aliases new file mode 100644 index 00000000..45489be8 --- /dev/null +++ b/test/integration/targets/lookup_csvfile/aliases @@ -0,0 +1,2 @@ +shippable/posix/group2 +skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller diff --git a/test/integration/targets/lookup_csvfile/files/cool list of things.csv b/test/integration/targets/lookup_csvfile/files/cool list of things.csv new file mode 100644 index 00000000..b1a74a0a --- /dev/null +++ b/test/integration/targets/lookup_csvfile/files/cool list of things.csv @@ -0,0 +1,3 @@ +woo,i,have,spaces,in,my,filename +i,am,so,cool,haha,be,jealous +maybe,i,will,work,like,i,should diff --git a/test/integration/targets/lookup_csvfile/files/crlf.csv b/test/integration/targets/lookup_csvfile/files/crlf.csv new file mode 100644 index 00000000..a17f6c47 --- /dev/null +++ b/test/integration/targets/lookup_csvfile/files/crlf.csv @@ -0,0 +1,2 @@ +this file,has,crlf,line,endings
+ansible,parses,them,just,fine
diff --git a/test/integration/targets/lookup_csvfile/files/people.csv b/test/integration/targets/lookup_csvfile/files/people.csv new file mode 100644 index 00000000..f93498cf --- /dev/null +++ b/test/integration/targets/lookup_csvfile/files/people.csv @@ -0,0 +1,6 @@ +# Last,First,Email,Extension +Smith,Jane,jsmith@example.com,1234 +Ipsum,Lorem,lipsum@another.example.com,9001 +"German von Lastname",Demo,hello@example.com,123123 +Example,Person,"crazy email"@example.com,9876 +"""The Rock"" Johnson",Dwayne,uhoh@example.com,1337 diff --git a/test/integration/targets/lookup_csvfile/files/tabs.csv b/test/integration/targets/lookup_csvfile/files/tabs.csv new file mode 100644 index 00000000..69f4d876 --- /dev/null +++ b/test/integration/targets/lookup_csvfile/files/tabs.csv @@ -0,0 +1,4 @@ +fruit bananas 30 +fruit apples 9 +electronics tvs 8 +shoes sneakers 26 diff --git a/test/integration/targets/lookup_csvfile/files/x1a.csv b/test/integration/targets/lookup_csvfile/files/x1a.csv new file mode 100644 index 00000000..d2d5a0d4 --- /dev/null +++ b/test/integration/targets/lookup_csvfile/files/x1a.csv @@ -0,0 +1,3 @@ +separatedbyx1achars +againbecause +wecan diff --git a/test/integration/targets/lookup_csvfile/tasks/main.yml b/test/integration/targets/lookup_csvfile/tasks/main.yml new file mode 100644 index 00000000..14b79bd6 --- /dev/null +++ b/test/integration/targets/lookup_csvfile/tasks/main.yml @@ -0,0 +1,69 @@ +- set_fact: + this_will_error: "{{ lookup('csvfile', 'file=people.csv delimiter=, col=1') }}" + ignore_errors: yes + register: no_keyword + +- set_fact: + this_will_error: "{{ lookup('csvfile', 'foo file=people.csv delimiter=, col=1 thisarg=doesnotexist') }}" + ignore_errors: yes + register: invalid_arg + +- set_fact: + this_will_error: "{{ lookup('csvfile', 'foo file=doesnotexist delimiter=, col=1') }}" + ignore_errors: yes + register: missing_file + +- name: Make sure we failed above + assert: + that: + - no_keyword is failed + - > + "Search key is required but was not found" in no_keyword.msg + - > + "not in paramvals" in invalid_arg.msg + - missing_file is failed + - > + "need string or buffer" in missing_file.msg or "expected str, bytes or os.PathLike object" in missing_file.msg + +- name: Check basic comma-separated file + assert: + that: + - lookup('csvfile', 'Smith file=people.csv delimiter=, col=1') == "Jane" + - lookup('csvfile', 'German von Lastname file=people.csv delimiter=, col=1') == "Demo" + +- name: Check tab-separated file + assert: + that: + - lookup('csvfile', 'electronics file=tabs.csv delimiter=TAB col=1') == "tvs" + - lookup('csvfile', 'fruit file=tabs.csv delimiter=TAB col=1') == "bananas" + - lookup('csvfile', 'fruit file=tabs.csv delimiter="\t" col=1') == "bananas" + +- name: Check \x1a-separated file + assert: + that: + - lookup('csvfile', 'again file=x1a.csv delimiter=\x1a col=1') == "because" + +- name: Check CSV file with CRLF line endings + assert: + that: + - lookup('csvfile', 'this file file=crlf.csv delimiter=, col=2') == "crlf" + - lookup('csvfile', 'ansible file=crlf.csv delimiter=, col=1') == "parses" + +- name: Check file with multi word filename + assert: + that: + - lookup('csvfile', 'maybe file="cool list of things.csv" delimiter=, col=3') == "work" + +- name: Test default behavior + assert: + that: + - lookup('csvfile', 'notfound file=people.csv delimiter=, col=2') == [] + - lookup('csvfile', 'notfound file=people.csv delimiter=, col=2, default=what?') == "what?" + +# NOTE: For historical reasons, this is correct; quotes in the search field must +# be treated literally as if they appear (escaped as required) in the field in the +# file. They cannot be used to surround the search text in general. +- name: Test quotes in the search field + assert: + that: + - lookup('csvfile', '"The Rock" Johnson file=people.csv delimiter=, col=1') == "Dwayne" diff --git a/test/integration/targets/lookup_dict/tasks/main.yml b/test/integration/targets/lookup_dict/tasks/main.yml index 6f778548..b1324136 100644 --- a/test/integration/targets/lookup_dict/tasks/main.yml +++ b/test/integration/targets/lookup_dict/tasks/main.yml @@ -27,7 +27,9 @@ - name: Convert a non-dict (failure expected) set_fact: - bad_fact: "{{ lookup('dict', 1) }}" + bad_fact: "{{ bbbbad }}" + vars: + bbbbad: "{{ lookup('dict', 1) }}" register: result ignore_errors: yes diff --git a/test/integration/targets/lookup_fileglob/find_levels/play.yml b/test/integration/targets/lookup_fileglob/find_levels/play.yml index 4bdee05d..578d482e 100644 --- a/test/integration/targets/lookup_fileglob/find_levels/play.yml +++ b/test/integration/targets/lookup_fileglob/find_levels/play.yml @@ -2,7 +2,7 @@ gather_facts: false vars: expected: - play_adj: ajectent to play + play_adj: adjacent to play play_adj_subdir: in files subdir adjacent to play somepath/play_adj_subsubdir: in play adjacent subdir of files/ in_role: file in role diff --git a/test/integration/targets/lookup_fileglob/find_levels/play_adj.txt b/test/integration/targets/lookup_fileglob/find_levels/play_adj.txt index 14f0cf50..2cc44111 100644 --- a/test/integration/targets/lookup_fileglob/find_levels/play_adj.txt +++ b/test/integration/targets/lookup_fileglob/find_levels/play_adj.txt @@ -1 +1 @@ -ajectent to play +adjacent to play diff --git a/test/integration/targets/lookup_fileglob/issue72873/test.yml b/test/integration/targets/lookup_fileglob/issue72873/test.yml new file mode 100644 index 00000000..218ee58d --- /dev/null +++ b/test/integration/targets/lookup_fileglob/issue72873/test.yml @@ -0,0 +1,31 @@ +- hosts: localhost + connection: local + gather_facts: false + vars: + dir: files + tasks: + - file: path='{{ dir }}' state=directory + + - file: path='setvars.bat' state=touch # in current directory! + + - file: path='{{ dir }}/{{ item }}' state=touch + loop: + - json.c + - strlcpy.c + - base64.c + - json.h + - base64.h + - strlcpy.h + - jo.c + + - name: Get working order results and sort them + set_fact: + 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 }}' + + - assert: + that: + - working == broken diff --git a/test/integration/targets/lookup_fileglob/runme.sh b/test/integration/targets/lookup_fileglob/runme.sh index 1e0297c7..be044211 100755 --- a/test/integration/targets/lookup_fileglob/runme.sh +++ b/test/integration/targets/lookup_fileglob/runme.sh @@ -13,3 +13,6 @@ for seed in foo foo/bar foo/bar/baz do ansible-playbook non_existent/play.yml -e "seed='${seed}'" "$@" done + +# test for issue 72873 fix +ansible-playbook issue72873/test.yml "$@" diff --git a/test/integration/targets/lookup_ini/duplicate.ini b/test/integration/targets/lookup_ini/duplicate.ini new file mode 100644 index 00000000..db510ddf --- /dev/null +++ b/test/integration/targets/lookup_ini/duplicate.ini @@ -0,0 +1,3 @@ +[reggae] +name = bob +name = marley diff --git a/test/integration/targets/lookup_ini/duplicate_case_check.ini b/test/integration/targets/lookup_ini/duplicate_case_check.ini new file mode 100644 index 00000000..abb0128b --- /dev/null +++ b/test/integration/targets/lookup_ini/duplicate_case_check.ini @@ -0,0 +1,3 @@ +[reggae] +name = bob +NAME = marley diff --git a/test/integration/targets/lookup_ini/inventory b/test/integration/targets/lookup_ini/inventory new file mode 100644 index 00000000..ae764270 --- /dev/null +++ b/test/integration/targets/lookup_ini/inventory @@ -0,0 +1,2 @@ +[all] +testhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/test/integration/targets/lookup_ini/runme.sh b/test/integration/targets/lookup_ini/runme.sh index 71a507de..76f836a9 100755 --- a/test/integration/targets/lookup_ini/runme.sh +++ b/test/integration/targets/lookup_ini/runme.sh @@ -2,4 +2,5 @@ set -eux -ansible-playbook test_lookup_properties.yml -i ../../inventory -v "$@" +ansible-playbook test_lookup_properties.yml -i inventory -v "$@" +ansible-playbook test_errors.yml -i inventory -v "$@" diff --git a/test/integration/targets/lookup_ini/test_errors.yml b/test/integration/targets/lookup_ini/test_errors.yml new file mode 100644 index 00000000..b7b04d90 --- /dev/null +++ b/test/integration/targets/lookup_ini/test_errors.yml @@ -0,0 +1,38 @@ +- name: Test INI lookup errors + hosts: testhost + + tasks: + - name: Test for failure on Python 3 + when: ansible_facts.python.version_info[0] >= 3 + block: + - name: Lookup a file with duplicate keys + debug: + msg: "{{ lookup('ini', 'reggae file=duplicate.ini section=reggae') }}" + ignore_errors: yes + register: duplicate + + - name: Lookup a file with keys that differ only in case + debug: + msg: "{{ lookup('ini', 'reggae file=duplicate_case_check.ini section=reggae') }}" + ignore_errors: yes + register: duplicate_case_sensitive + + - name: Ensure duplicate key errers were handled properly + assert: + that: + - duplicate is failed + - "'Duplicate option in' in duplicate.msg" + - duplicate_case_sensitive is failed + - "'Duplicate option in' in duplicate_case_sensitive.msg" + + - name: Lookup a file with a missing section + debug: + msg: "{{ lookup('ini', 'reggae file=lookup.ini section=missing') }}" + ignore_errors: yes + register: missing_section + + - name: Ensure error was shown for a missing section + assert: + that: + - missing_section is failed + - "'No section' in missing_section.msg" diff --git a/test/integration/targets/lookup_ini/test_lookup_properties.yml b/test/integration/targets/lookup_ini/test_lookup_properties.yml index a8cad9de..3a414bbc 100644 --- a/test/integration/targets/lookup_ini/test_lookup_properties.yml +++ b/test/integration/targets/lookup_ini/test_lookup_properties.yml @@ -1,70 +1,87 @@ ---- -- name: "Lookup test" - hosts: "localhost" -# connection: local +- name: Lookup test + hosts: testhost + tasks: - name: "read properties value" set_fact: - test1: "{{lookup('ini', 'value1 type=properties file=lookup.properties')}}" - test2: "{{lookup('ini', 'value2 type=properties file=lookup.properties')}}" - test_dot: "{{lookup('ini', 'value.dot type=properties file=lookup.properties')}}" + test1: "{{lookup('ini', 'value1 type=properties file=lookup.properties')}}" + test2: "{{lookup('ini', 'value2 type=properties file=lookup.properties')}}" + test_dot: "{{lookup('ini', 'value.dot type=properties file=lookup.properties')}}" field_with_space: "{{lookup('ini', 'field.with.space type=properties file=lookup.properties')}}" + - assert: that: "{{item}} is defined" with_items: [ 'test1', 'test2', 'test_dot', 'field_with_space' ] + - name: "read ini value" set_fact: - value1_global: "{{lookup('ini', 'value1 section=global file=lookup.ini')}}" - value2_global: "{{lookup('ini', 'value2 section=global file=lookup.ini')}}" - value1_section1: "{{lookup('ini', 'value1 section=section1 file=lookup.ini')}}" - field_with_unicode: "{{lookup('ini', 'unicode section=global file=lookup.ini')}}" + value1_global: "{{lookup('ini', 'value1 section=global file=lookup.ini')}}" + value2_global: "{{lookup('ini', 'value2 section=global file=lookup.ini')}}" + value1_section1: "{{lookup('ini', 'value1 section=section1 file=lookup.ini')}}" + field_with_unicode: "{{lookup('ini', 'unicode section=global file=lookup.ini')}}" + - debug: var={{item}} with_items: [ 'value1_global', 'value2_global', 'value1_section1', 'field_with_unicode' ] + - assert: that: - "field_with_unicode == 'été indien où à château français ïîôû'" + - name: "read ini value from iso8859-15 file" set_fact: field_with_unicode: "{{lookup('ini', 'field_with_unicode section=global encoding=iso8859-1 file=lookup-8859-15.ini')}}" + - assert: that: - "field_with_unicode == 'été indien où à château français ïîôû'" + - name: "read ini value with section and regexp" set_fact: value_section: "{{lookup('ini', 'value[1-2] section=value_section file=lookup.ini re=true')}}" other_section: "{{lookup('ini', 'other[1-2] section=other_section file=lookup.ini re=true')}}" + - debug: var={{item}} with_items: [ 'value_section', 'other_section' ] + - assert: that: - "value_section == '1,2'" - "other_section == '4,5'" + - name: "Reading unknown value" set_fact: unknown: "{{lookup('ini', 'unknown default=unknown section=section1 file=lookup.ini')}}" + - debug: var=unknown + - assert: that: - 'unknown == "unknown"' + - name: "Looping over section section1" debug: msg="{{item}}" with_ini: value[1-2] section=section1 file=lookup.ini re=true register: _ + - assert: that: - '_.results.0.item == "section1/value1"' - '_.results.1.item == "section1/value2"' + - name: "Looping over section value_section" debug: msg="{{item}}" with_ini: value[1-2] section=value_section file=lookup.ini re=true register: _ + - assert: that: - '_.results.0.item == "1"' - '_.results.1.item == "2"' + - debug: msg="{{item}}" with_ini: value[1-2] section=section1 file=lookup.ini re=true register: _ + - assert: that: - '_.results.0.item == "section1/value1"' diff --git a/test/integration/targets/lookup_inventory_hostnames/inventory b/test/integration/targets/lookup_inventory_hostnames/inventory index 1b968af2..ca7234f7 100644 --- a/test/integration/targets/lookup_inventory_hostnames/inventory +++ b/test/integration/targets/lookup_inventory_hostnames/inventory @@ -1,6 +1,18 @@ +nogroup01 +nogroup02 +nogroup03 + [group01] test01 test05 test03 test02 test04 + +[group02] +test03 +test04 +test200 +test201 +test203 +test204 diff --git a/test/integration/targets/lookup_inventory_hostnames/main.yml b/test/integration/targets/lookup_inventory_hostnames/main.yml index afc09ea8..4b822e34 100644 --- a/test/integration/targets/lookup_inventory_hostnames/main.yml +++ b/test/integration/targets/lookup_inventory_hostnames/main.yml @@ -4,10 +4,20 @@ tasks: - set_fact: hosts_a: "{{ lookup('inventory_hostnames', 'group01', wantlist=true) }}" - - - set_fact: hosts_b: "{{ groups['group01'] }}" + exclude: "{{ lookup('inventory_hostnames', 'group01:!test03', wantlist=true) }}" + intersect: "{{ lookup('inventory_hostnames', 'group01:&group02', wantlist=true) }}" + nogroup: "{{ lookup('inventory_hostnames', 'nogroup01', wantlist=true) }}" + doesnotexist: "{{ lookup('inventory_hostnames', 'doesnotexist', wantlist=true) }}" + all: "{{ lookup('inventory_hostnames', 'all', wantlist=true) }}" + from_patterns: "{{ lookup('inventory_hostnames', 't?s?03', wantlist=True) }}" - assert: that: - hosts_a == hosts_b + - "'test03' not in exclude" + - intersect == ['test03', 'test04'] + - nogroup == ['nogroup01'] + - doesnotexist == [] + - all|length == 12 + - from_patterns == ['test03'] diff --git a/test/integration/targets/lookup_items/tasks/main.yml b/test/integration/targets/lookup_items/tasks/main.yml index 12df8d0b..15a2cf21 100644 --- a/test/integration/targets/lookup_items/tasks/main.yml +++ b/test/integration/targets/lookup_items/tasks/main.yml @@ -4,6 +4,12 @@ - 'foo' - 'bar' +- name: Undefined var, skipped + debug: + with_items: + - '{{ baz }}' + when: false + - debug: var=foo - debug: var=bar diff --git a/test/integration/targets/lookup_password/runme.sh b/test/integration/targets/lookup_password/runme.sh index ac2c1704..a3637a7e 100755 --- a/test/integration/targets/lookup_password/runme.sh +++ b/test/integration/targets/lookup_password/runme.sh @@ -2,7 +2,6 @@ set -eux -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh # Requirements have to be installed prior to running ansible-playbook diff --git a/test/integration/targets/lookup_subelements/tasks/main.yml b/test/integration/targets/lookup_subelements/tasks/main.yml index 5c706b27..9d93cf20 100644 --- a/test/integration/targets/lookup_subelements/tasks/main.yml +++ b/test/integration/targets/lookup_subelements/tasks/main.yml @@ -39,7 +39,186 @@ - name: verify with_subelements in subkeys results assert: that: - - _subelements_missing_subkeys.skipped is not defined + - _subelements_missing_subkeys is not skipped - _subelements_missing_subkeys.results|length == 2 - "_xk == 'k'" - "_xl == 'l'" + +# Example from the DOCUMENTATION block +- set_fact: + users: + - name: alice + authorized: + - /tmp/alice/onekey.pub + - /tmp/alice/twokey.pub + mysql: + password: mysql-password + hosts: + - "%" + - "127.0.0.1" + - "::1" + - "localhost" + privs: + - "*.*:SELECT" + - "DB1.*:ALL" + groups: + - wheel + - name: bob + authorized: + - /tmp/bob/id_rsa.pub + mysql: + password: other-mysql-password + hosts: + - "db1" + privs: + - "*.*:SELECT" + - "DB2.*:ALL" + - name: carol + skipped: true + authorized: + - /tmp/carol/id_rsa.pub + mysql: + password: third-mysql-password + hosts: + - "db9" + privs: + - "*.*:SELECT" + - "DB9.*:ALL" + + +- name: Ensure it errors properly with non-dict + set_fact: + err: "{{ lookup('subelements', 9001, 'groups', wantlist=true) }}" + ignore_errors: true + register: err1 + +- assert: + that: + - err1 is failed + - "'expects a dictionary' in err1.msg" + +- name: Ensure it errors properly when pointing to non-list + set_fact: + err: "{{ lookup('subelements', users, 'mysql.password', wantlist=true) }}" + ignore_errors: true + register: err2 + +- assert: + that: + - err2 is failed + - "'should point to a list' in err2.msg" + +- name: Ensure it properly skips missing keys + set_fact: + err: "{{ lookup('subelements', users, 'mysql.hosts.doesnotexist', wantlist=true) }}" + ignore_errors: true + register: err3 + +- assert: + that: + - err3 is failed + - "'should point to a dictionary' in err3.msg" + +- name: Ensure it properly skips missing keys + set_fact: + err: "{{ lookup('subelements', users, 'mysql.monkey', wantlist=true) }}" + ignore_errors: true + register: err4 + +- assert: + that: + - err4 is failed + - >- + "could not find 'monkey' key in iterated item" in err4.msg + +- assert: + that: + - "'{{ item.0.name }}' != 'carol'" + with_subelements: + - "{{ users }}" + - mysql.privs + +- name: Ensure it errors properly when optional arg is nonsensical + set_fact: + err: neverset + with_subelements: + - "{{ users }}" + - mysql.privs + - wolves + ignore_errors: true + register: err5 + +- assert: + that: + - err5 is failed + - "'the optional third item must be a dict' in err5.msg" + +- name: Ensure it errors properly when given way too many args + set_fact: + err: neverset + with_subelements: + - "{{ users }}" + - mysql.privs + - wolves + - foo + - bar + - baz + - bye now + ignore_errors: true + register: err6 + +- assert: + that: + - err6 is failed + - "'expects a list of two or three' in err6.msg" + +- name: Ensure it errors properly when second arg is invalid type + set_fact: + err: neverset + with_subelements: + - "{{ users }}" + - true + ignore_errors: true + register: err7 + +- assert: + that: + - err7 is failed + - "'second a string' in err7.msg" + +- name: Ensure it errors properly when first arg is invalid type + set_fact: + err: neverset + with_subelements: + - true + - "{{ users }}" + ignore_errors: true + register: err8 + +- assert: + that: + - err8 is failed + - "'first a dict or a list' in err8.msg" + +- set_fact: + empty_subelements: "{{ lookup('subelements', {'skipped': true}, 'mysql.hosts', wantlist=true) }}" + +- assert: + that: + - empty_subelements == [] + +- set_fact: + some_dict: + key: "{{ users[0] }}" + another: "{{ users[1] }}" + +- name: Ensure it works when we give a dict instead of a list + set_fact: "user_{{ item.0.name }}={{ item.1 }}" + with_subelements: + - "{{ some_dict }}" + - mysql.hosts + +- assert: + that: + - "'{{ user_alice }}' == 'localhost'" + - "'{{ user_bob }}' == 'db1'" diff --git a/test/integration/targets/lookup_varnames/aliases b/test/integration/targets/lookup_varnames/aliases new file mode 100644 index 00000000..45489be8 --- /dev/null +++ b/test/integration/targets/lookup_varnames/aliases @@ -0,0 +1,2 @@ +shippable/posix/group2 +skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller diff --git a/test/integration/targets/lookup_varnames/tasks/main.yml b/test/integration/targets/lookup_varnames/tasks/main.yml new file mode 100644 index 00000000..fec3efd5 --- /dev/null +++ b/test/integration/targets/lookup_varnames/tasks/main.yml @@ -0,0 +1,38 @@ +# Example copied from docs +- name: Set some variables + set_fact: + qz_1: hello + qz_2: world + qa_1: "I won't show" + qz_: "I won't show either" + +- name: Try various regexes and make sure they work + assert: + that: + - lookup('varnames', '^qz_.+', wantlist=True) == ['qz_1', 'qz_2'] + - lookup('varnames', '^qz_.+', '^qa.*', wantlist=True) == ['qz_1', 'qz_2', 'qa_1'] + - "'ansible_python_interpreter' in lookup('varnames', '^ansible_.*', wantlist=True)" + - lookup('varnames', '^doesnotexist.*', wantlist=True) == [] + - lookup('varnames', '^doesnotexist.*', '.*python_inter.*', wantlist=True) == ['ansible_python_interpreter'] + - lookup('varnames', '^q.*_\d', wantlist=True) == ['qz_1', 'qz_2', 'qa_1'] + - lookup('varnames', '^q.*_\d') == 'qz_1,qz_2,qa_1' + +- name: Make sure it fails successfully + set_fact: + fail1: "{{ lookup('varnames', True, wantlist=True) }}" + register: fail1res + ignore_errors: yes + +- name: Make sure it fails successfully + set_fact: + fail2: "{{ lookup('varnames', '*', wantlist=True) }}" + register: fail2res + ignore_errors: yes + +- assert: + that: + - fail1res is failed + - "'Invalid setting identifier' in fail1res.msg" + - fail2res is failed + - "'Unable to use' in fail2res.msg" + - "'nothing to repeat' in fail2res.msg" diff --git a/test/integration/targets/loops/tasks/main.yml b/test/integration/targets/loops/tasks/main.yml index 5575dd36..03c7c440 100644 --- a/test/integration/targets/loops/tasks/main.yml +++ b/test/integration/targets/loops/tasks/main.yml @@ -27,20 +27,27 @@ block: - name: Measure time before loop with .5s pause set_fact: - times: "{{times|default([]) + [ lookup('pipe','date +%s.%3N') ]}}" + times: "{{times|default([]) + [ now(fmt='%s.%f') ]}}" with_sequence: count=3 loop_control: pause: 0.6 - - name: ensure lag, since there is 3 rounds, and 0.5 seconds between, it should last 1.2 seconds, but allowing leeway due to CI lag - assert: - that: - - tdiff|float >= 1.2 - - tdiff|int < 3 - vars: + - name: Debug times var + debug: + var: times + + - vars: tdiff: '{{ times[2]|float - times[0]|float }}' - when: - - ansible_facts['distribution'] not in ("MacOSX", "FreeBSD") + block: + - name: Debug tdiff used in next task + debug: + msg: 'tdiff={{ tdiff }}' + + - name: ensure lag, since there is 3 rounds, and 0.5 seconds between, it should last 1.2 seconds, but allowing leeway due to CI lag + assert: + that: + - tdiff|float >= 1.2 + - tdiff|int < 3 # # Tests of loop syntax with args @@ -206,17 +213,26 @@ with_dict: "{{ a_list }}" register: with_dict_passed_a_list ignore_errors: True + vars: + a_list: + - 1 + - 2 - assert: that: - with_dict_passed_a_list is failed + - '"with_dict expects a dict" in with_dict_passed_a_list.msg' - debug: msg: "with_list passed a dict: {{item}}" with_list: "{{ a_dict }}" register: with_list_passed_a_dict ignore_errors: True + vars: + a_dict: + key: value - assert: that: - with_list_passed_a_dict is failed + - '"with_list expects a list" in with_list_passed_a_dict.msg' - debug: var: "item" diff --git a/test/integration/targets/meta_tasks/runme.sh b/test/integration/targets/meta_tasks/runme.sh index 3f456def..3ee419cb 100755 --- a/test/integration/targets/meta_tasks/runme.sh +++ b/test/integration/targets/meta_tasks/runme.sh @@ -8,6 +8,7 @@ for test_strategy in linear free; do grep -q "META: end_host conditional evaluated to false, continuing execution for testhost" <<< "$out" grep -q "META: ending play for testhost2" <<< "$out" + grep -q '"skip_reason": "end_host conditional evaluated to False, continuing execution for testhost"' <<< "$out" grep -q "play not ended for testhost" <<< "$out" grep -qv "play not ended for testhost2" <<< "$out" @@ -15,6 +16,7 @@ for test_strategy in linear free; do grep -q "META: end_host conditional evaluated to false, continuing execution for testhost" <<< "$out" grep -q "META: ending play for testhost2" <<< "$out" + grep -q '"skip_reason": "end_host conditional evaluated to False, continuing execution for testhost"' <<< "$out" grep -q "play not ended for testhost" <<< "$out" grep -qv "play not ended for testhost2" <<< "$out" done diff --git a/test/integration/targets/module_precedence/lib_no_extension/ping b/test/integration/targets/module_precedence/lib_no_extension/ping index e30706e8..a28f4699 100644 --- a/test/integration/targets/module_precedence/lib_no_extension/ping +++ b/test/integration/targets/module_precedence/lib_no_extension/ping @@ -19,6 +19,8 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], diff --git a/test/integration/targets/module_precedence/lib_with_extension/ping.py b/test/integration/targets/module_precedence/lib_with_extension/ping.py index e30706e8..a28f4699 100644 --- a/test/integration/targets/module_precedence/lib_with_extension/ping.py +++ b/test/integration/targets/module_precedence/lib_with_extension/ping.py @@ -19,6 +19,8 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], diff --git a/test/integration/targets/module_precedence/multiple_roles/bar/library/ping.py b/test/integration/targets/module_precedence/multiple_roles/bar/library/ping.py index e7776001..98ef7b44 100644 --- a/test/integration/targets/module_precedence/multiple_roles/bar/library/ping.py +++ b/test/integration/targets/module_precedence/multiple_roles/bar/library/ping.py @@ -19,6 +19,8 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], diff --git a/test/integration/targets/module_precedence/multiple_roles/foo/library/ping.py b/test/integration/targets/module_precedence/multiple_roles/foo/library/ping.py index a6d153ba..8860b7aa 100644 --- a/test/integration/targets/module_precedence/multiple_roles/foo/library/ping.py +++ b/test/integration/targets/module_precedence/multiple_roles/foo/library/ping.py @@ -19,6 +19,8 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], diff --git a/test/integration/targets/module_precedence/roles_no_extension/foo/library/ping b/test/integration/targets/module_precedence/roles_no_extension/foo/library/ping index a6d153ba..8860b7aa 100644 --- a/test/integration/targets/module_precedence/roles_no_extension/foo/library/ping +++ b/test/integration/targets/module_precedence/roles_no_extension/foo/library/ping @@ -19,6 +19,8 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], diff --git a/test/integration/targets/module_precedence/roles_with_extension/foo/library/ping.py b/test/integration/targets/module_precedence/roles_with_extension/foo/library/ping.py index a6d153ba..8860b7aa 100644 --- a/test/integration/targets/module_precedence/roles_with_extension/foo/library/ping.py +++ b/test/integration/targets/module_precedence/roles_with_extension/foo/library/ping.py @@ -19,6 +19,8 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], diff --git a/test/integration/targets/module_utils/collections/ansible_collections/testns/testcoll/plugins/module_utils/legit.py b/test/integration/targets/module_utils/collections/ansible_collections/testns/testcoll/plugins/module_utils/legit.py new file mode 100644 index 00000000..b9d63482 --- /dev/null +++ b/test/integration/targets/module_utils/collections/ansible_collections/testns/testcoll/plugins/module_utils/legit.py @@ -0,0 +1,6 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +def importme(): + return "successfully imported from testns.testcoll" diff --git a/test/integration/targets/module_utils/library/test.py b/test/integration/targets/module_utils/library/test.py index fbb7e6e2..fb6c8a81 100644 --- a/test/integration/targets/module_utils/library/test.py +++ b/test/integration/targets/module_utils/library/test.py @@ -2,6 +2,8 @@ # Most of these names are only available via PluginLoader so pylint doesn't # know they exist # pylint: disable=no-name-in-module +__metaclass__ = type + results = {} # Test import with no from diff --git a/test/integration/targets/module_utils/library/test_datetime.py b/test/integration/targets/module_utils/library/test_datetime.py new file mode 100644 index 00000000..493a186a --- /dev/null +++ b/test/integration/targets/module_utils/library/test_datetime.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +# Most of these names are only available via PluginLoader so pylint doesn't +# know they exist +# pylint: disable=no-name-in-module +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.basic import AnsibleModule +import datetime + +module = AnsibleModule(argument_spec=dict( + datetime=dict(type=str, required=True), + date=dict(type=str, required=True), +)) +result = { + 'datetime': datetime.datetime.strptime(module.params.get('datetime'), '%Y-%m-%dT%H:%M:%S'), + 'date': datetime.datetime.strptime(module.params.get('date'), '%Y-%m-%d').date(), +} +module.exit_json(**result) diff --git a/test/integration/targets/module_utils/library/test_env_override.py b/test/integration/targets/module_utils/library/test_env_override.py index 94e3051b..ebfb5ddf 100644 --- a/test/integration/targets/module_utils/library/test_env_override.py +++ b/test/integration/targets/module_utils/library/test_env_override.py @@ -2,6 +2,9 @@ # Most of these names are only available via PluginLoader so pylint doesn't # know they exist # pylint: disable=no-name-in-module +from __future__ import absolute_import, division, print_function +__metaclass__ = type + from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.json_utils import data from ansible.module_utils.mork import data as mork_data diff --git a/test/integration/targets/module_utils/library/test_failure.py b/test/integration/targets/module_utils/library/test_failure.py index e1a87c2e..efb3ddae 100644 --- a/test/integration/targets/module_utils/library/test_failure.py +++ b/test/integration/targets/module_utils/library/test_failure.py @@ -1,14 +1,14 @@ #!/usr/bin/python +from __future__ import absolute_import, division, print_function +__metaclass__ = type results = {} # Test that we are rooted correctly # Following files: # module_utils/yak/zebra/foo.py -try: - from ansible.module_utils.zebra import foo - results['zebra'] = foo.data -except ImportError: - results['zebra'] = 'Failed in module as expected but incorrectly did not fail in AnsiballZ construction' +from ansible.module_utils.zebra import foo + +results['zebra'] = foo.data from ansible.module_utils.basic import AnsibleModule AnsibleModule(argument_spec=dict()).exit_json(**results) diff --git a/test/integration/targets/module_utils/library/test_optional.py b/test/integration/targets/module_utils/library/test_optional.py new file mode 100644 index 00000000..4d0225d9 --- /dev/null +++ b/test/integration/targets/module_utils/library/test_optional.py @@ -0,0 +1,84 @@ +#!/usr/bin/python +# Most of these names are only available via PluginLoader so pylint doesn't +# know they exist +# pylint: disable=no-name-in-module +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.basic import AnsibleModule + +# internal constants to keep pylint from griping about constant-valued conditionals +_private_false = False +_private_true = True + +# module_utils import statements nested below any block are considered optional "best-effort" for AnsiballZ to include. +# test a number of different import shapes and nesting types to exercise this... + +# first, some nested imports that should succeed... +try: + from ansible.module_utils.urls import fetch_url as yep1 +except ImportError: + yep1 = None + +try: + import ansible.module_utils.common.text.converters as yep2 +except ImportError: + yep2 = None + +try: + # optional import from a legit collection + from ansible_collections.testns.testcoll.plugins.module_utils.legit import importme as yep3 +except ImportError: + yep3 = None + +# and a bunch that should fail to be found, but not break the module_utils payload build in the process... +try: + from ansible.module_utils.bogus import fromnope1 +except ImportError: + fromnope1 = None + +if _private_false: + from ansible.module_utils.alsobogus import fromnope2 +else: + fromnope2 = None + +try: + import ansible.module_utils.verybogus + nope1 = ansible.module_utils.verybogus +except ImportError: + nope1 = None + +# deepish nested with multiple block types- make sure the AST walker made it all the way down +try: + if _private_true: + if _private_true: + if _private_true: + if _private_true: + try: + import ansible.module_utils.stillbogus as nope2 + except ImportError: + raise +except ImportError: + nope2 = None + +try: + # optional import from a valid collection with an invalid package + from ansible_collections.testns.testcoll.plugins.module_utils.bogus import collnope1 +except ImportError: + collnope1 = None + +try: + # optional import from a bogus collection + from ansible_collections.bogusns.boguscoll.plugins.module_utils.bogus import collnope2 +except ImportError: + collnope2 = None + +module = AnsibleModule(argument_spec={}) + +if not all([yep1, yep2, yep3]): + module.fail_json(msg='one or more existing optional imports did not resolve') + +if any([fromnope1, fromnope2, nope1, nope2, collnope1, collnope2]): + module.fail_json(msg='one or more missing optional imports resolved unexpectedly') + +module.exit_json(msg='all missing optional imports behaved as expected') diff --git a/test/integration/targets/module_utils/library/test_override.py b/test/integration/targets/module_utils/library/test_override.py index 9ff54bf9..b4e21cdd 100644 --- a/test/integration/targets/module_utils/library/test_override.py +++ b/test/integration/targets/module_utils/library/test_override.py @@ -1,4 +1,7 @@ #!/usr/bin/python +from __future__ import absolute_import, division, print_function +__metaclass__ = type + from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.facts import data diff --git a/test/integration/targets/module_utils/module_utils_test.yml b/test/integration/targets/module_utils/module_utils_test.yml index 943bf4ee..96b2a9e0 100644 --- a/test/integration/targets/module_utils/module_utils_test.yml +++ b/test/integration/targets/module_utils/module_utils_test.yml @@ -43,7 +43,6 @@ ignore_errors: True register: result - - debug: var=result - name: Make sure we failed in AnsiBallZ assert: that: @@ -60,3 +59,60 @@ that: - result.deprecations[0].msg == "Alias 'baz' is deprecated. See the module docs for more information" - result.deprecations[0].version == '9.99' + + - block: + - name: Get a string with a \0 in it + command: echo -e 'hi\0foo' + register: string_with_null + + - name: Use the null string as a module parameter + lineinfile: + path: "{{ output_dir }}/nulltest" + line: "{{ string_with_null.stdout }}" + create: yes + ignore_errors: yes + register: nulltest + + - name: See if the file exists + stat: + path: "{{ output_dir }}/nulltest" + register: nullstat + + - assert: + that: + - nulltest is failed + - nulltest.msg_to_log.startswith('Invoked ') + - nulltest.msg.startswith('Failed to log to syslog') + # Conditionalize this, because when we log with something other than + # syslog, it's probably successful and these assertions will fail. + when: nulltest is failed + + # Ensure we fail out early and don't actually run the module if logging + # failed. + - assert: + that: + - nullstat.stat.exists == nulltest is successful + always: + - file: + path: "{{ output_dir }}/nulltest" + state: absent + + - name: Test that date and datetime in module output works + test_datetime: + date: "2020-10-05" + datetime: "2020-10-05T10:05:05" + register: datetimetest + + - assert: + that: + - datetimetest.date == '2020-10-05' + - datetimetest.datetime == '2020-10-05T10:05:05' + + - name: Test that optional imports behave properly + test_optional: + register: optionaltest + + - assert: + that: + - optionaltest is success + - optionaltest.msg == 'all missing optional imports behaved as expected' diff --git a/test/integration/targets/module_utils/module_utils_vvvvv.yml b/test/integration/targets/module_utils/module_utils_vvvvv.yml index 1fd91d25..6a9f9201 100644 --- a/test/integration/targets/module_utils/module_utils_vvvvv.yml +++ b/test/integration/targets/module_utils/module_utils_vvvvv.yml @@ -1,6 +1,9 @@ - hosts: testhost gather_facts: no tasks: + - name: Use a specially crafted module to see if things were imported correctly + test: + # Invocation usually is output with 3vs or more, our callback plugin displays it anyway - name: Check no_log invocation results command: ansible-playbook -i {{ inventory_file }} module_utils_test_no_log.yml diff --git a/test/integration/targets/module_utils/runme.sh b/test/integration/targets/module_utils/runme.sh index f25dba63..801734f9 100755 --- a/test/integration/targets/module_utils/runme.sh +++ b/test/integration/targets/module_utils/runme.sh @@ -8,7 +8,8 @@ ANSIBLE_ROLES_PATH=../ ansible-playbook module_utils_basic_setcwd.yml -i ../../i # doesn't traceback with unicode in the custom module_utils directory path. ansible-playbook module_utils_vvvvv.yml -i ../../inventory -vvvvv "$@" -ansible-playbook module_utils_test.yml -i ../../inventory -v "$@" +ansible-playbook module_utils_test.yml -i ../../inventory -e output_dir="$OUTPUT_DIR" -v "$@" + ANSIBLE_MODULE_UTILS=other_mu_dir ansible-playbook module_utils_envvar.yml -i ../../inventory -v "$@" ansible-playbook module_utils_common_dict_transformation.yml -i ../../inventory "$@" diff --git a/test/integration/targets/module_utils_respawn/aliases b/test/integration/targets/module_utils_respawn/aliases new file mode 100644 index 00000000..a6dafcf8 --- /dev/null +++ b/test/integration/targets/module_utils_respawn/aliases @@ -0,0 +1 @@ +shippable/posix/group1 diff --git a/test/integration/targets/module_utils_respawn/library/respawnme.py b/test/integration/targets/module_utils_respawn/library/respawnme.py new file mode 100644 index 00000000..6471dba4 --- /dev/null +++ b/test/integration/targets/module_utils_respawn/library/respawnme.py @@ -0,0 +1,44 @@ +#!/usr/bin/python + +# Copyright: (c) 2021, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import sys + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.respawn import respawn_module, has_respawned + + +def main(): + mod = AnsibleModule(argument_spec=dict( + mode=dict(required=True, choices=['multi_respawn', 'no_respawn', 'respawn']) + )) + + # just return info about what interpreter we're currently running under + if mod.params['mode'] == 'no_respawn': + mod.exit_json(interpreter_path=sys.executable) + elif mod.params['mode'] == 'respawn': + if not has_respawned(): + new_interpreter = os.path.join(mod.tmpdir, 'anotherpython') + os.symlink(sys.executable, new_interpreter) + respawn_module(interpreter_path=new_interpreter) + + # respawn should always exit internally- if we continue executing here, it's a bug + raise Exception('FAIL, should never reach this line') + else: + # return the current interpreter, as well as a signal that we created a different one + mod.exit_json(created_interpreter=sys.executable, interpreter_path=sys.executable) + elif mod.params['mode'] == 'multi_respawn': + # blindly respawn with the current interpreter, the second time should bomb + respawn_module(sys.executable) + + # shouldn't be any way for us to fall through, but just in case, that's also a bug + raise Exception('FAIL, should never reach this code') + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_utils_respawn/tasks/main.yml b/test/integration/targets/module_utils_respawn/tasks/main.yml new file mode 100644 index 00000000..246c8f74 --- /dev/null +++ b/test/integration/targets/module_utils_respawn/tasks/main.yml @@ -0,0 +1,24 @@ +- name: collect the default interpreter + respawnme: + mode: no_respawn + register: default_python + +- name: cause a respawn + respawnme: + mode: respawn + register: respawned_python + +- name: cause multiple respawns (should fail) + respawnme: + mode: multi_respawn + ignore_errors: true + register: multi_respawn + +- assert: + that: + # the respawn task should report the path of the interpreter it created, and that it's running under the new interpreter + - respawned_python.created_interpreter == respawned_python.interpreter_path + # ensure that the respawned interpreter is not the same as the default + - default_python.interpreter_path != respawned_python.interpreter_path + # multiple nested respawns should fail + - multi_respawn is failed and multi_respawn.module_stderr is search('has already been respawned') diff --git a/test/integration/targets/module_utils_selinux/aliases b/test/integration/targets/module_utils_selinux/aliases new file mode 100644 index 00000000..aab3ff52 --- /dev/null +++ b/test/integration/targets/module_utils_selinux/aliases @@ -0,0 +1,6 @@ +shippable/posix/group1 +skip/aix +skip/osx +skip/macos +skip/freebsd +skip/docker diff --git a/test/integration/targets/module_utils_selinux/tasks/main.yml b/test/integration/targets/module_utils_selinux/tasks/main.yml new file mode 100644 index 00000000..c599377b --- /dev/null +++ b/test/integration/targets/module_utils_selinux/tasks/main.yml @@ -0,0 +1,37 @@ +- name: check selinux config + shell: | + command -v getenforce && + getenforce | grep -E 'Enforcing|Permissive' + ignore_errors: yes + register: selinux_state + +- name: explicitly collect selinux facts + setup: + gather_subset: + - '!all' + - '!any' + - selinux + register: selinux_facts + +- set_fact: + selinux_policytype: "unknown" + +- name: check selinux policy type + shell: grep '^SELINUXTYPE=' /etc/selinux/config | cut -d'=' -f2 + register: r + +- set_fact: + selinux_policytype: "{{ r.stdout_lines[0] }}" + when: r.changed + +- assert: + that: + - selinux_facts is success and selinux_facts.ansible_facts.ansible_selinux is defined + - (selinux_facts.ansible_facts.ansible_selinux.status in ['disabled', 'Missing selinux Python library'] if selinux_state is not success else True) + - (selinux_facts.ansible_facts.ansible_selinux.status == 'enabled' if selinux_state is success else True) + - (selinux_facts.ansible_facts.ansible_selinux.mode in ['enforcing', 'permissive'] if selinux_state is success else True) + - (selinux_facts.ansible_facts.ansible_selinux.type == selinux_policytype if selinux_state is success else True) + +- name: run selinux tests + include_tasks: selinux.yml + when: selinux_state is success diff --git a/test/integration/targets/module_utils_selinux/tasks/selinux.yml b/test/integration/targets/module_utils_selinux/tasks/selinux.yml new file mode 100644 index 00000000..6a2b159c --- /dev/null +++ b/test/integration/targets/module_utils_selinux/tasks/selinux.yml @@ -0,0 +1,93 @@ +- name: collect selinux facts + setup: + gather_subset: ['!all', '!min', selinux] + register: fact_output + +- debug: + var: fact_output + +- name: create tempdir container in home + file: + path: ~/.selinux_tmp + state: directory + +- name: create tempdir + tempfile: + path: ~/.selinux_tmp + prefix: selinux_test + state: directory + register: tempdir + +- name: ls -1Zd tempdir to capture context from FS + shell: ls -1Zd '{{ tempdir.path }}' + register: tempdir_context_output + +- name: create a file under the tempdir with no context info specified (it should inherit parent context) + file: + path: '{{ tempdir.path }}/file_inherited_context' + state: touch + register: file_inherited_context + +- name: ls -1Z inherited file to capture context from FS + shell: ls -1Z '{{ tempdir.path }}/file_inherited_context' + register: inherited_context_output + +- name: copy the file with explicit overrides on all context values + copy: + remote_src: yes + src: '{{ tempdir.path }}/file_inherited_context' + dest: '{{ tempdir.path }}/file_explicit_context' + seuser: system_u + serole: system_r + setype: user_tmp_t + # default configs don't have MLS levels defined, so we can't test that yet + # selevel: s1 + register: file_explicit_context + +- name: ls -1Z explicit file to capture context from FS + shell: ls -1Z '{{ tempdir.path }}/file_explicit_context' + register: explicit_context_output + +- name: alter the tempdir context + file: + path: '{{ tempdir.path }}' + seuser: system_u + serole: system_r + setype: user_tmp_t + # default configs don't have MLS levels defined, so we can't test that yet + # selevel: s1 + register: tempdir_altered + +- name: ls -1Z tempdir to capture context from FS + shell: ls -1Z '{{ tempdir.path }}/file_explicit_context' + register: tempdir_altered_context_output + +- name: copy the explicit context file with default overrides on all context values + copy: + remote_src: yes + src: '{{ tempdir.path }}/file_explicit_context' + dest: '{{ tempdir.path }}/file_default_context' + seuser: _default + serole: _default + setype: _default + selevel: _default + register: file_default_context + +- name: see what matchpathcon thinks the context of default_file_context should be + shell: matchpathcon {{ file_default_context.dest }} | awk '{ print $2 }' + register: expected_default_context + +- assert: + that: + - fact_output.ansible_facts.ansible_selinux.config_mode in ['enforcing','permissive'] + - fact_output.ansible_facts.ansible_selinux.mode in ['enforcing','permissive'] + - fact_output.ansible_facts.ansible_selinux.status == 'enabled' + - fact_output.ansible_facts.ansible_selinux_python_present == true + # assert that secontext is set on the file results (injected by basic.py, for better or worse) + - tempdir.secontext is match('.+:.+:.+') and tempdir.secontext in tempdir_context_output.stdout + - file_inherited_context.secontext is match('.+:.+:.+') and file_inherited_context.secontext in inherited_context_output.stdout + - file_inherited_context.secontext == tempdir.secontext # should've been inherited from the parent dir since not set explicitly + - file_explicit_context.secontext == 'system_u:system_r:user_tmp_t:s0' and file_explicit_context.secontext in explicit_context_output.stdout + - tempdir_altered.secontext == 'system_u:system_r:user_tmp_t:s0' and tempdir_altered.secontext in tempdir_altered_context_output.stdout + # the one with reset defaults should match the original tempdir context, not the altered one (ie, it was set by the original policy context, not inherited from the parent dir) + - file_default_context.secontext == expected_default_context.stdout_lines[0] diff --git a/test/integration/targets/module_utils_urls/aliases b/test/integration/targets/module_utils_urls/aliases new file mode 100644 index 00000000..3c4491b0 --- /dev/null +++ b/test/integration/targets/module_utils_urls/aliases @@ -0,0 +1,2 @@ +shippable/posix/group1 +needs/httptester diff --git a/test/integration/targets/module_utils_urls/library/test_peercert.py b/test/integration/targets/module_utils_urls/library/test_peercert.py new file mode 100644 index 00000000..ecb7d204 --- /dev/null +++ b/test/integration/targets/module_utils_urls/library/test_peercert.py @@ -0,0 +1,98 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: test_perrcert +short_description: Test getting the peer certificate of a HTTP response +description: Test getting the peer certificate of a HTTP response. +options: + url: + description: The endpoint to get the peer cert for + required: true + type: str +author: +- Ansible Project +''' + +EXAMPLES = r''' +# +''' + +RETURN = r''' +# +''' + +import base64 + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_text +from ansible.module_utils.urls import getpeercert, Request + + +def get_x509_shorthand(name, value): + prefix = { + 'countryName': 'C', + 'stateOrProvinceName': 'ST', + 'localityName': 'L', + 'organizationName': 'O', + 'commonName': 'CN', + 'organizationalUnitName': 'OU', + }[name] + + return '%s=%s' % (prefix, value) + + +def main(): + module_args = dict( + url=dict(type='str', required=True), + ) + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + result = { + 'changed': False, + 'cert': None, + 'raw_cert': None, + } + + req = Request().get(module.params['url']) + try: + cert = getpeercert(req) + b_cert = getpeercert(req, binary_form=True) + + finally: + req.close() + + if cert: + processed_cert = { + 'issuer': '', + 'not_after': cert.get('notAfter', None), + 'not_before': cert.get('notBefore', None), + 'serial_number': cert.get('serialNumber', None), + 'subject': '', + 'version': cert.get('version', None), + } + + for field in ['issuer', 'subject']: + field_values = [] + for x509_part in cert.get(field, []): + field_values.append(get_x509_shorthand(x509_part[0][0], x509_part[0][1])) + + processed_cert[field] = ",".join(field_values) + + result['cert'] = processed_cert + + if b_cert: + result['raw_cert'] = to_text(base64.b64encode(b_cert)) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/incidental_win_psexec/meta/main.yml b/test/integration/targets/module_utils_urls/meta/main.yml index 9f37e96c..f3a332d5 100644 --- a/test/integration/targets/incidental_win_psexec/meta/main.yml +++ b/test/integration/targets/module_utils_urls/meta/main.yml @@ -1,2 +1,3 @@ dependencies: +- prepare_http_tests - setup_remote_tmp_dir diff --git a/test/integration/targets/module_utils_urls/tasks/main.yml b/test/integration/targets/module_utils_urls/tasks/main.yml new file mode 100644 index 00000000..ca76a7d5 --- /dev/null +++ b/test/integration/targets/module_utils_urls/tasks/main.yml @@ -0,0 +1,32 @@ +- name: get peercert for HTTP connection + test_peercert: + url: http://{{ httpbin_host }}/get + register: cert_http + +- name: assert get peercert for HTTP connection + assert: + that: + - cert_http.raw_cert == None + +- name: get peercert for HTTPS connection + test_peercert: + url: https://{{ httpbin_host }}/get + register: cert_https + +# Alpine does not have openssl, just make sure the text was actually set instead +- name: check if openssl is installed + command: which openssl + ignore_errors: yes + register: openssl + +- name: get actual certificate from endpoint + shell: echo | openssl s_client -connect {{ httpbin_host }}:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' + register: cert_https_actual + changed_when: no + when: openssl is successful + +- name: assert get peercert for HTTPS connection + assert: + that: + - cert_https.raw_cert != None + - openssl is failed or cert_https.raw_cert == cert_https_actual.stdout_lines[1:-1] | join("") diff --git a/test/integration/targets/old_style_cache_plugins/runme.sh b/test/integration/targets/old_style_cache_plugins/runme.sh index 86d2433b..13911bd5 100755 --- a/test/integration/targets/old_style_cache_plugins/runme.sh +++ b/test/integration/targets/old_style_cache_plugins/runme.sh @@ -2,7 +2,6 @@ set -eux -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh # Run test if dependencies are installed diff --git a/test/integration/targets/package/tasks/main.yml b/test/integration/targets/package/tasks/main.yml index 4fc3a8a6..853b4711 100644 --- a/test/integration/targets/package/tasks/main.yml +++ b/test/integration/targets/package/tasks/main.yml @@ -74,6 +74,38 @@ ## package ## +# Verify module_defaults for package and the underlying module are utilized +# Validates: https://github.com/ansible/ansible/issues/72918 +- block: + # 'name' is required + - name: install apt with package defaults + package: + module_defaults: + package: + name: apt + state: present + + - name: install apt with dnf defaults (auto) + package: + module_defaults: + dnf: + name: apt + state: present + + - name: install apt with dnf defaults (use dnf) + package: + use: dnf + module_defaults: + dnf: + name: apt + state: present + always: + - name: remove apt + dnf: + name: apt + state: absent + when: ansible_distribution == "Fedora" + - name: define distros to attempt installing at on set_fact: package_distros: 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 0fb1337e..d225c0f9 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 @@ -202,3 +202,16 @@ - should_not_omit_1 is defined - should_not_omit_2 is defined - should_not_omit_3 == "__omit_place_holder__afb6b9bc3d20bfeaa00a1b23a5930f89" + +- name: Ensure module names are stripped of extra spaces (local_action) + local_action: set_fact b="b" + register: la_set_fact_b + +- name: Ensure module names are stripped of extra spaces (action) + action: set_fact c="c" + register: la_set_fact_c + +- assert: + that: + - b == "b" + - c == "c" diff --git a/test/integration/targets/pause/test-pause.py b/test/integration/targets/pause/test-pause.py index 7b37c666..3703470d 100755 --- a/test/integration/targets/pause/test-pause.py +++ b/test/integration/targets/pause/test-pause.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import os import pexpect import sys @@ -271,3 +274,19 @@ pause_test.send('supersecretpancakes') pause_test.send('\r') pause_test.expect(pexpect.EOF) pause_test.close() + + +# Test that enter presses may not continue the play when a timeout is set. + +pause_test = pexpect.spawn( + 'ansible-playbook', + args=["pause-3.yml"] + args, + timeout=10, + env=os.environ +) + +pause_test.logfile = log_buffer +pause_test.expect(r"\(ctrl\+C then 'C' = continue early, ctrl\+C then 'A' = abort\)") +pause_test.send('\r') +pause_test.expect(pexpect.EOF) +pause_test.close() diff --git a/test/integration/targets/pip/files/ansible_test_pip_chdir/__init__.py b/test/integration/targets/pip/files/ansible_test_pip_chdir/__init__.py index c8a79430..5d1f9aec 100644 --- a/test/integration/targets/pip/files/ansible_test_pip_chdir/__init__.py +++ b/test/integration/targets/pip/files/ansible_test_pip_chdir/__init__.py @@ -1,2 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + def main(): print("success") diff --git a/test/integration/targets/pip/files/setup.py b/test/integration/targets/pip/files/setup.py index 094064b7..aaf21875 100755 --- a/test/integration/targets/pip/files/setup.py +++ b/test/integration/targets/pip/files/setup.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + from setuptools import setup, find_packages setup( diff --git a/test/integration/targets/pip/tasks/main.yml b/test/integration/targets/pip/tasks/main.yml index c0a36c43..e669d297 100644 --- a/test/integration/targets/pip/tasks/main.yml +++ b/test/integration/targets/pip/tasks/main.yml @@ -31,7 +31,7 @@ package: name: git state: present - when: ansible_distribution != "MacOSX" + when: ansible_distribution not in ["MacOSX", "Alpine"] register: git_install - name: ensure wheel is installed diff --git a/test/integration/targets/plugin_config_for_inventory/cache_plugins/none.py b/test/integration/targets/plugin_config_for_inventory/cache_plugins/none.py new file mode 100644 index 00000000..62a91c85 --- /dev/null +++ b/test/integration/targets/plugin_config_for_inventory/cache_plugins/none.py @@ -0,0 +1,62 @@ +# (c) 2014, Brian Coca, Josh Drake, et al +# (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.cache import BaseCacheModule + +DOCUMENTATION = ''' + cache: none + short_description: write-only cache (no cache) + description: + - No caching at all + version_added: historical + author: core team (@ansible-core) + options: + _timeout: + default: 86400 + description: Expiration timeout for the cache plugin data + env: + - name: ANSIBLE_CACHE_PLUGIN_TIMEOUT + ini: + - key: fact_caching_timeout + section: defaults + type: integer +''' + + +class CacheModule(BaseCacheModule): + def __init__(self, *args, **kwargs): + super(CacheModule, self).__init__(*args, **kwargs) + self.empty = {} + self._timeout = self.get_option('_timeout') + + def get(self, key): + return self.empty.get(key) + + def set(self, key, value): + return value + + def keys(self): + return self.empty.keys() + + def contains(self, key): + return key in self.empty + + def delete(self, key): + del self.emtpy[key] + + def flush(self): + self.empty = {} + + def copy(self): + return self.empty.copy() + + def __getstate__(self): + return self.copy() + + def __setstate__(self, data): + self.empty = data diff --git a/test/integration/targets/plugin_config_for_inventory/config_with_parameter.yml b/test/integration/targets/plugin_config_for_inventory/config_with_parameter.yml index 8ff39884..b9e367b8 100644 --- a/test/integration/targets/plugin_config_for_inventory/config_with_parameter.yml +++ b/test/integration/targets/plugin_config_for_inventory/config_with_parameter.yml @@ -1,3 +1,5 @@ plugin: test_inventory departments: - paris +cache: yes +cache_timeout: 0 diff --git a/test/integration/targets/plugin_config_for_inventory/runme.sh b/test/integration/targets/plugin_config_for_inventory/runme.sh index 119a073a..2a223258 100755 --- a/test/integration/targets/plugin_config_for_inventory/runme.sh +++ b/test/integration/targets/plugin_config_for_inventory/runme.sh @@ -14,3 +14,9 @@ ansible-inventory --list -i ./config_without_parameter.yml --export | \ ansible-inventory --list -i ./config_with_parameter.yml --export | \ env python -c "import json, sys; inv = json.loads(sys.stdin.read()); \ assert set(inv['_meta']['hostvars']['test_host']['departments']) == set(['paris'])" + +export ANSIBLE_CACHE_PLUGINS=cache_plugins/ +export ANSIBLE_CACHE_PLUGIN=none +ansible-inventory --list -i ./config_with_parameter.yml --export | \ + env python -c "import json, sys; inv = json.loads(sys.stdin.read()); \ + assert inv['_meta']['hostvars']['test_host']['given_timeout'] == inv['_meta']['hostvars']['test_host']['cache_timeout']" diff --git a/test/integration/targets/plugin_config_for_inventory/test_inventory.py b/test/integration/targets/plugin_config_for_inventory/test_inventory.py index 63ed0cc2..f937c038 100644 --- a/test/integration/targets/plugin_config_for_inventory/test_inventory.py +++ b/test/integration/targets/plugin_config_for_inventory/test_inventory.py @@ -18,6 +18,31 @@ DOCUMENTATION = ''' - seine-et-marne - haute-garonne required: False + cache: + description: cache + type: bool + default: false + required: False + cache_plugin: + description: cache plugin + type: str + default: none + required: False + cache_timeout: + description: test cache parameter + type: integer + default: 7 + required: False + cache_connection: + description: cache connection + type: str + default: /tmp/foo + required: False + cache_prefix: + description: cache prefix + type: str + default: prefix_ + required: False ''' EXAMPLES = ''' @@ -50,3 +75,10 @@ class InventoryModule(BaseInventoryPlugin): self.inventory.add_group(group) self.inventory.add_host(group=group, host=host) self.inventory.set_variable(host, 'departments', departments) + + # Ensure the timeout we're given gets sent to the cache plugin + if self.get_option('cache'): + given_timeout = self.get_option('cache_timeout') + cache_timeout = self._cache._plugin.get_option('_timeout') + self.inventory.set_variable(host, 'given_timeout', given_timeout) + self.inventory.set_variable(host, 'cache_timeout', cache_timeout) diff --git a/test/integration/targets/prepare_http_tests/defaults/main.yml b/test/integration/targets/prepare_http_tests/defaults/main.yml index a1e5b8d1..217b3db5 100644 --- a/test/integration/targets/prepare_http_tests/defaults/main.yml +++ b/test/integration/targets/prepare_http_tests/defaults/main.yml @@ -1,4 +1,5 @@ badssl_host: wrong.host.badssl.com +self_signed_host: self-signed.ansible.http.tests httpbin_host: httpbin.org sni_host: ci-files.testing.ansible.com badssl_host_substring: wrong.host.badssl.com diff --git a/test/integration/targets/prepare_http_tests/handlers/main.yml b/test/integration/targets/prepare_http_tests/handlers/main.yml new file mode 100644 index 00000000..172cab73 --- /dev/null +++ b/test/integration/targets/prepare_http_tests/handlers/main.yml @@ -0,0 +1,4 @@ +- name: Remove python gssapi + pip: + name: gssapi + state: absent diff --git a/test/integration/targets/prepare_http_tests/library/httptester_kinit.py b/test/integration/targets/prepare_http_tests/library/httptester_kinit.py new file mode 100644 index 00000000..4f7b7ad4 --- /dev/null +++ b/test/integration/targets/prepare_http_tests/library/httptester_kinit.py @@ -0,0 +1,138 @@ +#!/usr/bin/python + +# Copyright: (c) 2020, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: httptester_kinit +short_description: Get Kerberos ticket +description: Get Kerberos ticket using kinit non-interactively. +options: + username: + description: The username to get the ticket for. + required: true + type: str + password: + description: The password for I(username). + required; true + type: str +author: +- Ansible Project +''' + +EXAMPLES = r''' +# +''' + +RETURN = r''' +# +''' + +import contextlib +import errno +import os +import subprocess + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_bytes, to_text + +try: + import configparser +except ImportError: + import ConfigParser as configparser + + +@contextlib.contextmanager +def env_path(name, value, default_value): + """ Adds a value to a PATH-like env var and preserve the existing value if present. """ + orig_value = os.environ.get(name, None) + os.environ[name] = '%s:%s' % (value, orig_value or default_value) + try: + yield + + finally: + if orig_value: + os.environ[name] = orig_value + + else: + del os.environ[name] + + +@contextlib.contextmanager +def krb5_conf(module, config): + """ Runs with a custom krb5.conf file that extends the existing config if present. """ + if config: + ini_config = configparser.ConfigParser() + for section, entries in config.items(): + ini_config.add_section(section) + for key, value in entries.items(): + ini_config.set(section, key, value) + + config_path = os.path.join(module.tmpdir, 'krb5.conf') + with open(config_path, mode='wt') as config_fd: + ini_config.write(config_fd) + + with env_path('KRB5_CONFIG', config_path, '/etc/krb5.conf'): + yield + + else: + yield + + +def main(): + module_args = dict( + username=dict(type='str', required=True), + password=dict(type='str', required=True, no_log=True), + ) + module = AnsibleModule( + argument_spec=module_args, + required_together=[('username', 'password')], + ) + + # Heimdal has a few quirks that we want to paper over in this module + # 1. KRB5_TRACE does not work in any released version (<=7.7), we need to use a custom krb5.config to enable it + # 2. When reading the password it reads from the pty not stdin by default causing an issue with subprocess. We + # can control that behaviour with '--password-file=STDIN' + # Also need to set the custom path to krb5-config and kinit as FreeBSD relies on the newer Heimdal version in the + # port package. + sysname = os.uname()[0] + prefix = '/usr/local/bin/' if sysname == 'FreeBSD' else '' + is_heimdal = sysname in ['Darwin', 'FreeBSD'] + + # Debugging purposes, get the Kerberos version. On platforms like OpenSUSE this may not be on the PATH. + try: + process = subprocess.Popen(['%skrb5-config' % prefix, '--version'], stdout=subprocess.PIPE) + stdout, stderr = process.communicate() + version = to_text(stdout) + except OSError as e: + if e.errno != errno.ENOENT: + raise + version = 'Unknown (no krb5-config)' + + kinit_args = ['%skinit' % prefix] + config = {} + if is_heimdal: + kinit_args.append('--password-file=STDIN') + config['logging'] = {'krb5': 'FILE:/dev/stdout'} + kinit_args.append(to_text(module.params['username'], errors='surrogate_or_strict')) + + with krb5_conf(module, config): + # Weirdly setting KRB5_CONFIG in the modules environment block does not work unless we pass it in explicitly. + # Take a copy of the existing environment to make sure the process has the same env vars as ours. Also set + # KRB5_TRACE to output and debug logs helping to identify problems when calling kinit with MIT. + kinit_env = os.environ.copy() + kinit_env['KRB5_TRACE'] = '/dev/stdout' + + process = subprocess.Popen(kinit_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + env=kinit_env) + stdout, stderr = process.communicate(to_bytes(module.params['password'], errors='surrogate_or_strict') + b'\n') + rc = process.returncode + + module.exit_json(changed=True, stdout=to_text(stdout), stderr=to_text(stderr), rc=rc, version=version) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/prepare_http_tests/meta/main.yml b/test/integration/targets/prepare_http_tests/meta/main.yml index 1810d4be..c2c543a2 100644 --- a/test/integration/targets/prepare_http_tests/meta/main.yml +++ b/test/integration/targets/prepare_http_tests/meta/main.yml @@ -1,2 +1,3 @@ dependencies: - setup_remote_tmp_dir + - setup_remote_constraints diff --git a/test/integration/targets/prepare_http_tests/tasks/default.yml b/test/integration/targets/prepare_http_tests/tasks/default.yml index bff90350..9ab8ad91 100644 --- a/test/integration/targets/prepare_http_tests/tasks/default.yml +++ b/test/integration/targets/prepare_http_tests/tasks/default.yml @@ -22,19 +22,19 @@ dest: "/etc/pki/trust/anchors/ansible.pem" when: ansible_os_family == 'Suse' -- name: Debian - Retrieve test cacert +- name: Debian/Alpine - Retrieve test cacert get_url: url: "http://ansible.http.tests/cacert.pem" dest: "/usr/local/share/ca-certificates/ansible.crt" - when: ansible_os_family == 'Debian' + when: ansible_os_family in ['Debian', 'Alpine'] - name: Redhat - Update ca trust command: update-ca-trust extract when: ansible_os_family == 'RedHat' -- name: Debian/Suse - Update ca certificates +- name: Debian/Alpine/Suse - Update ca certificates command: update-ca-certificates - when: ansible_os_family == 'Debian' or ansible_os_family == 'Suse' + when: ansible_os_family in ['Debian', 'Alpine', 'Suse'] - name: FreeBSD - Retrieve test cacert get_url: diff --git a/test/integration/targets/prepare_http_tests/tasks/kerberos.yml b/test/integration/targets/prepare_http_tests/tasks/kerberos.yml new file mode 100644 index 00000000..06feea1c --- /dev/null +++ b/test/integration/targets/prepare_http_tests/tasks/kerberos.yml @@ -0,0 +1,63 @@ +- set_fact: + krb5_config: '{{ remote_tmp_dir }}/krb5.conf' + krb5_realm: '{{ httpbin_host.split(".")[1:] | join(".") | upper }}' + krb5_provider: '{{ (ansible_facts.os_family == "FreeBSD" or ansible_facts.distribution == "MacOSX") | ternary("Heimdal", "MIT") }}' + +- set_fact: + krb5_username: admin@{{ krb5_realm }} + +- name: Create krb5.conf file + template: + src: krb5.conf.j2 + dest: '{{ krb5_config }}' + +- name: Include distribution specific variables + include_vars: '{{ lookup("first_found", params) }}' + vars: + params: + files: + - '{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml' + - '{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml' + - '{{ ansible_facts.distribution }}.yml' + - '{{ ansible_facts.os_family }}.yml' + - default.yml + paths: + - '{{ role_path }}/vars' + +- name: Install Kerberos sytem packages + package: + name: '{{ krb5_packages }}' + state: present + when: ansible_facts.distribution not in ['Alpine', 'MacOSX'] + +# apk isn't available on ansible-core so just call command +- name: Alpine - Install Kerberos system packages + command: apk add {{ krb5_packages | join(' ') }} + when: ansible_facts.distribution == 'Alpine' + +- name: Install python gssapi + pip: + name: + - gssapi + - importlib ; python_version < '2.7' + state: present + extra_args: '-c {{ remote_constraints }}' + environment: + # Put /usr/local/bin for FreeBSD as we need to use the heimdal port over the builtin version + # https://github.com/pythongssapi/python-gssapi/issues/228 + # Need the /usr/lib/mit/bin custom path for OpenSUSE as krb5-config is placed there + PATH: '/usr/local/bin:{{ ansible_facts.env.PATH }}:/usr/lib/mit/bin' + notify: Remove python gssapi + +- name: test the environment to make sure Kerberos is working properly + httptester_kinit: + username: '{{ krb5_username }}' + password: '{{ krb5_password }}' + environment: + KRB5_CONFIG: '{{ krb5_config }}' + KRB5CCNAME: FILE:{{ remote_tmp_dir }}/krb5.cc + +- name: remove test credential cache + file: + path: '{{ remote_tmp_dir }}/krb5.cc' + state: absent diff --git a/test/integration/targets/prepare_http_tests/tasks/main.yml b/test/integration/targets/prepare_http_tests/tasks/main.yml index 86e350c2..9ab00221 100644 --- a/test/integration/targets/prepare_http_tests/tasks/main.yml +++ b/test/integration/targets/prepare_http_tests/tasks/main.yml @@ -22,3 +22,13 @@ - has_httptester|bool # skip the setup if running on Windows Server 2008 as httptester is not available - ansible_os_family != 'Windows' or (ansible_os_family == 'Windows' and not ansible_distribution_version.startswith("6.0.")) + +- set_fact: + krb5_password: "{{ lookup('env', 'KRB5_PASSWORD') }}" + +- name: setup Kerberos client + include_tasks: kerberos.yml + when: + - has_httptester|bool + - ansible_os_family != 'Windows' + - krb5_password != '' diff --git a/test/integration/targets/prepare_http_tests/templates/krb5.conf.j2 b/test/integration/targets/prepare_http_tests/templates/krb5.conf.j2 new file mode 100644 index 00000000..3ddfe5ea --- /dev/null +++ b/test/integration/targets/prepare_http_tests/templates/krb5.conf.j2 @@ -0,0 +1,25 @@ +[libdefaults] + default_realm = {{ krb5_realm | upper }} + dns_lookup_realm = false + dns_lookup_kdc = false + rdns = false + +[realms] + {{ krb5_realm | upper }} = { +{% if krb5_provider == 'Heimdal' %} +{# Heimdal seems to only use UDP unless TCP is explicitly set and we must use TCP as the SSH tunnel only supports TCP. #} +{# The hostname doesn't seem to work when using the alias, just use localhost as that works. #} + kdc = tcp/127.0.0.1 + admin_server = tcp/127.0.0.1 +{% else %} + kdc = {{ httpbin_host }} + admin_server = {{ httpbin_host }} +{% endif %} + } + +[domain_realm] + .{{ krb5_realm | lower }} = {{ krb5_realm | upper }} + {{ krb5_realm | lower }} = {{ krb5_realm | upper }} + +[logging] + krb5 = FILE:/dev/stdout diff --git a/test/integration/targets/prepare_http_tests/vars/Alpine.yml b/test/integration/targets/prepare_http_tests/vars/Alpine.yml new file mode 100644 index 00000000..2ac6a380 --- /dev/null +++ b/test/integration/targets/prepare_http_tests/vars/Alpine.yml @@ -0,0 +1,3 @@ +krb5_packages: +- krb5 +- krb5-dev diff --git a/test/integration/targets/prepare_http_tests/vars/Debian.yml b/test/integration/targets/prepare_http_tests/vars/Debian.yml new file mode 100644 index 00000000..2b6f9b8d --- /dev/null +++ b/test/integration/targets/prepare_http_tests/vars/Debian.yml @@ -0,0 +1,3 @@ +krb5_packages: +- krb5-user +- libkrb5-dev diff --git a/test/integration/targets/prepare_http_tests/vars/FreeBSD.yml b/test/integration/targets/prepare_http_tests/vars/FreeBSD.yml new file mode 100644 index 00000000..752b5368 --- /dev/null +++ b/test/integration/targets/prepare_http_tests/vars/FreeBSD.yml @@ -0,0 +1,2 @@ +krb5_packages: +- heimdal diff --git a/test/integration/targets/prepare_http_tests/vars/Suse.yml b/test/integration/targets/prepare_http_tests/vars/Suse.yml new file mode 100644 index 00000000..0e159c4f --- /dev/null +++ b/test/integration/targets/prepare_http_tests/vars/Suse.yml @@ -0,0 +1,3 @@ +krb5_packages: +- krb5-client +- krb5-devel diff --git a/test/integration/targets/prepare_http_tests/vars/default.yml b/test/integration/targets/prepare_http_tests/vars/default.yml new file mode 100644 index 00000000..5bc07d54 --- /dev/null +++ b/test/integration/targets/prepare_http_tests/vars/default.yml @@ -0,0 +1,3 @@ +krb5_packages: +- krb5-devel +- krb5-workstation diff --git a/test/integration/targets/prepare_http_tests/vars/httptester.yml b/test/integration/targets/prepare_http_tests/vars/httptester.yml index 0e23ae93..26acf115 100644 --- a/test/integration/targets/prepare_http_tests/vars/httptester.yml +++ b/test/integration/targets/prepare_http_tests/vars/httptester.yml @@ -3,3 +3,4 @@ badssl_host: fail.ansible.http.tests httpbin_host: ansible.http.tests sni_host: sni1.ansible.http.tests badssl_host_substring: HTTP Client Testing Service +self_signed_host: self-signed.ansible.http.tests diff --git a/test/integration/targets/pull/pull-integration-test/multi_play_1.yml b/test/integration/targets/pull/pull-integration-test/multi_play_1.yml new file mode 100644 index 00000000..0ec0da6b --- /dev/null +++ b/test/integration/targets/pull/pull-integration-test/multi_play_1.yml @@ -0,0 +1,6 @@ +- name: test multiple playbooks for ansible-pull + hosts: all + gather_facts: False + tasks: + - name: debug output + debug: msg="test multi_play_1" diff --git a/test/integration/targets/pull/pull-integration-test/multi_play_2.yml b/test/integration/targets/pull/pull-integration-test/multi_play_2.yml new file mode 100644 index 00000000..1fe5a584 --- /dev/null +++ b/test/integration/targets/pull/pull-integration-test/multi_play_2.yml @@ -0,0 +1,6 @@ +- name: test multiple playbooks for ansible-pull + hosts: all + gather_facts: False + tasks: + - name: debug output + debug: msg="test multi_play_2" diff --git a/test/integration/targets/pull/runme.sh b/test/integration/targets/pull/runme.sh index dcadc495..347971a4 100755 --- a/test/integration/targets/pull/runme.sh +++ b/test/integration/targets/pull/runme.sh @@ -49,6 +49,20 @@ function pass_tests { fi } +function pass_tests_multi { + # test for https://github.com/ansible/ansible/issues/72708 + if ! grep 'test multi_play_1' "${temp_log}"; then + cat "${temp_log}" + echo "Did not run multiple playbooks" + exit 1 + fi + if ! grep 'test multi_play_2' "${temp_log}"; then + cat "${temp_log}" + echo "Did not run multiple playbooks" + exit 1 + fi +} + export ANSIBLE_INVENTORY export ANSIBLE_HOST_PATTERN_MISMATCH @@ -67,3 +81,7 @@ JSON_EXTRA_ARGS='{"docker_registries_login": [{ "docker_password": "'"${PASSWORD ANSIBLE_CONFIG='' ansible-pull -d "${pull_dir}" -U "${repo_dir}" -e "${JSON_EXTRA_ARGS}" "$@" --tags untagged,test_ev | tee "${temp_log}" pass_tests + +ANSIBLE_CONFIG='' ansible-pull -d "${pull_dir}" -U "${repo_dir}" "$@" multi_play_1.yml multi_play_2.yml | tee "${temp_log}" + +pass_tests_multi
\ No newline at end of file diff --git a/test/integration/targets/pull/setup.yml b/test/integration/targets/pull/setup.yml index a82d02ae..ebd5a1c0 100644 --- a/test/integration/targets/pull/setup.yml +++ b/test/integration/targets/pull/setup.yml @@ -3,7 +3,7 @@ - name: install git package: name: git - when: ansible_distribution != "MacOSX" + when: ansible_distribution not in ["MacOSX", "Alpine"] register: git_install - name: save install result copy: diff --git a/test/integration/targets/reboot/handlers/main.yml b/test/integration/targets/reboot/handlers/main.yml new file mode 100644 index 00000000..a40bac09 --- /dev/null +++ b/test/integration/targets/reboot/handlers/main.yml @@ -0,0 +1,4 @@ +- name: remove molly-guard + apt: + name: molly-guard + state: absent diff --git a/test/integration/targets/reboot/tasks/check_reboot.yml b/test/integration/targets/reboot/tasks/check_reboot.yml index 1aff1be2..059c422a 100644 --- a/test/integration/targets/reboot/tasks/check_reboot.yml +++ b/test/integration/targets/reboot/tasks/check_reboot.yml @@ -1,5 +1,5 @@ - name: Get current boot time - command: "{{ boot_time_command[ansible_facts['distribution'] | lower] | default('cat /proc/sys/kernel/random/boot_id') }}" + command: "{{ _boot_time_command[ansible_facts['distribution'] | lower] | default('cat /proc/sys/kernel/random/boot_id') }}" register: after_boot_time - name: Ensure system was actually rebooted diff --git a/test/integration/targets/reboot/tasks/get_boot_time.yml b/test/integration/targets/reboot/tasks/get_boot_time.yml index cec22f06..7f79770a 100644 --- a/test/integration/targets/reboot/tasks/get_boot_time.yml +++ b/test/integration/targets/reboot/tasks/get_boot_time.yml @@ -1,3 +1,3 @@ - name: Get current boot time - command: "{{ boot_time_command[ansible_facts['distribution'] | lower] | default('cat /proc/sys/kernel/random/boot_id') }}" + command: "{{ _boot_time_command[ansible_facts['distribution'] | lower] | default('cat /proc/sys/kernel/random/boot_id') }}" register: before_boot_time diff --git a/test/integration/targets/reboot/tasks/main.yml b/test/integration/targets/reboot/tasks/main.yml index 2568b9b2..7687cb73 100644 --- a/test/integration/targets/reboot/tasks/main.yml +++ b/test/integration/targets/reboot/tasks/main.yml @@ -1,4 +1,6 @@ -- block: +- name: Test reboot + when: ansible_facts.virtualization_type | default('') not in ['docker', 'container', 'containerd'] + block: # This block can be removed once we have a mechanism in ansible-test to separate # the control node from the managed node. - block: @@ -23,89 +25,17 @@ Skipping reboot test. that: - not controller_temp_file.stat.exists + always: + - name: Cleanup temp file + file: + path: /tmp/Anything-Nutlike-Nuzzle-Plow-Overdue + state: absent + delegate_to: localhost + connection: local + when: inventory_hostname == ansible_play_hosts[0] - - import_tasks: get_boot_time.yml - - - name: Reboot with default settings - reboot: - register: reboot_result - - - import_tasks: check_reboot.yml - - - import_tasks: get_boot_time.yml - - - name: Reboot with all options - reboot: - connect_timeout: 30 - search_paths: /usr/local/bin - msg: Rebooting - post_reboot_delay: 1 - pre_reboot_delay: 61 - test_command: uptime - reboot_timeout: 500 - register: reboot_result - - - import_tasks: check_reboot.yml - - - import_tasks: get_boot_time.yml - - - name: Test with negative values for delays - reboot: - post_reboot_delay: -0.5 - pre_reboot_delay: -61 - register: reboot_result - - - import_tasks: check_reboot.yml - - - name: Use invalid parameter - reboot: - foo: bar - ignore_errors: true - register: invalid_parameter - - - name: Ensure task fails with error - assert: - that: - - invalid_parameter is failed - - "invalid_parameter.msg == 'Invalid options for reboot: foo'" - - - name: Reboot with test command that fails - reboot: - test_command: 'FAIL' - reboot_timeout: "{{ timeout }}" - register: reboot_fail_test - failed_when: "reboot_fail_test.msg != 'Timed out waiting for post-reboot test command (timeout=' ~ timeout ~ ')'" - vars: - timeout: "{{ timeout_value[ansible_facts['distribution'] | lower] | default(60) }}" - - - name: Test molly-guard - block: - - import_tasks: get_boot_time.yml - - - name: Install molly-guard - apt: - update_cache: yes - name: molly-guard - state: present - - - name: Reboot when molly-guard is installed - reboot: - search_paths: /lib/molly-guard - register: reboot_result - - - import_tasks: check_reboot.yml - - when: ansible_facts.distribution in ['Debian', 'Ubuntu'] - tags: - - molly-guard - - always: - - name: Cleanup temp file - file: - path: /tmp/Anything-Nutlike-Nuzzle-Plow-Overdue - state: absent - delegate_to: localhost - connection: local - when: inventory_hostname == ansible_play_hosts[0] - - when: ansible_virtualization_type | default('') != 'docker' + - import_tasks: test_standard_scenarios.yml + - import_tasks: test_reboot_command.yml + - import_tasks: test_invalid_parameter.yml + - import_tasks: test_invalid_test_command.yml + - import_tasks: test_molly_guard.yml diff --git a/test/integration/targets/reboot/tasks/test_invalid_parameter.yml b/test/integration/targets/reboot/tasks/test_invalid_parameter.yml new file mode 100644 index 00000000..f8e1a8f4 --- /dev/null +++ b/test/integration/targets/reboot/tasks/test_invalid_parameter.yml @@ -0,0 +1,11 @@ +- name: Use invalid parameter + reboot: + foo: bar + ignore_errors: yes + register: invalid_parameter + +- name: Ensure task fails with error + assert: + that: + - invalid_parameter is failed + - "invalid_parameter.msg == 'Invalid options for reboot: foo'" diff --git a/test/integration/targets/reboot/tasks/test_invalid_test_command.yml b/test/integration/targets/reboot/tasks/test_invalid_test_command.yml new file mode 100644 index 00000000..ea1db81d --- /dev/null +++ b/test/integration/targets/reboot/tasks/test_invalid_test_command.yml @@ -0,0 +1,8 @@ +- name: Reboot with test command that fails + reboot: + test_command: 'FAIL' + reboot_timeout: "{{ timeout }}" + register: reboot_fail_test + failed_when: "reboot_fail_test.msg != 'Timed out waiting for post-reboot test command (timeout=' ~ timeout ~ ')'" + vars: + timeout: "{{ _timeout_value[ansible_facts['distribution'] | lower] | default(60) }}" diff --git a/test/integration/targets/reboot/tasks/test_molly_guard.yml b/test/integration/targets/reboot/tasks/test_molly_guard.yml new file mode 100644 index 00000000..f91fd4f1 --- /dev/null +++ b/test/integration/targets/reboot/tasks/test_molly_guard.yml @@ -0,0 +1,20 @@ +- name: Test molly-guard + when: ansible_facts.distribution in ['Debian', 'Ubuntu'] + tags: + - molly-guard + block: + - import_tasks: get_boot_time.yml + + - name: Install molly-guard + apt: + update_cache: yes + name: molly-guard + state: present + notify: remove molly-guard + + - name: Reboot when molly-guard is installed + reboot: + search_paths: /lib/molly-guard + register: reboot_result + + - import_tasks: check_reboot.yml diff --git a/test/integration/targets/reboot/tasks/test_reboot_command.yml b/test/integration/targets/reboot/tasks/test_reboot_command.yml new file mode 100644 index 00000000..779d380b --- /dev/null +++ b/test/integration/targets/reboot/tasks/test_reboot_command.yml @@ -0,0 +1,22 @@ +- import_tasks: get_boot_time.yml +- name: Reboot with custom reboot_command using unqualified path + reboot: + reboot_command: reboot + register: reboot_result +- import_tasks: check_reboot.yml + + +- import_tasks: get_boot_time.yml +- name: Reboot with custom reboot_command using absolute path + reboot: + reboot_command: /sbin/reboot + register: reboot_result +- import_tasks: check_reboot.yml + + +- import_tasks: get_boot_time.yml +- name: Reboot with custom reboot_command with parameters + reboot: + reboot_command: shutdown -r now + register: reboot_result +- import_tasks: check_reboot.yml diff --git a/test/integration/targets/reboot/tasks/test_standard_scenarios.yml b/test/integration/targets/reboot/tasks/test_standard_scenarios.yml new file mode 100644 index 00000000..fac85be7 --- /dev/null +++ b/test/integration/targets/reboot/tasks/test_standard_scenarios.yml @@ -0,0 +1,32 @@ +- import_tasks: get_boot_time.yml +- name: Reboot with default settings + reboot: + register: reboot_result +- import_tasks: check_reboot.yml + + +- import_tasks: get_boot_time.yml +- name: Reboot with all options except reboot_command + reboot: + connect_timeout: 30 + search_paths: + - /sbin + - /bin + - /usr/sbin + - /usr/bin + msg: Rebooting + post_reboot_delay: 1 + pre_reboot_delay: 61 + test_command: uptime + reboot_timeout: 500 + register: reboot_result +- import_tasks: check_reboot.yml + + +- import_tasks: get_boot_time.yml +- name: Test with negative values for delays + reboot: + post_reboot_delay: -0.5 + pre_reboot_delay: -61 + register: reboot_result +- import_tasks: check_reboot.yml diff --git a/test/integration/targets/reboot/vars/main.yml b/test/integration/targets/reboot/vars/main.yml index 24367c80..0e1997c4 100644 --- a/test/integration/targets/reboot/vars/main.yml +++ b/test/integration/targets/reboot/vars/main.yml @@ -1,9 +1,9 @@ -boot_time_command: +_boot_time_command: freebsd: '/sbin/sysctl kern.boottime' openbsd: '/sbin/sysctl kern.boottime' macosx: 'who -b' solaris: 'who -b' sunos: 'who -b' -timeout_value: +_timeout_value: solaris: 120 diff --git a/test/integration/targets/roles/roles/b/meta/main.yml b/test/integration/targets/roles/roles/b/meta/main.yml index f95ffe65..abe2dd45 100644 --- a/test/integration/targets/roles/roles/b/meta/main.yml +++ b/test/integration/targets/roles/roles/b/meta/main.yml @@ -1,2 +1,4 @@ dependencies: - name: a + +argument_specs: {} diff --git a/test/integration/targets/roles_arg_spec/aliases b/test/integration/targets/roles_arg_spec/aliases new file mode 100644 index 00000000..70a7b7a9 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/aliases @@ -0,0 +1 @@ +shippable/posix/group5 diff --git a/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/MANIFEST.json b/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/MANIFEST.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/MANIFEST.json diff --git a/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/roles/blah/meta/argument_specs.yml b/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/roles/blah/meta/argument_specs.yml new file mode 100644 index 00000000..3cb0a87b --- /dev/null +++ b/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/roles/blah/meta/argument_specs.yml @@ -0,0 +1,8 @@ +argument_specs: + main: + short_description: "The foo.bar.blah role" + options: + blah_str: + type: "str" + required: true + description: "A string value" diff --git a/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/roles/blah/tasks/main.yml b/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/roles/blah/tasks/main.yml new file mode 100644 index 00000000..ecb4dac6 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/collections/ansible_collections/foo/bar/roles/blah/tasks/main.yml @@ -0,0 +1,3 @@ +- name: "First task of blah role" + debug: + msg: "The string is {{ blah_str }}" diff --git a/test/integration/targets/roles_arg_spec/roles/a/meta/argument_specs.yml b/test/integration/targets/roles_arg_spec/roles/a/meta/argument_specs.yml new file mode 100644 index 00000000..cfc1a372 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/a/meta/argument_specs.yml @@ -0,0 +1,17 @@ +argument_specs: + main: + short_description: Main entry point for role A. + options: + a_str: + type: "str" + required: true + + alternate: + short_description: Alternate entry point for role A. + options: + a_int: + type: "int" + required: true + + no_spec_entrypoint: + short_description: An entry point with no spec diff --git a/test/integration/targets/roles_arg_spec/roles/a/meta/main.yml b/test/integration/targets/roles_arg_spec/roles/a/meta/main.yml new file mode 100644 index 00000000..90920dbb --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/a/meta/main.yml @@ -0,0 +1,13 @@ +# This meta/main.yml exists to test that it is NOT read, with preference being +# given to the meta/argument_specs.yml file. This spec contains an extra required +# parameter, a_something, that argument_specs.yml does not. +argument_specs: + main: + short_description: Main entry point for role A. + options: + a_str: + type: "str" + required: true + a_something: + type: "str" + required: true diff --git a/test/integration/targets/roles_arg_spec/roles/a/tasks/alternate.yml b/test/integration/targets/roles_arg_spec/roles/a/tasks/alternate.yml new file mode 100644 index 00000000..4d688be6 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/a/tasks/alternate.yml @@ -0,0 +1,3 @@ +--- +- debug: + msg: "Role A (alternate) with {{ a_int }}" diff --git a/test/integration/targets/roles_arg_spec/roles/a/tasks/main.yml b/test/integration/targets/roles_arg_spec/roles/a/tasks/main.yml new file mode 100644 index 00000000..a74f37bb --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/a/tasks/main.yml @@ -0,0 +1,3 @@ +--- +- debug: + msg: "Role A with {{ a_str }}" diff --git a/test/integration/targets/roles_arg_spec/roles/a/tasks/no_spec_entrypoint.yml b/test/integration/targets/roles_arg_spec/roles/a/tasks/no_spec_entrypoint.yml new file mode 100644 index 00000000..f1e600b9 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/a/tasks/no_spec_entrypoint.yml @@ -0,0 +1,3 @@ +--- +- debug: + msg: "Role A no_spec_entrypoint" diff --git a/test/integration/targets/roles_arg_spec/roles/b/meta/argument_specs.yaml b/test/integration/targets/roles_arg_spec/roles/b/meta/argument_specs.yaml new file mode 100644 index 00000000..93663e9a --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/b/meta/argument_specs.yaml @@ -0,0 +1,13 @@ +argument_specs: + main: + short_description: Main entry point for role B. + options: + b_str: + type: "str" + required: true + b_int: + type: "int" + required: true + b_bool: + type: "bool" + required: true diff --git a/test/integration/targets/roles_arg_spec/roles/b/tasks/main.yml b/test/integration/targets/roles_arg_spec/roles/b/tasks/main.yml new file mode 100644 index 00000000..b7e15cc0 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/b/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- debug: + msg: "Role B" +- debug: + var: b_str +- debug: + var: b_int +- debug: + var: b_bool 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 new file mode 100644 index 00000000..1a1ccbe4 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/c/meta/main.yml @@ -0,0 +1,7 @@ +argument_specs: + main: + short_description: Main entry point for role C. + options: + c_int: + type: "int" + required: true diff --git a/test/integration/targets/roles_arg_spec/roles/c/tasks/main.yml b/test/integration/targets/roles_arg_spec/roles/c/tasks/main.yml new file mode 100644 index 00000000..78282be3 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/c/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- debug: + msg: "Role C that includes Role A with var {{ c_int }}" + +- name: "Role C import_role A with a_str {{ a_str }}" + import_role: + name: a + +- name: "Role C include_role A with a_int {{ a_int }}" + include_role: + name: a + tasks_from: "alternate" diff --git a/test/integration/targets/roles_arg_spec/roles/role_with_no_tasks/meta/argument_specs.yml b/test/integration/targets/roles_arg_spec/roles/role_with_no_tasks/meta/argument_specs.yml new file mode 100644 index 00000000..55e48006 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/role_with_no_tasks/meta/argument_specs.yml @@ -0,0 +1,7 @@ +argument_specs: + main: + short_description: Main entry point for role role_with_no_tasks. + options: + a_str: + type: "str" + required: true diff --git a/test/integration/targets/roles_arg_spec/roles/test1/defaults/main.yml b/test/integration/targets/roles_arg_spec/roles/test1/defaults/main.yml new file mode 100644 index 00000000..5255f939 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/test1/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for test1 +test1_var1: 'THE_TEST1_VAR1_DEFAULT_VALUE' diff --git a/test/integration/targets/roles_arg_spec/roles/test1/meta/argument_specs.yml b/test/integration/targets/roles_arg_spec/roles/test1/meta/argument_specs.yml new file mode 100644 index 00000000..02edac66 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/test1/meta/argument_specs.yml @@ -0,0 +1,107 @@ +--- +argument_specs: + main: + short_description: "EXPECTED FAILURE Validate the argument spec for the 'test1' role" + options: + test1_choices: + required: false + # required: true + choices: + - "this paddle game" + - "the astray" + - "this remote control" + - "the chair" + type: "str" + default: "this paddle game" + tidy_expected: + # required: false + # default: none + type: "list" + test1_var1: + # required: true + default: "THIS IS THE DEFAULT SURVEY ANSWER FOR test1_survey_test1_var1" + type: "str" + test1_var2: + required: false + default: "This IS THE DEFAULT fake band name / test1_var2 answer from survey_spec.yml" + type: "str" + bust_some_stuff: + # required: false + type: "int" + some_choices: + choices: + - "choice1" + - "choice2" + required: false + type: "str" + some_str: + type: "str" + some_list: + type: "list" + elements: "float" + some_dict: + type: "dict" + some_bool: + type: "bool" + some_int: + type: "int" + some_float: + type: "float" + some_path: + type: "path" + some_raw: + type: "raw" + some_jsonarg: + type: "jsonarg" + required: true + some_json: + type: "json" + required: true + some_bytes: + type: "bytes" + some_bits: + type: "bits" + some_str_aliases: + type: "str" + aliases: + - "some_str_nicknames" + - "some_str_akas" + - "some_str_formerly_known_as" + some_dict_options: + type: "dict" + options: + some_second_level: + type: "bool" + default: true + some_str_removed_in: + type: "str" + removed_in: 2.10 + some_tmp_path: + type: "path" + multi_level_option: + type: "dict" + options: + second_level: + type: "dict" + options: + third_level: + type: "int" + required: true + + other: + short_description: "test1_simple_preset_arg_spec_other" + description: "A simpler set of required args for other tasks" + options: + test1_var1: + default: "This the default value for the other set of arg specs for test1 test1_var1" + type: "str" + + test1_other: + description: "test1_other for role_that_includes_role" + options: + some_test1_other_arg: + default: "The some_test1_other_arg default value" + type: str + some_required_str: + type: str + required: true diff --git a/test/integration/targets/roles_arg_spec/roles/test1/tasks/main.yml b/test/integration/targets/roles_arg_spec/roles/test1/tasks/main.yml new file mode 100644 index 00000000..9ecf8b0a --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/test1/tasks/main.yml @@ -0,0 +1,11 @@ +--- +# tasks file for test1 +- name: debug for task1 show test1_var1 + debug: + var: test1_var1 + tags: ["runme"] + +- name: debug for task1 show test1_var2 + debug: + var: test1_var2 + tags: ["runme"] diff --git a/test/integration/targets/roles_arg_spec/roles/test1/tasks/other.yml b/test/integration/targets/roles_arg_spec/roles/test1/tasks/other.yml new file mode 100644 index 00000000..b045813e --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/test1/tasks/other.yml @@ -0,0 +1,11 @@ +--- +# "other" tasks file for test1 +- name: other tasks debug for task1 show test1_var1 + debug: + var: test1_var1 + tags: ["runme"] + +- name: other tasks debug for task1 show test1_var2 + debug: + var: test1_var2 + tags: ["runme"] diff --git a/test/integration/targets/roles_arg_spec/roles/test1/tasks/test1_other.yml b/test/integration/targets/roles_arg_spec/roles/test1/tasks/test1_other.yml new file mode 100644 index 00000000..8b1ec13f --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/test1/tasks/test1_other.yml @@ -0,0 +1,11 @@ +--- +# "test1_other" tasks file for test1 +- name: "test1_other BLIPPY test1_other tasks debug for task1 show test1_var1" + debug: + var: test1_var1 + tags: ["runme"] + +- name: "BLIPPY FOO test1_other tasks debug for task1 show test1_var2" + debug: + var: test1_var2 + tags: ["runme"] diff --git a/test/integration/targets/roles_arg_spec/roles/test1/vars/main.yml b/test/integration/targets/roles_arg_spec/roles/test1/vars/main.yml new file mode 100644 index 00000000..3e72dd67 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/test1/vars/main.yml @@ -0,0 +1,4 @@ +--- +# vars file for test1 +test1_var1: 'THE_TEST1_VAR1_VARS_VALUE' +test1_var2: 'THE_TEST1_VAR2_VARS_VALUE' diff --git a/test/integration/targets/roles_arg_spec/roles/test1/vars/other.yml b/test/integration/targets/roles_arg_spec/roles/test1/vars/other.yml new file mode 100644 index 00000000..a397bdc6 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/roles/test1/vars/other.yml @@ -0,0 +1,4 @@ +--- +# vars file for test1 +test1_var1: 'other_THE_TEST1_VAR1_VARS_VALUE' +test1_var2: 'other_THE_TEST1_VAR2_VARS_VALUE' diff --git a/test/integration/targets/roles_arg_spec/runme.sh b/test/integration/targets/roles_arg_spec/runme.sh new file mode 100755 index 00000000..209a34e3 --- /dev/null +++ b/test/integration/targets/roles_arg_spec/runme.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -eux + +# This effectively disables junit callback output by directing the output to +# a directory ansible-test will not look at. +# +# Since the failures in these tests are on the role arg spec validation and the +# name for those tasks is fixed (we cannot add "EXPECTED FAILURE" to the name), +# disabling the junit callback output is the easiest way to prevent these from +# showing up in test run output. +# +# Longer term, an option can be added to the junit callback allowing a custom +# regexp to be supplied rather than the hard coded "EXPECTED FAILURE". +export JUNIT_OUTPUT_DIR="${OUTPUT_DIR}" + +# Various simple role scenarios +ansible-playbook test.yml -i ../../inventory "$@" + +# More complex role test +ansible-playbook test_complex_role_fails.yml -i ../../inventory "$@" + +# Test play level role will fail +set +e +ansible-playbook test_play_level_role_fails.yml -i ../../inventory "$@" +test $? -ne 0 +set -e + +# Test the validation task is tagged with 'always' by specifying an unused tag. +# The task is tagged with 'foo' but we use 'bar' in the call below and expect +# the validation task to run anyway since it is tagged 'always'. +ansible-playbook test_tags.yml -i ../../inventory "$@" --tags bar | grep "a : Validating arguments against arg spec 'main' - Main entry point for role A." diff --git a/test/integration/targets/roles_arg_spec/test.yml b/test/integration/targets/roles_arg_spec/test.yml new file mode 100644 index 00000000..06268c6a --- /dev/null +++ b/test/integration/targets/roles_arg_spec/test.yml @@ -0,0 +1,340 @@ +--- +- hosts: localhost + gather_facts: false + roles: + - { role: a, a_str: "roles" } + + vars: + INT_VALUE: 42 + + tasks: + + - name: "Valid simple role usage with include_role" + include_role: + name: a + vars: + a_str: "include_role" + + - name: "Valid simple role usage with import_role" + import_role: + name: a + vars: + a_str: "import_role" + + - name: "Valid role usage (more args)" + include_role: + name: b + vars: + b_str: "xyz" + b_int: 5 + b_bool: true + + - name: "Valid simple role usage with include_role of different entry point" + include_role: + name: a + tasks_from: "alternate" + vars: + a_int: 256 + + - name: "Valid simple role usage with import_role of different entry point" + import_role: + name: a + tasks_from: "alternate" + vars: + a_int: 512 + + - name: "Valid simple role usage with a templated value" + import_role: + name: a + vars: + a_int: "{{ INT_VALUE }}" + + - name: "Call role entry point that is defined, but has no spec data" + import_role: + name: a + tasks_from: "no_spec_entrypoint" + +- name: "New play to reset vars: Test include_role fails" + hosts: localhost + gather_facts: false + vars: + expected_returned_spec: + b_bool: + required: true + type: "bool" + b_int: + required: true + type: "int" + b_str: + required: true + type: "str" + + tasks: + - block: + - name: "Invalid role usage" + include_role: + name: b + vars: + b_bool: 7 + + - fail: + msg: "Should not get here" + + rescue: + - debug: + var: ansible_failed_result + + - name: "Validate failure" + assert: + that: + - ansible_failed_task.name == "Validating arguments against arg spec 'main' - Main entry point for role B." + - ansible_failed_result.argument_errors | length == 2 + - "'missing required arguments: b_int, b_str' in ansible_failed_result.argument_errors" + - ansible_failed_result.validate_args_context.argument_spec_name == "main" + - ansible_failed_result.validate_args_context.name == "b" + - ansible_failed_result.validate_args_context.type == "role" + - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/roles/b')" + - ansible_failed_result.argument_spec_data == expected_returned_spec + + +- name: "New play to reset vars: Test import_role fails" + hosts: localhost + gather_facts: false + vars: + expected_returned_spec: + b_bool: + required: true + type: "bool" + b_int: + required: true + type: "int" + b_str: + required: true + type: "str" + + tasks: + - block: + - name: "Invalid role usage" + import_role: + name: b + vars: + b_bool: 7 + + - fail: + msg: "Should not get here" + + rescue: + - debug: + var: ansible_failed_result + + - name: "Validate failure" + assert: + that: + - ansible_failed_task.name == "Validating arguments against arg spec 'main' - Main entry point for role B." + - ansible_failed_result.argument_errors | length == 2 + - "'missing required arguments: b_int, b_str' in ansible_failed_result.argument_errors" + - ansible_failed_result.validate_args_context.argument_spec_name == "main" + - ansible_failed_result.validate_args_context.name == "b" + - ansible_failed_result.validate_args_context.type == "role" + - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/roles/b')" + - ansible_failed_result.argument_spec_data == expected_returned_spec + + +- name: "New play to reset vars: Test nested role including/importing role succeeds" + hosts: localhost + gather_facts: false + vars: + c_int: 1 + a_str: "some string" + a_int: 42 + tasks: + - name: "Test import_role of role C" + import_role: + name: c + + - name: "Test include_role of role C" + include_role: + name: c + + +- name: "New play to reset vars: Test nested role including/importing role fails" + hosts: localhost + gather_facts: false + vars: + main_expected_returned_spec: + a_str: + required: true + type: "str" + alternate_expected_returned_spec: + a_int: + required: true + type: "int" + + 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" + + 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 | length == 1 + - "'missing required arguments: a_str' in ansible_failed_result.argument_errors" + - ansible_failed_result.validate_args_context.argument_spec_name == "main" + - ansible_failed_result.validate_args_context.name == "a" + - ansible_failed_result.validate_args_context.type == "role" + - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/roles/a')" + - ansible_failed_result.argument_spec_data == main_expected_returned_spec + + - block: + - name: "Test include_role of role C (missing a_int from `alternate` entry point)" + include_role: + name: c + vars: + c_int: 200 + a_str: "some string" + + - fail: + msg: "Should not get here" + + rescue: + - debug: + var: ansible_failed_result + - name: "Validate include_role failure" + assert: + that: + # NOTE: a bug here that prevents us from getting ansible_failed_task + - ansible_failed_result.argument_errors | length == 1 + - "'missing required arguments: a_int' in ansible_failed_result.argument_errors" + - ansible_failed_result.validate_args_context.argument_spec_name == "alternate" + - ansible_failed_result.validate_args_context.name == "a" + - ansible_failed_result.validate_args_context.type == "role" + - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/roles/a')" + - ansible_failed_result.argument_spec_data == alternate_expected_returned_spec + +- name: "New play to reset vars: Test role with no tasks can fail" + hosts: localhost + gather_facts: false + tasks: + - block: + - name: "Test import_role of role role_with_no_tasks (missing a_str)" + import_role: + name: role_with_no_tasks + + - 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 | length == 1 + - "'missing required arguments: a_str' in ansible_failed_result.argument_errors" + - ansible_failed_result.validate_args_context.argument_spec_name == "main" + - ansible_failed_result.validate_args_context.name == "role_with_no_tasks" + - ansible_failed_result.validate_args_context.type == "role" + - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/roles/role_with_no_tasks')" + +- name: "New play to reset vars: Test disabling role validation with rolespec_validate=False" + hosts: localhost + gather_facts: false + tasks: + - block: + - name: "Test import_role of role C (missing a_str), but validation turned off" + import_role: + name: c + rolespec_validate: False + - fail: + msg: "Should not get here" + + rescue: + - debug: + var: ansible_failed_result + - name: "Validate import_role failure" + assert: + that: + # We expect the role to actually run, but will fail because an undefined variable was referenced + # and validation wasn't performed up front (thus not returning 'argument_errors'). + - "'argument_errors' not in ansible_failed_result" + - "'The task includes an option with an undefined variable.' in ansible_failed_result.msg" + +- name: "New play to reset vars: Test collection-based role" + hosts: localhost + gather_facts: false + tasks: + - name: "Valid collection-based role usage" + import_role: + name: "foo.bar.blah" + vars: + blah_str: "some string" + + +- name: "New play to reset vars: Test collection-based role will fail" + hosts: localhost + gather_facts: false + tasks: + - block: + - name: "Invalid collection-based role usage" + import_role: + name: "foo.bar.blah" + - fail: + msg: "Should not get here" + rescue: + - debug: var=ansible_failed_result + - name: "Validate import_role failure for collection-based role" + assert: + that: + - ansible_failed_result.argument_errors | length == 1 + - "'missing required arguments: blah_str' in ansible_failed_result.argument_errors" + - ansible_failed_result.validate_args_context.argument_spec_name == "main" + - ansible_failed_result.validate_args_context.name == "blah" + - ansible_failed_result.validate_args_context.type == "role" + - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/collections/ansible_collections/foo/bar/roles/blah')" + +- name: "New play to reset vars: Test templating succeeds" + hosts: localhost + gather_facts: false + vars: + value_some_choices: "choice2" + value_some_list: [1.5] + value_some_dict: {"some_key": "some_value"} + value_some_int: 1 + value_some_float: 1.5 + value_some_json: '{[1, 3, 3] 345345|45v<#!}' + value_some_jsonarg: {"foo": [1, 3, 3]} + value_some_second_level: True + value_third_level: 5 + tasks: + - block: + - include_role: + name: test1 + vars: + some_choices: "{{ value_some_choices }}" + some_list: "{{ value_some_list }}" + some_dict: "{{ value_some_dict }}" + some_int: "{{ value_some_int }}" + some_float: "{{ value_some_float }}" + some_json: "{{ value_some_json }}" + some_jsonarg: "{{ value_some_jsonarg }}" + some_dict_options: + some_second_level: "{{ value_some_second_level }}" + multi_level_option: + second_level: + third_level: "{{ value_third_level }}" + rescue: + - debug: var=ansible_failed_result + - fail: + msg: "Should not get here" diff --git a/test/integration/targets/roles_arg_spec/test_complex_role_fails.yml b/test/integration/targets/roles_arg_spec/test_complex_role_fails.yml new file mode 100644 index 00000000..a04785fb --- /dev/null +++ b/test/integration/targets/roles_arg_spec/test_complex_role_fails.yml @@ -0,0 +1,180 @@ +--- +- name: "Running include_role test1" + hosts: localhost + gather_facts: false + vars: + ansible_unicode_type_match: "<type 'ansible.parsing.yaml.objects.AnsibleUnicode'>" + unicode_type_match: "<type 'unicode'>" + string_type_match: "<type 'str'>" + float_type_match: "<type 'float'>" + list_type_match: "<type 'list'>" + ansible_list_type_match: "<type 'ansible.parsing.yaml.objects.AnsibleSequence'>" + dict_type_match: "<type 'dict'>" + ansible_dict_type_match: "<type 'ansible.parsing.yaml.objects.AnsibleMapping'>" + ansible_unicode_class_match: "<class 'ansible.parsing.yaml.objects.AnsibleUnicode'>" + unicode_class_match: "<class 'unicode'>" + string_class_match: "<class 'str'>" + bytes_class_match: "<class 'bytes'>" + float_class_match: "<class 'float'>" + list_class_match: "<class 'list'>" + ansible_list_class_match: "<class 'ansible.parsing.yaml.objects.AnsibleSequence'>" + dict_class_match: "<class 'dict'>" + ansible_dict_class_match: "<class 'ansible.parsing.yaml.objects.AnsibleMapping'>" + expected: + test1_1: + argument_errors: [ + "argument 'tidy_expected' is of type <class 'ansible.parsing.yaml.objects.AnsibleMapping'> and we were unable to convert to list: <class 'ansible.parsing.yaml.objects.AnsibleMapping'> cannot be converted to a list", + "argument 'bust_some_stuff' is of type <class 'str'> and we were unable to convert to int: <class 'str'> cannot be converted to an int", + "argument 'some_list' is of type <class 'ansible.parsing.yaml.objects.AnsibleMapping'> and we were unable to convert to list: <class 'ansible.parsing.yaml.objects.AnsibleMapping'> cannot be converted to a list", + "argument 'some_dict' is of type <class 'ansible.parsing.yaml.objects.AnsibleSequence'> and we were unable to convert to dict: <class 'ansible.parsing.yaml.objects.AnsibleSequence'> cannot be converted to a dict", + "argument 'some_int' is of type <class 'float'> and we were unable to convert to int: <class 'float'> cannot be converted to an int", + "argument 'some_float' is of type <class 'str'> and we were unable to convert to float: <class 'str'> cannot be converted to a float", + "argument 'some_bytes' is of type <class 'bytes'> and we were unable to convert to bytes: <class 'bytes'> cannot be converted to a Byte value", + "argument 'some_bits' is of type <class 'str'> and we were unable to convert to bits: <class 'str'> cannot be converted to a Bit value", + "value of test1_choices must be one of: this paddle game, the astray, this remote control, the chair, got: My dog", + "value of some_choices must be one of: choice1, choice2, got: choice4", + "argument 'some_second_level' is of type <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> found in 'some_dict_options'. and we were unable to convert to bool: The value 'not-a-bool' is not a valid boolean. ", + "argument 'third_level' is of type <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> found in 'multi_level_option -> second_level'. and we were unable to convert to int: <class 'ansible.parsing.yaml.objects.AnsibleUnicode'> cannot be converted to an int" + ] + + tasks: + # This test play requires jinja >= 2.7 + - name: get the jinja2 version + shell: python -c 'import jinja2; print(jinja2.__version__)' + register: jinja2_version + delegate_to: localhost + changed_when: false + + - debug: + msg: "Jinja version: {{ jinja2_version.stdout }}" + + - name: include_role test1 since it has a arg_spec.yml + block: + - include_role: + name: test1 + vars: + tidy_expected: + some_key: some_value + test1_var1: 37.4 + test1_choices: "My dog" + bust_some_stuff: "some_string_that_is_not_an_int" + some_choices: "choice4" + some_str: 37.5 + some_list: {'a': false} + some_dict: + - "foo" + - "bar" + some_int: 37. + some_float: "notafloatisit" + some_path: "anything_is_a_valid_path" + some_raw: {"anything_can_be": "a_raw_type"} + # not sure what would be an invalid jsonarg + # some_jsonarg: "not sure what this does yet" + some_json: | + '{[1, 3, 3] 345345|45v<#!}' + some_jsonarg: | + {"foo": [1, 3, 3]} + # not sure we can load binary in safe_load + some_bytes: !!binary | + R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 + OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ + +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC + AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= + some_bits: "foo" + # some_str_nicknames: [] + # some_str_akas: {} + some_str_removed_in: "foo" + some_dict_options: + some_second_level: "not-a-bool" + multi_level_option: + second_level: + third_level: "should_be_int" + + - fail: + msg: "Should not get here" + + rescue: + - debug: + var: ansible_failed_result + + - name: replace py version specific types with generic names so tests work on py2 and py3 + set_fact: + # We want to compare if the actual failure messages and the expected failure messages + # are the same. But to compare and do set differences, we have to handle some + # differences between py2/py3. + # The validation failure messages include python type and class reprs, which are + # different between py2 and py3. For ex, "<type 'str'>" vs "<class 'str'>". Plus + # the usual py2/py3 unicode/str/bytes type shenanigans. The 'THE_FLOAT_REPR' is + # because py3 quotes the value in the error while py2 does not, so we just ignore + # the rest of the line. + actual_generic: "{{ ansible_failed_result.argument_errors| + map('replace', ansible_unicode_type_match, 'STR')| + map('replace', unicode_type_match, 'STR')| + map('replace', string_type_match, 'STR')| + map('replace', float_type_match, 'FLOAT')| + map('replace', list_type_match, 'LIST')| + map('replace', ansible_list_type_match, 'LIST')| + map('replace', dict_type_match, 'DICT')| + map('replace', ansible_dict_type_match, 'DICT')| + map('replace', ansible_unicode_class_match, 'STR')| + map('replace', unicode_class_match, 'STR')| + map('replace', string_class_match, 'STR')| + map('replace', bytes_class_match, 'STR')| + map('replace', float_class_match, 'FLOAT')| + map('replace', list_class_match, 'LIST')| + map('replace', ansible_list_class_match, 'LIST')| + map('replace', dict_class_match, 'DICT')| + map('replace', ansible_dict_class_match, 'DICT')| + map('regex_replace', '''float:.*$''', 'THE_FLOAT_REPR')| + map('regex_replace', 'Valid booleans include.*$', '')| + list }}" + expected_generic: "{{ expected.test1_1.argument_errors| + map('replace', ansible_unicode_type_match, 'STR')| + map('replace', unicode_type_match, 'STR')| + map('replace', string_type_match, 'STR')| + map('replace', float_type_match, 'FLOAT')| + map('replace', list_type_match, 'LIST')| + map('replace', ansible_list_type_match, 'LIST')| + map('replace', dict_type_match, 'DICT')| + map('replace', ansible_dict_type_match, 'DICT')| + map('replace', ansible_unicode_class_match, 'STR')| + map('replace', unicode_class_match, 'STR')| + map('replace', string_class_match, 'STR')| + map('replace', bytes_class_match, 'STR')| + map('replace', float_class_match, 'FLOAT')| + map('replace', list_class_match, 'LIST')| + map('replace', ansible_list_class_match, 'LIST')| + map('replace', dict_class_match, 'DICT')| + map('replace', ansible_dict_class_match, 'DICT')| + map('regex_replace', '''float:.*$''', 'THE_FLOAT_REPR')| + map('regex_replace', 'Valid booleans include.*$', '')| + list }}" + + - name: figure out the difference between expected and actual validate_argument_spec failures + set_fact: + actual_not_in_expected: "{{ actual_generic| difference(expected_generic) | sort() }}" + expected_not_in_actual: "{{ expected_generic | difference(actual_generic) | sort() }}" + + - name: assert that all actual validate_argument_spec failures were in expected + assert: + that: + - actual_not_in_expected | length == 0 + msg: "Actual validate_argument_spec failures that were not expected: {{ actual_not_in_expected }}" + + - name: assert that all expected validate_argument_spec failures were in expected + assert: + that: + - expected_not_in_actual | length == 0 + msg: "Expected validate_argument_spec failures that were not in actual results: {{ expected_not_in_actual }}" + + - name: assert that `validate_args_context` return value has what we expect + assert: + that: + - ansible_failed_result.validate_args_context.argument_spec_name == "main" + - ansible_failed_result.validate_args_context.name == "test1" + - ansible_failed_result.validate_args_context.type == "role" + - "ansible_failed_result.validate_args_context.path is search('roles_arg_spec/roles/test1')" + + # skip this task if jinja isnt >= 2.7, aka centos6 + when: + - jinja2_version.stdout is version('2.7', '>=') diff --git a/test/integration/targets/roles_arg_spec/test_play_level_role_fails.yml b/test/integration/targets/roles_arg_spec/test_play_level_role_fails.yml new file mode 100644 index 00000000..6c79569d --- /dev/null +++ b/test/integration/targets/roles_arg_spec/test_play_level_role_fails.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + gather_facts: false + roles: + - { role: a, invalid_str: "roles" } diff --git a/test/integration/targets/roles_arg_spec/test_tags.yml b/test/integration/targets/roles_arg_spec/test_tags.yml new file mode 100644 index 00000000..b4ea188b --- /dev/null +++ b/test/integration/targets/roles_arg_spec/test_tags.yml @@ -0,0 +1,11 @@ +--- +- hosts: localhost + gather_facts: false + tasks: + - name: "Tag test #1" + import_role: + name: a + vars: + a_str: "tag test #1" + tags: + - foo diff --git a/test/integration/targets/run_modules/library/test.py b/test/integration/targets/run_modules/library/test.py index bbe3182c..15a92e91 100644 --- a/test/integration/targets/run_modules/library/test.py +++ b/test/integration/targets/run_modules/library/test.py @@ -1,5 +1,8 @@ #!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + from ansible.module_utils.basic import AnsibleModule module = AnsibleModule(argument_spec=dict()) diff --git a/test/integration/targets/script/files/no_shebang.py b/test/integration/targets/script/files/no_shebang.py index c6c813af..f2d386a0 100644 --- a/test/integration/targets/script/files/no_shebang.py +++ b/test/integration/targets/script/files/no_shebang.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import sys sys.stdout.write("Script with shebang omitted") diff --git a/test/integration/targets/service/files/ansible_test_service.py b/test/integration/targets/service/files/ansible_test_service.py index c4feed85..522493fc 100644 --- a/test/integration/targets/service/files/ansible_test_service.py +++ b/test/integration/targets/service/files/ansible_test_service.py @@ -3,6 +3,9 @@ # this is mostly based off of the code found here: # http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import os import resource import signal diff --git a/test/integration/targets/service/tasks/main.yml b/test/integration/targets/service/tasks/main.yml index 69a9ef20..4fc2ddfe 100644 --- a/test/integration/targets/service/tasks/main.yml +++ b/test/integration/targets/service/tasks/main.yml @@ -1,3 +1,7 @@ +- name: skip unsupported distros + meta: end_host + when: ansible_distribution in ['Alpine'] + - name: install the test daemon script copy: src: ansible_test_service.py diff --git a/test/integration/targets/service/tasks/tests.yml b/test/integration/targets/service/tasks/tests.yml index de66bf5c..cfb42152 100644 --- a/test/integration/targets/service/tasks/tests.yml +++ b/test/integration/targets/service/tasks/tests.yml @@ -11,6 +11,39 @@ that: - "enable_in_check_mode_result is changed" +- name: (check mode run) test that service defaults are used + service: + register: enable_in_check_mode_result + check_mode: yes + module_defaults: + service: + name: ansible_test + enabled: yes + +- name: assert that changes reported for check mode run + assert: + that: + - "enable_in_check_mode_result is changed" + +- name: (check mode run) test that specific module defaults are used + service: + register: enable_in_check_mode_result + check_mode: yes + when: "ansible_service_mgr in ['sysvinit', 'systemd']" + module_defaults: + sysvinit: + name: ansible_test + enabled: yes + systemd: + name: ansible_test + enabled: yes + +- name: assert that changes reported for check mode run + assert: + that: + - "enable_in_check_mode_result is changed" + when: "ansible_service_mgr in ['sysvinit', 'systemd']" + - name: enable the ansible test service service: name=ansible_test enabled=yes register: enable_result diff --git a/test/integration/targets/service_facts/tasks/main.yml b/test/integration/targets/service_facts/tasks/main.yml index 5a08fad3..d2d6528b 100644 --- a/test/integration/targets/service_facts/tasks/main.yml +++ b/test/integration/targets/service_facts/tasks/main.yml @@ -3,6 +3,10 @@ # Copyright: (c) 2020, Abhijeet Kasurde <akasurde@redhat.com> # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +- name: skip broken distros + meta: end_host + when: ansible_distribution == 'Alpine' + - name: Gather service facts service_facts: diff --git a/test/integration/targets/set_fact/runme.sh b/test/integration/targets/set_fact/runme.sh index 364798a1..781894a0 100755 --- a/test/integration/targets/set_fact/runme.sh +++ b/test/integration/targets/set_fact/runme.sh @@ -26,5 +26,8 @@ fi ansible-playbook -i inventory --flush-cache "$@" set_fact_no_cache.yml # Test boolean conversions in set_fact -ansible-playbook -v set_fact_bool_conv.yml +ANSIBLE_JINJA2_NATIVE=0 ansible-playbook -v set_fact_bool_conv.yml ANSIBLE_JINJA2_NATIVE=1 ansible-playbook -v set_fact_bool_conv_jinja2_native.yml + +# Test parsing of values when using an empty string as a key +ansible-playbook -i inventory set_fact_empty_str_key.yml diff --git a/test/integration/targets/set_fact/set_fact_empty_str_key.yml b/test/integration/targets/set_fact/set_fact_empty_str_key.yml new file mode 100644 index 00000000..28631901 --- /dev/null +++ b/test/integration/targets/set_fact/set_fact_empty_str_key.yml @@ -0,0 +1,15 @@ +- name: Test set_fact for empty string as a key + hosts: testhost + gather_facts: no + vars: + value: 1 + tasks: + - name: Define fact with key as an empty string + set_fact: + foo: + "": "bar{{ value }}" + + - name: Verify the parsed value of the key + assert: + that: + - foo == {"":"bar1"} diff --git a/test/integration/targets/setup_cron/tasks/main.yml b/test/integration/targets/setup_cron/tasks/main.yml index 93dcefa5..c5a988e0 100644 --- a/test/integration/targets/setup_cron/tasks/main.yml +++ b/test/integration/targets/setup_cron/tasks/main.yml @@ -19,17 +19,23 @@ - when: faketime_pkg | default(false, true) block: - - name: install cron and faketime packages + - name: install faketime packages package: name: '{{ faketime_pkg }}' register: faketime_package_installed until: faketime_package_installed is success + when: ansible_distribution != 'Alpine' + + - name: install faketime packages - Alpine + command: apk add -U {{ faketime_pkg }} --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing + when: ansible_distribution == 'Alpine' - name: Find libfaketime path shell: '{{ list_pkg_files }} {{ faketime_pkg }} | grep -F libfaketime.so.1' args: warn: false register: libfaketime_path + when: list_pkg_files is defined - when: ansible_service_mgr == 'systemd' block: @@ -68,3 +74,11 @@ daemon-reload: "{{ (ansible_service_mgr == 'systemd') | ternary(true, omit) }}" name: '{{ cron_service }}' state: restarted + when: ansible_distribution != 'Alpine' + +- name: enable cron service - Alpine + command: nohup crond + environment: + FAKETIME: "+0y x10" + LD_PRELOAD: "/usr/lib/faketime/libfaketime.so.1" + when: ansible_distribution == 'Alpine' diff --git a/test/integration/targets/setup_cron/vars/alpine.yml b/test/integration/targets/setup_cron/vars/alpine.yml new file mode 100644 index 00000000..37e6fc37 --- /dev/null +++ b/test/integration/targets/setup_cron/vars/alpine.yml @@ -0,0 +1 @@ +faketime_pkg: libfaketime diff --git a/test/integration/targets/setup_epel/tasks/main.yml b/test/integration/targets/setup_epel/tasks/main.yml index c279810e..1c41e13e 100644 --- a/test/integration/targets/setup_epel/tasks/main.yml +++ b/test/integration/targets/setup_epel/tasks/main.yml @@ -1,5 +1,5 @@ - name: Install EPEL yum: - name: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/setup_epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm + name: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/setup_epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm disable_gpg_check: true when: ansible_facts.distribution in ['RedHat', 'CentOS'] 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 new file mode 100644 index 00000000..f16d9b53 --- /dev/null +++ b/test/integration/targets/setup_paramiko/install-Alpine-3-python-3.yml @@ -0,0 +1,9 @@ +- 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 diff --git a/test/integration/targets/setup_paramiko/install-FreeBSD-11.4-python-3.yml b/test/integration/targets/setup_paramiko/install-FreeBSD-11.4-python-3.yml new file mode 100644 index 00000000..9a7bfb67 --- /dev/null +++ b/test/integration/targets/setup_paramiko/install-FreeBSD-11.4-python-3.yml @@ -0,0 +1,3 @@ +- name: Install Paramiko for Python 3 on FreeBSD 11.4 + pkgng: + name: py37-paramiko diff --git a/test/integration/targets/setup_paramiko/install-FreeBSD-12.2-python-3.yml b/test/integration/targets/setup_paramiko/install-FreeBSD-12.2-python-3.yml new file mode 100644 index 00000000..4fe6011b --- /dev/null +++ b/test/integration/targets/setup_paramiko/install-FreeBSD-12.2-python-3.yml @@ -0,0 +1,3 @@ +- name: Install Paramiko for Python 3 on FreeBSD 12.2 + pkgng: + name: py37-paramiko diff --git a/test/integration/targets/setup_paramiko/install.yml b/test/integration/targets/setup_paramiko/install.yml index e98abe33..1868987d 100644 --- a/test/integration/targets/setup_paramiko/install.yml +++ b/test/integration/targets/setup_paramiko/install.yml @@ -11,6 +11,7 @@ when: not detect_paramiko.found include_tasks: "{{ item }}" with_first_found: + - "install-{{ ansible_distribution }}-{{ ansible_distribution_version }}-python-{{ ansible_python.version.major }}.yml" - "install-{{ ansible_distribution }}-{{ ansible_distribution_major_version }}-python-{{ ansible_python.version.major }}.yml" - "install-{{ ansible_os_family }}-{{ ansible_distribution_major_version }}-python-{{ ansible_python.version.major }}.yml" - "install-{{ ansible_os_family }}-python-{{ ansible_python.version.major }}.yml" diff --git a/test/integration/targets/setup_paramiko/setup.sh b/test/integration/targets/setup_paramiko/setup.sh index 316320c3..9f7afcbb 100644 --- a/test/integration/targets/setup_paramiko/setup.sh +++ b/test/integration/targets/setup_paramiko/setup.sh @@ -3,7 +3,6 @@ set -eux -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh # for pip installs, if needed, otherwise unused ANSIBLE_ROLES_PATH=../ ansible-playbook ../setup_paramiko/install.yml -i ../setup_paramiko/inventory "$@" trap 'ansible-playbook ../setup_paramiko/uninstall.yml -i ../setup_paramiko/inventory "$@"' EXIT 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 new file mode 100644 index 00000000..e9dcc62c --- /dev/null +++ b/test/integration/targets/setup_paramiko/uninstall-Alpine-3-python-3.yml @@ -0,0 +1,4 @@ +- name: Uninstall Paramiko for Python 3 on Alpine + pip: + name: paramiko + state: absent diff --git a/test/integration/targets/setup_paramiko/uninstall-FreeBSD-11.4-python-3.yml b/test/integration/targets/setup_paramiko/uninstall-FreeBSD-11.4-python-3.yml new file mode 100644 index 00000000..86956fd9 --- /dev/null +++ b/test/integration/targets/setup_paramiko/uninstall-FreeBSD-11.4-python-3.yml @@ -0,0 +1,4 @@ +- name: Uninstall Paramiko for Python 3 on FreeBSD 11.4 + pkgng: + name: py37-paramiko + state: absent diff --git a/test/integration/targets/setup_paramiko/uninstall-FreeBSD-12.2-python-3.yml b/test/integration/targets/setup_paramiko/uninstall-FreeBSD-12.2-python-3.yml new file mode 100644 index 00000000..0359bf4c --- /dev/null +++ b/test/integration/targets/setup_paramiko/uninstall-FreeBSD-12.2-python-3.yml @@ -0,0 +1,4 @@ +- name: Uninstall Paramiko for Python 3 on FreeBSD 12.2 + pkgng: + name: py37-paramiko + state: absent diff --git a/test/integration/targets/setup_paramiko/uninstall.yml b/test/integration/targets/setup_paramiko/uninstall.yml index 48ff68e6..e517a2b9 100644 --- a/test/integration/targets/setup_paramiko/uninstall.yml +++ b/test/integration/targets/setup_paramiko/uninstall.yml @@ -8,6 +8,7 @@ - name: Uninstall Paramiko include_tasks: "{{ item }}" with_first_found: + - "uninstall-{{ ansible_distribution }}-{{ ansible_distribution_version }}-python-{{ ansible_python.version.major }}.yml" - "uninstall-{{ ansible_distribution }}-{{ ansible_distribution_major_version }}-python-{{ ansible_python.version.major }}.yml" - "uninstall-{{ ansible_os_family }}-{{ ansible_distribution_major_version }}-python-{{ ansible_python.version.major }}.yml" - "uninstall-{{ ansible_os_family }}-python-{{ ansible_python.version.major }}.yml" diff --git a/test/integration/targets/setup_rpm_repo/files/create-repo.py b/test/integration/targets/setup_rpm_repo/files/create-repo.py index 2033fdf8..a4d10140 100644 --- a/test/integration/targets/setup_rpm_repo/files/create-repo.py +++ b/test/integration/targets/setup_rpm_repo/files/create-repo.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type import sys from collections import namedtuple diff --git a/test/integration/targets/setup_rpm_repo/library/create_repo.py b/test/integration/targets/setup_rpm_repo/library/create_repo.py index 6dc1e457..e6a61bad 100644 --- a/test/integration/targets/setup_rpm_repo/library/create_repo.py +++ b/test/integration/targets/setup_rpm_repo/library/create_repo.py @@ -3,49 +3,59 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -import os +import subprocess import sys import tempfile from collections import namedtuple from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.respawn import has_respawned, probe_interpreters_for_module, respawn_module +HAS_RPMFLUFF = True +can_use_rpm_weak_deps = None try: from rpmfluff import SimpleRpmBuild from rpmfluff import YumRepoBuild except ImportError: - from rpmfluff.rpmbuild import SimpleRpmBuild - from rpmfluff.yumrepobuild import YumRepoBuild + try: + from rpmfluff.rpmbuild import SimpleRpmBuild + from rpmfluff.yumrepobuild import YumRepoBuild + except ImportError: + HAS_RPMFLUFF = False -try: - from rpmfluff import can_use_rpm_weak_deps -except ImportError: +can_use_rpm_weak_deps = None +if HAS_RPMFLUFF: try: - from rpmfluff.utils import can_use_rpm_weak_deps + from rpmfluff import can_use_rpm_weak_deps except ImportError: - can_use_rpm_weak_deps = None + try: + from rpmfluff.utils import can_use_rpm_weak_deps + except ImportError: + pass + -RPM = namedtuple('RPM', ['name', 'version', 'release', 'epoch', 'recommends']) +RPM = namedtuple('RPM', ['name', 'version', 'release', 'epoch', 'recommends', 'arch']) SPECS = [ - RPM('dinginessentail', '1.0', '1', None, None), - RPM('dinginessentail', '1.0', '2', '1', None), - RPM('dinginessentail', '1.1', '1', '1', None), - RPM('dinginessentail-olive', '1.0', '1', None, None), - RPM('dinginessentail-olive', '1.1', '1', None, None), - RPM('landsidescalping', '1.0', '1', None, None), - RPM('landsidescalping', '1.1', '1', None, None), - RPM('dinginessentail-with-weak-dep', '1.0', '1', None, ['dinginessentail-weak-dep']), - RPM('dinginessentail-weak-dep', '1.0', '1', None, None), + RPM('dinginessentail', '1.0', '1', None, None, None), + RPM('dinginessentail', '1.0', '2', '1', None, None), + RPM('dinginessentail', '1.1', '1', '1', None, None), + RPM('dinginessentail-olive', '1.0', '1', None, None, None), + RPM('dinginessentail-olive', '1.1', '1', None, None, None), + RPM('landsidescalping', '1.0', '1', None, None, None), + RPM('landsidescalping', '1.1', '1', None, None, None), + RPM('dinginessentail-with-weak-dep', '1.0', '1', None, ['dinginessentail-weak-dep'], None), + RPM('dinginessentail-weak-dep', '1.0', '1', None, None, None), + RPM('noarchfake', '1.0', '1', None, None, 'noarch'), ] def create_repo(arch='x86_64'): pkgs = [] for spec in SPECS: - pkg = SimpleRpmBuild(spec.name, spec.version, spec.release, [arch]) + pkg = SimpleRpmBuild(spec.name, spec.version, spec.release, [spec.arch or arch]) pkg.epoch = spec.epoch if spec.recommends: @@ -58,8 +68,19 @@ def create_repo(arch='x86_64'): pkgs.append(pkg) - repo = YumRepoBuild(pkgs) - repo.make(arch) + # HACK: EPEL6 version of rpmfluff can't do multi-arch packaging, so we'll just build separately and copy + # the noarch stuff in, since we don't currently care about the repodata for noarch + if sys.version_info[0:2] == (2, 6): + noarch_repo = YumRepoBuild([p for p in pkgs if 'noarch' in p.get_build_archs()]) + noarch_repo.make('noarch') + + repo = YumRepoBuild([p for p in pkgs if arch in p.get_build_archs()]) + repo.make(arch) + + subprocess.call("cp {0}/*.rpm {1}".format(noarch_repo.repoDir, repo.repoDir), shell=True) + else: + repo = YumRepoBuild(pkgs) + repo.make(arch, 'noarch') for pkg in pkgs: pkg.clean() @@ -75,6 +96,16 @@ def main(): } ) + if not HAS_RPMFLUFF: + system_interpreters = ['/usr/libexec/platform-python', '/usr/bin/python3', '/usr/bin/python'] + + interpreter = probe_interpreters_for_module(system_interpreters, 'rpmfluff') + + if not interpreter or has_respawned(): + module.fail_json('unable to find rpmfluff; tried {0}'.format(system_interpreters)) + + respawn_module(interpreter) + arch = module.params['arch'] tempdir = module.params['tempdir'] diff --git a/test/integration/targets/setup_rpm_repo/tasks/main.yml b/test/integration/targets/setup_rpm_repo/tasks/main.yml index a6766f20..b2c9ae1b 100644 --- a/test/integration/targets/setup_rpm_repo/tasks/main.yml +++ b/test/integration/targets/setup_rpm_repo/tasks/main.yml @@ -24,13 +24,6 @@ args: name: "{{ rpm_repo_packages }}" - - name: Install rpmfluff from pip on RHEL 8 and later - pip: - name: rpmfluff - when: - - ansible_facts.distribution in ['RedHat', 'CentOS'] - - ansible_facts.distribution_major_version is version('8', '>=') - - set_fact: repos: - "fake-{{ ansible_architecture }}" diff --git a/test/integration/targets/setup_rpm_repo/vars/RedHat-8.yml b/test/integration/targets/setup_rpm_repo/vars/RedHat-8.yml index 84849e23..6e149335 100644 --- a/test/integration/targets/setup_rpm_repo/vars/RedHat-8.yml +++ b/test/integration/targets/setup_rpm_repo/vars/RedHat-8.yml @@ -2,3 +2,4 @@ rpm_repo_packages: - rpm-build - createrepo_c - createrepo + - python3-rpmfluff diff --git a/test/integration/targets/shell/connection_plugins/test_connection_default.py b/test/integration/targets/shell/connection_plugins/test_connection_default.py index 52b027d0..60feedd6 100644 --- a/test/integration/targets/shell/connection_plugins/test_connection_default.py +++ b/test/integration/targets/shell/connection_plugins/test_connection_default.py @@ -25,9 +25,6 @@ class Connection(ConnectionBase): def __init__(self, *args, **kwargs): super(Connection, self).__init__(*args, **kwargs) - def transport(self): - pass - def _connect(self): pass diff --git a/test/integration/targets/shell/connection_plugins/test_connection_override.py b/test/integration/targets/shell/connection_plugins/test_connection_override.py index 56d531c4..d26d2b52 100644 --- a/test/integration/targets/shell/connection_plugins/test_connection_override.py +++ b/test/integration/targets/shell/connection_plugins/test_connection_override.py @@ -26,9 +26,6 @@ class Connection(ConnectionBase): self._shell_type = 'powershell' # Set a shell type that is not sh super(Connection, self).__init__(*args, **kwargs) - def transport(self): - pass - def _connect(self): pass diff --git a/test/integration/targets/slurp/defaults/main.yml b/test/integration/targets/slurp/defaults/main.yml new file mode 100644 index 00000000..05d1041e --- /dev/null +++ b/test/integration/targets/slurp/defaults/main.yml @@ -0,0 +1 @@ +become_test_user: testuser diff --git a/test/integration/targets/slurp/handlers/main.yml b/test/integration/targets/slurp/handlers/main.yml new file mode 100644 index 00000000..eeda7ced --- /dev/null +++ b/test/integration/targets/slurp/handlers/main.yml @@ -0,0 +1,6 @@ +- name: remove test user and their home dir + user: + name: "{{ become_test_user }}" + state: absent + remove: yes + force: yes diff --git a/test/integration/targets/slurp/tasks/main.yml b/test/integration/targets/slurp/tasks/main.yml index 4f3556fa..6d14f4b0 100644 --- a/test/integration/targets/slurp/tasks/main.yml +++ b/test/integration/targets/slurp/tasks/main.yml @@ -54,37 +54,6 @@ - "slurp_binary is not changed" - "slurp_binary is not failed" -- name: test slurping a non-existent file - slurp: - src: '{{ output_dir }}/i_do_not_exist' - register: slurp_missing - ignore_errors: true - -- name: check slurp missing result - assert: - that: - - "slurp_missing is failed" - - "slurp_missing.msg" - - "slurp_missing is not changed" - -- name: Create a directory to test with - file: - path: '{{ output_dir }}/baz/' - state: directory - -- name: test slurping a directory - slurp: - src: '{{ output_dir }}/baz' - register: slurp_dir - ignore_errors: true - -- name: check slurp directory result - assert: - that: - - "slurp_dir is failed" - - "slurp_dir.msg" - - "slurp_dir is not changed" - - name: test slurp with missing argument action: slurp register: slurp_no_args @@ -96,3 +65,5 @@ - "slurp_no_args is failed" - "slurp_no_args.msg" - "slurp_no_args is not changed" + +- import_tasks: test_unreadable.yml diff --git a/test/integration/targets/slurp/tasks/test_unreadable.yml b/test/integration/targets/slurp/tasks/test_unreadable.yml new file mode 100644 index 00000000..da5e36af --- /dev/null +++ b/test/integration/targets/slurp/tasks/test_unreadable.yml @@ -0,0 +1,81 @@ +- name: test slurping a non-existent file + slurp: + src: '{{ output_dir }}/i_do_not_exist' + register: slurp_missing + ignore_errors: yes + +- name: Create a directory to test with + file: + path: '{{ output_dir }}/baz/' + state: directory + +- name: test slurping a directory + slurp: + src: '{{ output_dir }}/baz' + register: slurp_dir + ignore_errors: yes + +# Ensure unreadable file and directory handling and error messages +# https://github.com/ansible/ansible/issues/67340 +- name: create test user + user: + name: "{{ become_test_user }}" + create_home: yes + notify: + - "remove test user and their home dir" + +- name: create unreadable file + copy: + content: "Hello, World!" + dest: "{{ output_dir }}/qux.txt" + mode: '0600' + owner: root + +- name: test slurp unreadable file + slurp: + src: "{{ output_dir }}/qux.txt" + register: slurp_unreadable_file + become: yes + become_user: "{{ become_test_user }}" + become_method: su + ignore_errors: yes + +- name: create unreadable directory + file: + path: "{{ output_dir }}/test_data" + state: directory + mode: '0700' + owner: root + +- name: test slurp unreadable directory + slurp: + src: "{{ output_dir }}/test_data" + register: slurp_unreadable_dir + become: yes + become_user: "{{ become_test_user }}" + become_method: su + ignore_errors: yes + +- name: Try to access file as directory + slurp: + src: "{{ output_dir }}/qux.txt/somefile" + ignore_errors: yes + register: slurp_path_file_as_dir + +- name: check slurp failures + assert: + that: + - slurp_missing is failed + - slurp_missing.msg is search('file not found') + - slurp_missing is not changed + - slurp_unreadable_file is failed + - slurp_unreadable_file.msg is regex('^file is not readable:') + - slurp_unreadable_file is not changed + - slurp_unreadable_dir is failed + - slurp_unreadable_dir.msg is regex('^file is not readable:') + - slurp_unreadable_dir is not changed + - slurp_path_file_as_dir is failed + - slurp_path_file_as_dir is search('unable to slurp file') + - slurp_dir is failed + - slurp_dir.msg is search('source is a directory and must be a file') + - slurp_dir is not changed diff --git a/test/integration/targets/stat/tasks/main.yml b/test/integration/targets/stat/tasks/main.yml index bd6b1e89..285e2b83 100644 --- a/test/integration/targets/stat/tasks/main.yml +++ b/test/integration/targets/stat/tasks/main.yml @@ -155,3 +155,25 @@ - "'xgrp' in stat_result.stat" - "'xoth' in stat_result.stat" - "'xusr' in stat_result.stat" + +- name: make a new file with colon in filename + copy: + dest: "{{ output_dir }}/foo:bar.txt" + mode: '0644' + content: "hello world" + +- name: check stat of a file with colon in name + stat: + path: "{{ output_dir }}/foo:bar.txt" + follow: True + register: stat_result + +- debug: + var: stat_result + +- assert: + that: + - "'changed' in stat_result" + - "stat_result.changed == false" + - "stat_result.stat.mimetype == 'text/plain'" + - "stat_result.stat.charset == 'us-ascii'" diff --git a/test/integration/targets/strategy_linear/inventory b/test/integration/targets/strategy_linear/inventory index 4a34c320..698d69dc 100644 --- a/test/integration/targets/strategy_linear/inventory +++ b/test/integration/targets/strategy_linear/inventory @@ -1,3 +1,3 @@ [local] -testhost ansible_connection=local -testhost2 ansible_connection=local +testhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" +testhost2 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/test/integration/targets/strategy_linear/runme.sh b/test/integration/targets/strategy_linear/runme.sh index 41639f3c..cbb6aea3 100755 --- a/test/integration/targets/strategy_linear/runme.sh +++ b/test/integration/targets/strategy_linear/runme.sh @@ -3,3 +3,5 @@ set -eux ansible-playbook test_include_file_noop.yml -i inventory "$@" + +ansible-playbook task_action_templating.yml -i inventory "$@" diff --git a/test/integration/targets/strategy_linear/task_action_templating.yml b/test/integration/targets/strategy_linear/task_action_templating.yml new file mode 100644 index 00000000..5f7438fe --- /dev/null +++ b/test/integration/targets/strategy_linear/task_action_templating.yml @@ -0,0 +1,26 @@ +- hosts: testhost,testhost2 + gather_facts: no + tasks: + - set_fact: + module_to_run: 'debug' + when: inventory_hostname == 'testhost' + + - set_fact: + module_to_run: 'ping' + when: inventory_hostname == 'testhost2' + + - action: + module: '{{ module_to_run }}' + register: out + + - assert: + that: + - "'msg' in out" + - "'ping' not in out" + when: inventory_hostname == 'testhost' + + - assert: + that: + - "'ping' in out" + - "'msg' not in out" + when: inventory_hostname == 'testhost2' diff --git a/test/integration/targets/subversion/roles/subversion/defaults/main.yml b/test/integration/targets/subversion/roles/subversion/defaults/main.yml index af5ea026..f989345a 100644 --- a/test/integration/targets/subversion/roles/subversion/defaults/main.yml +++ b/test/integration/targets/subversion/roles/subversion/defaults/main.yml @@ -8,3 +8,4 @@ subversion_repo_url: http://127.0.0.1:{{ apache_port }}/svn/{{ subversion_repo_n subversion_repo_auth_url: http://127.0.0.1:{{ apache_port }}/svnauth/{{ subversion_repo_name }} subversion_username: subsvn_user''' subversion_password: Password123! +subversion_external_repo_url: https://github.com/ansible/ansible-base-test-container # GitHub serves SVN diff --git a/test/integration/targets/subversion/roles/subversion/tasks/setup.yml b/test/integration/targets/subversion/roles/subversion/tasks/setup.yml index 5c9c5cb5..cab9151a 100644 --- a/test/integration/targets/subversion/roles/subversion/tasks/setup.yml +++ b/test/integration/targets/subversion/roles/subversion/tasks/setup.yml @@ -11,6 +11,11 @@ package: name: '{{ subversion_packages }}' state: present + when: ansible_distribution != 'Alpine' + +- name: install SVN pre-reqs - Alpine + command: 'apk add -U -u {{ subversion_packages|join(" ") }}' + when: ansible_distribution == 'Alpine' - name: upgrade SVN pre-reqs package: @@ -55,9 +60,9 @@ - name: start test Apache SVN site - non Red Hat command: apachectl -k start -f {{ subversion_server_dir }}/subversion.conf - when: not ansible_os_family == 'RedHat' + when: ansible_os_family not in ['RedHat', 'Alpine'] # On Red Hat based OS', we can't use apachectl to start up own instance, just use the raw httpd - name: start test Apache SVN site - Red Hat command: httpd -k start -f {{ subversion_server_dir }}/subversion.conf - when: ansible_os_family == 'RedHat' + when: ansible_os_family in ['RedHat', 'Alpine'] diff --git a/test/integration/targets/subversion/roles/subversion/tasks/tests.yml b/test/integration/targets/subversion/roles/subversion/tasks/tests.yml index 8421f9de..b8f85d95 100644 --- a/test/integration/targets/subversion/roles/subversion/tasks/tests.yml +++ b/test/integration/targets/subversion/roles/subversion/tasks/tests.yml @@ -130,4 +130,16 @@ - "export_branches.stat.isdir" - "subverted4.changed" +- name: clone a small external repo with validate_certs=true + subversion: + repo: "{{ subversion_external_repo_url }}" + dest: "{{ subversion_test_dir }}/svn-external1" + validate_certs: yes + +- name: clone a small external repo with validate_certs=false + subversion: + repo: "{{ subversion_external_repo_url }}" + dest: "{{ subversion_test_dir }}/svn-external2" + validate_certs: no + # TBA: test for additional options or URL variants welcome diff --git a/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2 b/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2 index 07e7083a..86f40707 100644 --- a/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2 +++ b/test/integration/targets/subversion/roles/subversion/templates/subversion.conf.j2 @@ -39,6 +39,10 @@ LoadModule authz_svn_module libexec/apache24/mod_authz_svn.so Include /etc/apache2/httpd.conf LoadModule dav_module /usr/lib64/apache2/mod_dav.so LoadModule dav_svn_module /usr/lib64/apache2/mod_dav_svn.so +{% elif ansible_os_family == "Alpine" %} +Include /etc/apache2/httpd.conf +LoadModule dav_module /usr/lib/apache2/mod_dav.so +LoadModule dav_svn_module /usr/lib/apache2/mod_dav_svn.so {% elif ansible_os_family == "RedHat" %} Include /etc/httpd/conf/httpd.conf {% endif %} diff --git a/test/integration/targets/subversion/vars/Alpine.yml b/test/integration/targets/subversion/vars/Alpine.yml new file mode 100644 index 00000000..ce071fdd --- /dev/null +++ b/test/integration/targets/subversion/vars/Alpine.yml @@ -0,0 +1,7 @@ +--- +subversion_packages: +- subversion +- mod_dav_svn +- apache2-webdav +apache_user: apache +apache_group: apache diff --git a/test/integration/targets/subversion/vars/Debian.yml b/test/integration/targets/subversion/vars/Debian.yml index bf7c2084..dfe131b0 100644 --- a/test/integration/targets/subversion/vars/Debian.yml +++ b/test/integration/targets/subversion/vars/Debian.yml @@ -1,6 +1,6 @@ --- subversion_packages: - subversion -- libapache2-svn +- libapache2-mod-svn apache_user: www-data apache_group: www-data diff --git a/test/integration/targets/tags/ansible_run_tags.yml b/test/integration/targets/tags/ansible_run_tags.yml new file mode 100644 index 00000000..0e965ad2 --- /dev/null +++ b/test/integration/targets/tags/ansible_run_tags.yml @@ -0,0 +1,49 @@ +--- +- name: verify ansible_run_tags work as expected + hosts: testhost + gather_facts: False + tasks: + - debug: + var: ansible_run_tags + tags: + - always + + - debug: + var: expect + tags: + - always + + - assert: + that: + - ansible_run_tags == ['all'] + when: expect == 'all' + tags: + - always + + - assert: + that: + - ansible_run_tags|sort == ['tag1', 'tag3'] + when: expect == 'list' + tags: + - always + + - assert: + that: + - ansible_run_tags == ['untagged'] + when: expect == 'untagged' + tags: + - always + + - assert: + that: + - ansible_run_tags|sort == ['tag3', 'untagged'] + when: expect == 'untagged_list' + tags: + - always + + - assert: + that: + - ansible_run_tags == ['tagged'] + when: expect == 'tagged' + tags: + - always diff --git a/test/integration/targets/tags/runme.sh b/test/integration/targets/tags/runme.sh index 949fbd5f..29849952 100755 --- a/test/integration/targets/tags/runme.sh +++ b/test/integration/targets/tags/runme.sh @@ -14,7 +14,7 @@ export LC_ALL=en_US.UTF-8 # Run everything by default [ "$("${COMMAND[@]}" | grep -F Task_with | xargs)" = \ -"Task_with_tag TAGS: [tag] Task_with_always_tag TAGS: [always] Task_with_unicode_tag TAGS: [くらとみ] Task_with_list_of_tags TAGS: [café, press] Task_without_tag TAGS: [] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3]" ] +"Task_with_tag TAGS: [tag] Task_with_always_tag TAGS: [always] Task_with_unicode_tag TAGS: [くらとみ] Task_with_list_of_tags TAGS: [café, press] Task_without_tag TAGS: [] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3] Task_with_meta_tags TAGS: [meta_tag]" ] # Run the exact tags, and always [ "$("${COMMAND[@]}" --tags tag | grep -F Task_with | xargs)" = \ @@ -22,11 +22,15 @@ export LC_ALL=en_US.UTF-8 # Skip one tag [ "$("${COMMAND[@]}" --skip-tags tag | grep -F Task_with | xargs)" = \ -"Task_with_always_tag TAGS: [always] Task_with_unicode_tag TAGS: [くらとみ] Task_with_list_of_tags TAGS: [café, press] Task_without_tag TAGS: [] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3]" ] +"Task_with_always_tag TAGS: [always] Task_with_unicode_tag TAGS: [くらとみ] Task_with_list_of_tags TAGS: [café, press] Task_without_tag TAGS: [] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3] Task_with_meta_tags TAGS: [meta_tag]" ] # Skip a unicode tag [ "$("${COMMAND[@]}" --skip-tags 'くらとみ' | grep -F Task_with | xargs)" = \ -"Task_with_tag TAGS: [tag] Task_with_always_tag TAGS: [always] Task_with_list_of_tags TAGS: [café, press] Task_without_tag TAGS: [] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3]" ] +"Task_with_tag TAGS: [tag] Task_with_always_tag TAGS: [always] Task_with_list_of_tags TAGS: [café, press] Task_without_tag TAGS: [] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3] Task_with_meta_tags TAGS: [meta_tag]" ] + +# Skip a meta task tag +[ "$("${COMMAND[@]}" --skip-tags meta_tag | grep -F Task_with | xargs)" = \ +"Task_with_tag TAGS: [tag] Task_with_always_tag TAGS: [always] Task_with_unicode_tag TAGS: [くらとみ] Task_with_list_of_tags TAGS: [café, press] Task_without_tag TAGS: [] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3]" ] # Run just a unicode tag and always [ "$("${COMMAND[@]}" --tags 'くらとみ' | grep -F Task_with | xargs)" = \ @@ -47,3 +51,28 @@ export LC_ALL=en_US.UTF-8 # Run templated tags [ "$("${COMMAND[@]}" --tags tag3 | grep -F Task_with | xargs)" = \ "Task_with_always_tag TAGS: [always] Task_with_templated_tags TAGS: [tag3]" ] + +# Run meta tags +[ "$("${COMMAND[@]}" --tags meta_tag | grep -F Task_with | xargs)" = \ +"Task_with_always_tag TAGS: [always] Task_with_meta_tags TAGS: [meta_tag]" ] + +# Run tagged +[ "$("${COMMAND[@]}" --tags tagged | grep -F Task_with | xargs)" = \ +"Task_with_tag TAGS: [tag] Task_with_always_tag TAGS: [always] Task_with_unicode_tag TAGS: [くらとみ] Task_with_list_of_tags TAGS: [café, press] Task_with_csv_tags TAGS: [tag1, tag2] Task_with_templated_tags TAGS: [tag3] Task_with_meta_tags TAGS: [meta_tag]" ] + +# Run untagged +[ "$("${COMMAND[@]}" --tags untagged | grep -F Task_with | xargs)" = \ +"Task_with_always_tag TAGS: [always] Task_without_tag TAGS: []" ] + +# Skip 'always' +[ "$("${COMMAND[@]}" --tags untagged --skip-tags always | grep -F Task_with | xargs)" = \ +"Task_without_tag TAGS: []" ] + +# Test ansible_run_tags +ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=all "$@" +ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=all --tags all "$@" +ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=list --tags tag1,tag3 "$@" +ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=list --tags tag1 --tags tag3 "$@" +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 "$@" diff --git a/test/integration/targets/tags/test_tags.yml b/test/integration/targets/tags/test_tags.yml index 76ac5ba4..f0f9a72d 100644 --- a/test/integration/targets/tags/test_tags.yml +++ b/test/integration/targets/tags/test_tags.yml @@ -31,3 +31,6 @@ - name: Task_with_templated_tags debug: msg=templated tags: "{{ the_tags }}" + - name: Task_with_meta_tags + meta: reset_connection + tags: meta_tag diff --git a/test/integration/targets/template/in_template_overrides.j2 b/test/integration/targets/template/in_template_overrides.j2 new file mode 100644 index 00000000..22476070 --- /dev/null +++ b/test/integration/targets/template/in_template_overrides.j2 @@ -0,0 +1,5 @@ +#jinja2:variable_start_string:'<<' , variable_end_string:'>>' +var_a: << var_a >> +var_b: << var_b >> +var_c: << var_c >> +var_d: << var_d >> diff --git a/test/integration/targets/template/in_template_overrides.yml b/test/integration/targets/template/in_template_overrides.yml new file mode 100644 index 00000000..3c2d4d99 --- /dev/null +++ b/test/integration/targets/template/in_template_overrides.yml @@ -0,0 +1,28 @@ +- hosts: localhost + gather_facts: false + vars: + var_a: "value" + var_b: "{{ var_a }}" + var_c: "<< var_a >>" + tasks: + - set_fact: + var_d: "{{ var_a }}" + + - block: + - template: + src: in_template_overrides.j2 + dest: out.txt + + - command: cat out.txt + register: out + + - assert: + that: + - "'var_a: value' in out.stdout" + - "'var_b: value' in out.stdout" + - "'var_c: << var_a >>' in out.stdout" + - "'var_d: value' in out.stdout" + always: + - file: + path: out.txt + state: absent diff --git a/test/integration/targets/template/role_filter/filter_plugins/myplugin.py b/test/integration/targets/template/role_filter/filter_plugins/myplugin.py index 44935ab0..b0a88894 100644 --- a/test/integration/targets/template/role_filter/filter_plugins/myplugin.py +++ b/test/integration/targets/template/role_filter/filter_plugins/myplugin.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + class FilterModule(object): def filters(self): diff --git a/test/integration/targets/template/runme.sh b/test/integration/targets/template/runme.sh index cb00df75..a4f0bbe5 100755 --- a/test/integration/targets/template/runme.sh +++ b/test/integration/targets/template/runme.sh @@ -34,3 +34,9 @@ ansible-playbook 6653.yml -v "$@" # https://github.com/ansible/ansible/issues/72262 ansible-playbook 72262.yml -v "$@" + +# ensure unsafe is preserved, even with extra newlines +ansible-playbook unsafe.yml -v "$@" + +# ensure Jinja2 overrides from a template are used +ansible-playbook in_template_overrides.yml -v "$@" diff --git a/test/integration/targets/template/tasks/main.yml b/test/integration/targets/template/tasks/main.yml index da803436..c5744d0d 100644 --- a/test/integration/targets/template/tasks/main.yml +++ b/test/integration/targets/template/tasks/main.yml @@ -715,5 +715,16 @@ assert: that: "\"'y' is undefined\" in error.msg" +- template: + src: template_import_macro_globals.j2 + dest: "{{ output_dir }}/template_import_macro_globals.templated" + +- command: "cat {{ output_dir }}/template_import_macro_globals.templated" + register: out + +- assert: + that: + - out.stdout == "bar=lookedup_bar" + # aliases file requires root for template tests so this should be safe - include: backup_test.yml diff --git a/test/integration/targets/template/templates/macro_using_globals.j2 b/test/integration/targets/template/templates/macro_using_globals.j2 new file mode 100644 index 00000000..d8d0626d --- /dev/null +++ b/test/integration/targets/template/templates/macro_using_globals.j2 @@ -0,0 +1,3 @@ +{% macro foo(bar) -%} +{{ bar }}={{ lookup('lines', 'echo lookedup_bar') }} +{%- endmacro %} diff --git a/test/integration/targets/template/templates/template_import_macro_globals.j2 b/test/integration/targets/template/templates/template_import_macro_globals.j2 new file mode 100644 index 00000000..9b9a9c68 --- /dev/null +++ b/test/integration/targets/template/templates/template_import_macro_globals.j2 @@ -0,0 +1,2 @@ +{% from 'macro_using_globals.j2' import foo %} +{{ foo('bar') }} diff --git a/test/integration/targets/template/unsafe.yml b/test/integration/targets/template/unsafe.yml new file mode 100644 index 00000000..6746e1ea --- /dev/null +++ b/test/integration/targets/template/unsafe.yml @@ -0,0 +1,19 @@ +- hosts: localhost + gather_facts: false + vars: + nottemplated: this should not be seen + imunsafe: !unsafe '{{ nottemplated }}' + tasks: + + - set_fact: + this_was_unsafe: > + {{ imunsafe }} + + - set_fact: + this_always_safe: '{{ imunsafe }}' + + - name: ensure nothing was templated + assert: + that: + - this_always_safe == imunsafe + - imunsafe == this_was_unsafe.strip() diff --git a/test/integration/targets/template_jinja2_latest/aliases b/test/integration/targets/template_jinja2_latest/aliases index 8602d059..2a89ae7e 100644 --- a/test/integration/targets/template_jinja2_latest/aliases +++ b/test/integration/targets/template_jinja2_latest/aliases @@ -2,3 +2,4 @@ needs/root shippable/posix/group2 needs/target/template skip/aix +needs/file/test/lib/ansible_test/_data/requirements/constraints.txt diff --git a/test/integration/targets/template_jinja2_latest/pip-requirements.txt b/test/integration/targets/template_jinja2_latest/pip-requirements.txt new file mode 100644 index 00000000..fdd9ec5c --- /dev/null +++ b/test/integration/targets/template_jinja2_latest/pip-requirements.txt @@ -0,0 +1,4 @@ +# pip 7.1 added support for constraints, which are required by ansible-test to install most python requirements +# see https://github.com/pypa/pip/blame/e648e00dc0226ade30ade99591b245b0c98e86c9/NEWS.rst#L1258 +pip >= 7.1, < 10 ; python_version < '2.7' # pip 10+ drops support for python 2.6 (sanity_ok) +pip >= 7.1 ; python_version >= '2.7' # sanity_ok diff --git a/test/integration/targets/template_jinja2_latest/runme.sh b/test/integration/targets/template_jinja2_latest/runme.sh index d6a09677..d6ef693e 100755 --- a/test/integration/targets/template_jinja2_latest/runme.sh +++ b/test/integration/targets/template_jinja2_latest/runme.sh @@ -2,10 +2,11 @@ set -eux -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh -pip install -U -r requirements.txt +pip install --requirement pip-requirements.txt + +pip install -U -r requirements.txt --constraint "../../../lib/ansible_test/_data/requirements/constraints.txt" ANSIBLE_ROLES_PATH=../ export ANSIBLE_ROLES_PATH diff --git a/test/integration/targets/template_jinja2_non_native/46169.yml b/test/integration/targets/template_jinja2_non_native/46169.yml new file mode 100644 index 00000000..efb443ea --- /dev/null +++ b/test/integration/targets/template_jinja2_non_native/46169.yml @@ -0,0 +1,32 @@ +- hosts: localhost + gather_facts: no + tasks: + - set_fact: + output_dir: "{{ lookup('env', 'OUTPUT_DIR') }}" + + - template: + src: templates/46169.json.j2 + dest: "{{ output_dir }}/result.json" + + - command: "diff templates/46169.json.j2 {{ output_dir }}/result.json" + register: diff_result + + - assert: + that: + - diff_result.stdout == "" + + - block: + - set_fact: + non_native_lookup: "{{ lookup('template', 'templates/46169.json.j2') }}" + + - assert: + that: + - non_native_lookup | type_debug == 'NativeJinjaUnsafeText' + + - set_fact: + native_lookup: "{{ lookup('template', 'templates/46169.json.j2', jinja2_native=true) }}" + + - assert: + that: + - native_lookup | type_debug == 'dict' + when: lookup('pipe', ansible_python_interpreter ~ ' -c "import jinja2; print(jinja2.__version__)"') is version('2.10', '>=') diff --git a/test/integration/targets/template_jinja2_non_native/aliases b/test/integration/targets/template_jinja2_non_native/aliases new file mode 100644 index 00000000..b5983214 --- /dev/null +++ b/test/integration/targets/template_jinja2_non_native/aliases @@ -0,0 +1 @@ +shippable/posix/group3 diff --git a/test/integration/targets/template_jinja2_non_native/runme.sh b/test/integration/targets/template_jinja2_non_native/runme.sh new file mode 100755 index 00000000..fe9d495a --- /dev/null +++ b/test/integration/targets/template_jinja2_non_native/runme.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -eux + +export ANSIBLE_JINJA2_NATIVE=1 +ansible-playbook 46169.yml -v "$@" +unset ANSIBLE_JINJA2_NATIVE diff --git a/test/integration/targets/template_jinja2_non_native/templates/46169.json.j2 b/test/integration/targets/template_jinja2_non_native/templates/46169.json.j2 new file mode 100644 index 00000000..a4fc3f67 --- /dev/null +++ b/test/integration/targets/template_jinja2_non_native/templates/46169.json.j2 @@ -0,0 +1,3 @@ +{ + "key": "bar" +} diff --git a/test/integration/targets/templating_lookups/runme.sh b/test/integration/targets/templating_lookups/runme.sh index e958bcfb..ebfc0d11 100755 --- a/test/integration/targets/templating_lookups/runme.sh +++ b/test/integration/targets/templating_lookups/runme.sh @@ -9,4 +9,4 @@ ansible-playbook template_lookup_vaulted/playbook.yml --vault-password-file temp ansible-playbook template_deepcopy/playbook.yml -i template_deepcopy/hosts "$@" # https://github.com/ansible/ansible/issues/66943 -ansible-playbook template_lookup_safe_eval_unicode/playbook.yml "$@" +ANSIBLE_JINJA2_NATIVE=0 ansible-playbook template_lookup_safe_eval_unicode/playbook.yml "$@" diff --git a/test/integration/targets/test_core/tasks/main.yml b/test/integration/targets/test_core/tasks/main.yml index 50c43581..9f753e3c 100644 --- a/test/integration/targets/test_core/tasks/main.yml +++ b/test/integration/targets/test_core/tasks/main.yml @@ -87,7 +87,7 @@ ignore_errors: yes register: misuse_of_changed -# providing artificial task results since there are no modules in ansible-base that provide a 'results' list instead of 'changed' +# providing artificial task results since there are no modules in ansible-core that provide a 'results' list instead of 'changed' - name: Prepare artificial task results set_fact: results_all_changed: @@ -217,6 +217,42 @@ ignore_errors: yes register: version_bad_value +- name: Try version with both strict and version_type + debug: + msg: "{{ '1.0' is version('1.0', strict=False, version_type='loose') }}" + ignore_errors: yes + register: version_strict_version_type + +- name: Try version with bad version_type + debug: + msg: "{{ '1.0' is version('1.0', version_type='boom') }}" + ignore_errors: yes + register: version_bad_version_type + +- name: Try version with bad semver + debug: + msg: "{{ 'nope' is version('nopenope', version_type='semver') }}" + ignore_errors: yes + register: version_bad_semver + +- name: Try version with empty input value + debug: + msg: "{{ '' is version('1.0', '>') }}" + ignore_errors: yes + register: version_empty_input + +- name: Try version with empty comparison value + debug: + msg: "{{ '1.0' is version('', '>') }}" + ignore_errors: yes + register: version_empty_comparison + +- name: Try version with empty input and comparison values + debug: + msg: "{{ '' is version('', '>') }}" + ignore_errors: yes + register: version_empty_both + - name: Assert version tests work assert: that: @@ -234,8 +270,18 @@ - "'2.0' is version('1.0', '>', true)" - "'1.0' is version('1.0', '<=', true)" - "'1.0' is version('1.0', '>=', true)" + - "'1.2.3' is version('2.0.0', 'lt', version_type='semver')" - version_bad_operator is failed - version_bad_value is failed + - version_strict_version_type is failed + - version_bad_version_type is failed + - version_bad_semver is failed + - version_empty_input is failed + - version_empty_input is search('version value cannot be empty') + - version_empty_comparison is failed + - version_empty_comparison is search('to compare against cannot be empty') + - version_empty_both is failed + - version_empty_both is search('version value cannot be empty') - name: Assert any tests work assert: @@ -266,7 +312,7 @@ - '"off" is not truthy(convert_bool=True)' - '"fred" is truthy(convert_bool=True)' - '{} is not truthy' - - '{"key": "value"} is truthy' + - '{"key": "value"} is truthy' - name: Assert falsy tests work assert: diff --git a/test/integration/targets/unarchive/handlers/main.yml b/test/integration/targets/unarchive/handlers/main.yml new file mode 100644 index 00000000..cb8b671e --- /dev/null +++ b/test/integration/targets/unarchive/handlers/main.yml @@ -0,0 +1,3 @@ +- name: restore packages + package: + name: "{{ unarchive_packages }}" diff --git a/test/integration/targets/unarchive/tasks/main.yml b/test/integration/targets/unarchive/tasks/main.yml index 7051539c..035a5561 100644 --- a/test/integration/targets/unarchive/tasks/main.yml +++ b/test/integration/targets/unarchive/tasks/main.yml @@ -1,11 +1,14 @@ - import_tasks: prepare_tests.yml +- import_tasks: test_missing_binaries.yml - import_tasks: test_tar.yml - import_tasks: test_tar_gz.yml - import_tasks: test_tar_gz_creates.yml - import_tasks: test_tar_gz_owner_group.yml - import_tasks: test_tar_gz_keep_newer.yml +- import_tasks: test_tar_zst.yml - import_tasks: test_zip.yml - import_tasks: test_exclude.yml +- import_tasks: test_include.yml - import_tasks: test_parent_not_writeable.yml - import_tasks: test_mode.yml - import_tasks: test_quotable_characters.yml diff --git a/test/integration/targets/unarchive/tasks/prepare_tests.yml b/test/integration/targets/unarchive/tasks/prepare_tests.yml index 783d77d3..98a8ba11 100644 --- a/test/integration/targets/unarchive/tasks/prepare_tests.yml +++ b/test/integration/targets/unarchive/tasks/prepare_tests.yml @@ -1,9 +1,10 @@ +- name: Include system specific variables + include_vars: "{{ ansible_facts.system }}.yml" + # Need unzip for unarchive module, and zip for archive creation. -- name: Ensure zip & unzip are present +- name: Ensure required binaries are present package: - name: - - zip - - unzip + name: "{{ unarchive_packages }}" when: ansible_pkg_mgr in ('yum', 'dnf', 'apt', 'pkgng') - name: prep our file @@ -18,6 +19,28 @@ - name: prep a tar.gz file shell: tar czvf test-unarchive.tar.gz foo-unarchive.txt chdir={{remote_tmp_dir}} +- name: see if we have the zstd executable + ignore_errors: true + shell: zstd --version + register: zstd_available + +- when: zstd_available.rc == 0 + block: + - name: find gnu tar + shell: | + #!/bin/sh + which gtar 2>/dev/null + if test $? -ne 0; then + if test -z "`tar --version | grep bsdtar`"; then + which tar + fi + fi + register: gnu_tar + + - name: prep a tar.zst file + shell: "{{ gnu_tar.stdout }} --use-compress-program=zstd -cvf test-unarchive.tar.zst foo-unarchive.txt chdir={{remote_tmp_dir}}" + when: gnu_tar.stdout != "" + - name: prep a chmodded file for zip copy: src: foo.txt @@ -89,4 +112,4 @@ mode: preserve - name: prep a tar.gz file with directory - shell: tar czvf test-unarchive-dir.tar.gz unarchive-dir chdir={{remote_tmp_dir}} + shell: tar czvf test-unarchive-dir.tar.gz unarchive-dir chdir={{remote_tmp_dir}} diff --git a/test/integration/targets/unarchive/tasks/test_exclude.yml b/test/integration/targets/unarchive/tasks/test_exclude.yml index be24756c..bf9f14fb 100644 --- a/test/integration/targets/unarchive/tasks/test_exclude.yml +++ b/test/integration/targets/unarchive/tasks/test_exclude.yml @@ -37,12 +37,3 @@ file: path: '{{remote_tmp_dir}}/test-unarchive-zip' state: absent - -- name: remove our test files for the archive - file: - path: '{{remote_tmp_dir}}/{{item}}' - state: absent - with_items: - - foo-unarchive.txt - - foo-unarchive-777.txt - - FOO-UNAR.TXT diff --git a/test/integration/targets/unarchive/tasks/test_include.yml b/test/integration/targets/unarchive/tasks/test_include.yml new file mode 100644 index 00000000..3ed30fa3 --- /dev/null +++ b/test/integration/targets/unarchive/tasks/test_include.yml @@ -0,0 +1,83 @@ +- name: Create a tar file with multiple files + shell: tar cvf test-unarchive-multi.tar foo-unarchive-777.txt foo-unarchive.txt + args: + chdir: "{{ remote_tmp_dir }}" + +- name: Create include test directories + file: + state: directory + path: "{{ remote_tmp_dir }}/{{ item }}" + loop: + - include-zip + - include-tar + +- name: Unpack zip file include one file + unarchive: + src: "{{ remote_tmp_dir }}/test-unarchive.zip" + dest: "{{ remote_tmp_dir }}/include-zip" + include: + - FOO-UNAR.TXT + +- name: Verify that single file was unarchived + find: + paths: "{{ remote_tmp_dir }}/include-zip" + register: unarchive_dir02 + +# The map filter was added in Jinja2 2.7, which is newer than the version on RHEL/CentOS 6, +# so we skip this validation on those hosts +- name: Verify that zip extraction included only one file + assert: + that: + - file_names == ['FOO-UNAR.TXT'] + vars: + file_names: "{{ unarchive_dir02.files | map(attribute='path') | map('basename') }}" + when: + - "ansible_facts.os_family == 'RedHat'" + - ansible_facts.distribution_major_version is version('7', '>=') + +- name: Unpack tar file include one file + unarchive: + src: "{{ remote_tmp_dir }}/test-unarchive-multi.tar" + dest: "{{ remote_tmp_dir }}/include-tar" + include: + - foo-unarchive-777.txt + +- name: verify that single file was unarchived from tar + find: + paths: "{{ remote_tmp_dir }}/include-tar" + register: unarchive_dir03 + +- name: Verify that tar extraction included only one file + assert: + that: + - file_names == ['foo-unarchive-777.txt'] + vars: + file_names: "{{ unarchive_dir03.files | map(attribute='path') | map('basename') }}" + when: + - "ansible_facts.os_family == 'RedHat'" + - ansible_facts.distribution_major_version is version('7', '>=') + +- name: Check mutually exclusive parameters + unarchive: + src: "{{ remote_tmp_dir }}/test-unarchive-multi.tar" + dest: "{{ remote_tmp_dir }}/include-tar" + include: + - foo-unarchive-777.txt + exclude: + - foo + ignore_errors: yes + register: unarchive_mutually_exclusive_check + +- name: Check mutually exclusive parameters + assert: + that: + - unarchive_mutually_exclusive_check is failed + - "'mutually exclusive' in unarchive_mutually_exclusive_check.msg" + +- name: "Remove include feature tests directory" + file: + state: absent + path: "{{ remote_tmp_dir }}/{{ item }}" + loop: + - 'include-zip' + - 'include-tar' diff --git a/test/integration/targets/unarchive/tasks/test_missing_binaries.yml b/test/integration/targets/unarchive/tasks/test_missing_binaries.yml new file mode 100644 index 00000000..f8e45367 --- /dev/null +++ b/test/integration/targets/unarchive/tasks/test_missing_binaries.yml @@ -0,0 +1,56 @@ +- name: Test missing binaries + when: ansible_pkg_mgr in ('yum', 'dnf', 'apt', 'pkgng') + block: + - name: Remove zip binaries + package: + state: absent + name: + - zip + - unzip + notify: restore packages + + - name: create unarchive destinations + file: + path: '{{ remote_tmp_dir }}/test-unarchive-{{ item }}' + state: directory + loop: + - zip + - tar + + # With the zip binaries absent and tar still present, this task should work + - name: unarchive a tar file + unarchive: + src: '{{remote_tmp_dir}}/test-unarchive.tar' + dest: '{{remote_tmp_dir}}/test-unarchive-tar' + remote_src: yes + register: tar + + - name: unarchive a zip file + unarchive: + src: '{{remote_tmp_dir}}/test-unarchive.zip' + dest: '{{remote_tmp_dir}}/test-unarchive-zip' + list_files: True + remote_src: yes + register: zip_fail + ignore_errors: yes + + - name: Ensure tasks worked as expected + assert: + that: + - tar is success + - zip_fail is failed + - zip_fail.msg is search('Unable to find required') + + - name: Remove unarchive destinations + file: + path: '{{ remote_tmp_dir }}/test-unarchive-{{ item }}' + state: absent + loop: + - zip + - tar + + - name: Reinsntall zip binaries + package: + name: + - zip + - unzip diff --git a/test/integration/targets/unarchive/tasks/test_owner_group.yml b/test/integration/targets/unarchive/tasks/test_owner_group.yml new file mode 100644 index 00000000..95f1457e --- /dev/null +++ b/test/integration/targets/unarchive/tasks/test_owner_group.yml @@ -0,0 +1,162 @@ +- block: + - name: Create a group to chown to + group: + name: testgroup + register: testgroup + + - name: Create a user to chown to + user: + name: testuser + groups: + - testgroup + register: testuser + + - set_fact: + outdir: '{{remote_tmp_dir}}/test-unarchive-{{ ext | replace(".", "_") }}' + + - debug: + msg: Username test + + # username + - name: create our unarchive destinations + file: + path: '{{outdir}}' + state: directory + + - name: unarchive a file + unarchive: + src: '{{remote_tmp_dir}}/{{archive}}' + dest: '{{outdir}}' + list_files: True + remote_src: yes + owner: testuser + + - name: stat an output file + stat: + path: '{{outdir}}/{{testfile}}' + register: stat + + - name: verify that the file has the right owner + assert: + that: + - stat.stat.exists + - stat.stat.pw_name == testuser.name + - stat.stat.uid == testuser.uid + + - name: nuke destination + file: + path: '{{outdir}}' + state: absent + + - debug: + msg: uid test + + # uid + - name: create our unarchive destinations + file: + path: '{{outdir}}' + state: directory + + - name: unarchive a file + unarchive: + src: '{{remote_tmp_dir}}/{{archive}}' + dest: '{{outdir}}' + list_files: True + remote_src: yes + owner: '{{ testuser.uid }}' + + - name: stat an output file + stat: + path: '{{outdir}}/{{testfile}}' + register: stat + + - name: verify that the file has the right owner + assert: + that: + - stat.stat.exists + - stat.stat.pw_name == testuser.name + - stat.stat.uid == testuser.uid + + - name: nuke destination + file: + path: '{{outdir}}' + state: absent + + - debug: + msg: groupname test + + # groupname + - name: create our unarchive destinations + file: + path: '{{outdir}}' + state: directory + + - name: unarchive a file + unarchive: + src: '{{remote_tmp_dir}}/{{archive}}' + dest: '{{outdir}}' + list_files: True + remote_src: yes + group: testgroup + + - name: stat an output file + stat: + path: '{{outdir}}/{{testfile}}' + register: stat + + - name: verify that the file has the right owner + assert: + that: + - stat.stat.exists + - stat.stat.gr_name == testgroup.name + - stat.stat.gid == testgroup.gid + + - name: nuke destination + file: + path: '{{outdir}}' + state: absent + + - debug: + msg: gid test + + # gid + - name: create our unarchive destinations + file: + path: '{{outdir}}' + state: directory + + - name: unarchive a file + unarchive: + src: '{{remote_tmp_dir}}/{{archive}}' + dest: '{{outdir}}' + list_files: True + remote_src: yes + group: '{{ testgroup.gid }}' + + - name: stat an output file + stat: + path: '{{outdir}}/{{testfile}}' + register: stat + + - name: verify that the file has the right owner + assert: + that: + - stat.stat.exists + - stat.stat.gr_name == testgroup.name + - stat.stat.gid == testgroup.gid + + - name: nuke destination + file: + path: '{{outdir}}' + state: absent + + always: + - name: Remove testuser + user: + name: testuser + state: absent + + - name: Remove testgroup + group: + name: testgroup + state: absent diff --git a/test/integration/targets/unarchive/tasks/test_tar.yml b/test/integration/targets/unarchive/tasks/test_tar.yml index 09105c60..0a020410 100644 --- a/test/integration/targets/unarchive/tasks/test_tar.yml +++ b/test/integration/targets/unarchive/tasks/test_tar.yml @@ -24,3 +24,10 @@ file: path: '{{remote_tmp_dir}}/test-unarchive-tar' state: absent + +- name: test owner/group perms + include_tasks: test_owner_group.yml + vars: + ext: tar + archive: test-unarchive.tar + testfile: foo-unarchive.txt diff --git a/test/integration/targets/unarchive/tasks/test_tar_gz.yml b/test/integration/targets/unarchive/tasks/test_tar_gz.yml index ac9e9a15..e88f77b6 100644 --- a/test/integration/targets/unarchive/tasks/test_tar_gz.yml +++ b/test/integration/targets/unarchive/tasks/test_tar_gz.yml @@ -26,3 +26,10 @@ file: path: '{{remote_tmp_dir}}/test-unarchive-tar-gz' state: absent + +- name: test owner/group perms + include_tasks: test_owner_group.yml + vars: + ext: tar.gz + archive: test-unarchive.tar.gz + testfile: foo-unarchive.txt diff --git a/test/integration/targets/unarchive/tasks/test_tar_zst.yml b/test/integration/targets/unarchive/tasks/test_tar_zst.yml new file mode 100644 index 00000000..18b12815 --- /dev/null +++ b/test/integration/targets/unarchive/tasks/test_tar_zst.yml @@ -0,0 +1,40 @@ +# Only do this whole file when the "zstd" executable is present +- when: + - zstd_available.rc == 0 + - gnu_tar.stdout != "" + block: + - name: create our tar.zst unarchive destination + file: + path: '{{remote_tmp_dir}}/test-unarchive-tar-zst' + state: directory + + - name: unarchive a tar.zst file + unarchive: + src: '{{remote_tmp_dir}}/test-unarchive.tar.zst' + dest: '{{remote_tmp_dir}}/test-unarchive-tar-zst' + remote_src: yes + register: unarchive02 + + - name: verify that the file was marked as changed + assert: + that: + - "unarchive02.changed == true" + # Verify that no file list is generated + - "'files' not in unarchive02" + + - name: verify that the file was unarchived + file: + path: '{{remote_tmp_dir}}/test-unarchive-tar-zst/foo-unarchive.txt' + state: file + + - name: remove our tar.zst unarchive destination + file: + path: '{{remote_tmp_dir}}/test-unarchive-tar-zst' + state: absent + + - name: test owner/group perms + include_tasks: test_owner_group.yml + vars: + ext: tar.zst + archive: test-unarchive.tar.zst + testfile: foo-unarchive.txt diff --git a/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml b/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml index 6181e3bd..e4c2bec5 100644 --- a/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml +++ b/test/integration/targets/unarchive/tasks/test_unprivileged_user.yml @@ -1,7 +1,6 @@ - name: Create unarchivetest1 user user: name: unarchivetest1 - uid: 1002610001 group: "{{ group_table[ansible_facts['distribution']] | default(omit) }}" register: user vars: diff --git a/test/integration/targets/unarchive/tasks/test_zip.yml b/test/integration/targets/unarchive/tasks/test_zip.yml index aae57d8e..cf03946f 100644 --- a/test/integration/targets/unarchive/tasks/test_zip.yml +++ b/test/integration/targets/unarchive/tasks/test_zip.yml @@ -43,3 +43,15 @@ assert: that: - "unarchive03b.changed == false" + +- name: nuke zip destination + file: + path: '{{remote_tmp_dir}}/test-unarchive-zip' + state: absent + +- name: test owner/group perms + include_tasks: test_owner_group.yml + vars: + ext: zip + archive: test-unarchive.zip + testfile: foo-unarchive.txt diff --git a/test/integration/targets/unarchive/vars/Darwin.yml b/test/integration/targets/unarchive/vars/Darwin.yml new file mode 100644 index 00000000..902feed6 --- /dev/null +++ b/test/integration/targets/unarchive/vars/Darwin.yml @@ -0,0 +1 @@ +unarchive_packages: [] diff --git a/test/integration/targets/unarchive/vars/FreeBSD.yml b/test/integration/targets/unarchive/vars/FreeBSD.yml new file mode 100644 index 00000000..0f5c401c --- /dev/null +++ b/test/integration/targets/unarchive/vars/FreeBSD.yml @@ -0,0 +1,4 @@ +unarchive_packages: + - unzip + - zip + - zstd diff --git a/test/integration/targets/unarchive/vars/Linux.yml b/test/integration/targets/unarchive/vars/Linux.yml new file mode 100644 index 00000000..61109348 --- /dev/null +++ b/test/integration/targets/unarchive/vars/Linux.yml @@ -0,0 +1,4 @@ +unarchive_packages: + - tar + - unzip + - zip diff --git a/test/integration/targets/unsafe_writes/basic.yml b/test/integration/targets/unsafe_writes/basic.yml index b173c7f8..410726ad 100644 --- a/test/integration/targets/unsafe_writes/basic.yml +++ b/test/integration/targets/unsafe_writes/basic.yml @@ -38,7 +38,7 @@ - copy_without is failed - name: test overwriting file with unsafe - copy: content=NEW dest={{testufile}} unsafe_writes=True + copy: content=NEWNOREALLY dest={{testufile}} unsafe_writes=True register: copy_with - name: ensure we properly changed @@ -46,6 +46,21 @@ that: - copy_with is changed + - name: test fallback env var + when: lookup('env', 'ANSIBLE_UNSAFE_WRITES') not in ('', None) + vars: + env_enabled: "{{lookup('env', 'ANSIBLE_UNSAFE_WRITES')|bool}}" + block: + - name: test overwriting file with unsafe depending on fallback environment setting + copy: content=NEWBUTNOTDIFFERENT dest={{testufile}} + register: copy_with_env + ignore_errors: True + + - name: ensure we properly follow env var + assert: + msg: "Failed with envvar: {{env_enabled}}, due AUW: to {{q('env', 'ANSIBLE_UNSAFE_WRITES')}}" + that: + - env_enabled and copy_with_env is changed or not env_enabled and copy_with_env is failed always: - name: remove immutable flag from dir to prevent issues with cleanup file: path={{testudir}} state=directory attributes="-i" diff --git a/test/integration/targets/unsafe_writes/runme.sh b/test/integration/targets/unsafe_writes/runme.sh index 5c37f727..791a5676 100755 --- a/test/integration/targets/unsafe_writes/runme.sh +++ b/test/integration/targets/unsafe_writes/runme.sh @@ -2,4 +2,11 @@ set -eux +# test w/o fallback env var ansible-playbook basic.yml -i ../../inventory -e "output_dir=${OUTPUT_DIR}" "$@" + +# test enabled fallback env var +ANSIBLE_UNSAFE_WRITES=1 ansible-playbook basic.yml -i ../../inventory -e "output_dir=${OUTPUT_DIR}" "$@" + +# test disnabled fallback env var +ANSIBLE_UNSAFE_WRITES=0 ansible-playbook basic.yml -i ../../inventory -e "output_dir=${OUTPUT_DIR}" "$@" diff --git a/test/integration/targets/until/action_plugins/shell_no_failed.py b/test/integration/targets/until/action_plugins/shell_no_failed.py new file mode 100644 index 00000000..594c0140 --- /dev/null +++ b/test/integration/targets/until/action_plugins/shell_no_failed.py @@ -0,0 +1,28 @@ +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + del tmp # tmp no longer has any effect + + try: + self._task.args['_raw_params'] = self._task.args.pop('cmd') + except KeyError: + pass + shell_action = self._shared_loader_obj.action_loader.get('ansible.legacy.shell', + task=self._task, + connection=self._connection, + play_context=self._play_context, + loader=self._loader, + templar=self._templar, + shared_loader_obj=self._shared_loader_obj) + result = shell_action.run(task_vars=task_vars) + result.pop('failed', None) + return result diff --git a/test/integration/targets/until/tasks/main.yml b/test/integration/targets/until/tasks/main.yml index 4a09ff3b..2b2ac94e 100644 --- a/test/integration/targets/until/tasks/main.yml +++ b/test/integration/targets/until/tasks/main.yml @@ -69,3 +69,16 @@ retries: 5 delay: 0.5 failed_when: changed_when_attempts.attempts > 6 + +# Test until on module that doesn't return failed, but does return rc +- name: create counter file + copy: + dest: "{{ output_dir }}/until_counter" + content: 3 + +- shell_no_failed: + cmd: | + COUNTER=$(cat "{{ output_dir }}/until_counter"); NEW=$(expr $COUNTER - 1); echo $NEW > "{{ output_dir }}/until_counter"; exit $COUNTER + register: counter + delay: 0.5 + until: counter.rc == 0 diff --git a/test/integration/targets/uri/files/testserver.py b/test/integration/targets/uri/files/testserver.py index 81043b66..24967d4f 100644 --- a/test/integration/targets/uri/files/testserver.py +++ b/test/integration/targets/uri/files/testserver.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import sys if __name__ == '__main__': diff --git a/test/integration/targets/uri/tasks/main.yml b/test/integration/targets/uri/tasks/main.yml index 409607af..4cefc6b3 100644 --- a/test/integration/targets/uri/tasks/main.yml +++ b/test/integration/targets/uri/tasks/main.yml @@ -131,6 +131,48 @@ - "stat_result.stat.exists == true" - "result.changed == true" +- name: "get ca certificate {{ self_signed_host }}" + get_url: + url: "http://{{ httpbin_host }}/ca2cert.pem" + dest: "{{ remote_tmp_dir }}/ca2cert.pem" + +- name: test https fetch to a site with self signed certificate using ca_path + uri: + url: "https://{{ self_signed_host }}:444/" + dest: "{{ output_dir }}/self-signed_using_ca_path.html" + ca_path: "{{ remote_tmp_dir }}/ca2cert.pem" + validate_certs: yes + register: result + +- stat: + path: "{{ output_dir }}/self-signed_using_ca_path.html" + register: stat_result + +- name: Assert that the file was downloaded + assert: + that: + - "stat_result.stat.exists == true" + - "result.changed == true" + +- name: test https fetch to a site with self signed certificate without using ca_path + uri: + url: "https://{{ self_signed_host }}:444/" + dest: "{{ output_dir }}/self-signed-without_using_ca_path.html" + validate_certs: yes + register: result + ignore_errors: true + +- stat: + path: "{{ output_dir }}/self-signed-without_using_ca_path.html" + register: stat_result + +- name: Assure that https access to a host with self-signed certificate without providing ca_path fails + assert: + that: + - "stat_result.stat.exists == false" + - result is failed + - "'certificate verify failed' in result.msg" + - name: test redirect without follow_redirects uri: url: 'https://{{ httpbin_host }}/redirect/2' @@ -598,3 +640,12 @@ - name: Check return-content import_tasks: return-content.yml + +- name: Test use_gssapi=True + include_tasks: + file: use_gssapi.yml + apply: + environment: + KRB5_CONFIG: '{{ krb5_config }}' + KRB5CCNAME: FILE:{{ remote_tmp_dir }}/krb5.cc + when: krb5_config is defined diff --git a/test/integration/targets/uri/tasks/redirect-none.yml b/test/integration/targets/uri/tasks/redirect-none.yml index 0f5ec68b..0d1b2b34 100644 --- a/test/integration/targets/uri/tasks/redirect-none.yml +++ b/test/integration/targets/uri/tasks/redirect-none.yml @@ -249,7 +249,7 @@ - http_308_head is failure - http_308_head.json is not defined - http_308_head.location == 'https://{{ httpbin_host }}/anything' - - "http_308_head.msg == 'Status code was 308 and not [200]: HTTP Error 308: UNKNOWN'" + - "'Status code was 308 and not [200]: HTTP Error 308: ' in http_308_head.msg" - 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' @@ -269,7 +269,7 @@ - http_308_get is failure - http_308_get.json is not defined - http_308_get.location == 'https://{{ httpbin_host }}/anything' - - "http_308_get.msg == 'Status code was 308 and not [200]: HTTP Error 308: UNKNOWN'" + - "'Status code was 308 and not [200]: HTTP Error 308: ' in http_308_get.msg" - 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' @@ -290,7 +290,7 @@ - http_308_post is failure - http_308_post.json is not defined - http_308_post.location == 'https://{{ httpbin_host }}/anything' - - "http_308_post.msg == 'Status code was 308 and not [200]: HTTP Error 308: UNKNOWN'" + - "'Status code was 308 and not [200]: HTTP Error 308: ' in http_308_post.msg" - http_308_post.redirected == false - http_308_post.status == 308 - http_308_post.url == 'https://{{ httpbin_host }}/redirect-to?status_code=308&url=https://{{ httpbin_host }}/anything' diff --git a/test/integration/targets/uri/tasks/redirect-safe.yml b/test/integration/targets/uri/tasks/redirect-safe.yml index c95dd5aa..bcc41696 100644 --- a/test/integration/targets/uri/tasks/redirect-safe.yml +++ b/test/integration/targets/uri/tasks/redirect-safe.yml @@ -268,7 +268,7 @@ - http_308_post is failure - http_308_post.json is not defined - http_308_post.location == 'https://{{ httpbin_host }}/anything' - - "http_308_post.msg == 'Status code was 308 and not [200]: HTTP Error 308: UNKNOWN'" + - "'Status code was 308 and not [200]: HTTP Error 308: ' in http_308_post.msg" - http_308_post.redirected == false - http_308_post.status == 308 - http_308_post.url == 'https://{{ httpbin_host }}/redirect-to?status_code=308&url=https://{{ httpbin_host }}/anything' diff --git a/test/integration/targets/uri/tasks/redirect-urllib2.yml b/test/integration/targets/uri/tasks/redirect-urllib2.yml index 10b115ee..6cdafdb2 100644 --- a/test/integration/targets/uri/tasks/redirect-urllib2.yml +++ b/test/integration/targets/uri/tasks/redirect-urllib2.yml @@ -246,7 +246,7 @@ - http_308_head is failure - http_308_head.json is not defined - http_308_head.location == 'https://{{ httpbin_host }}/anything' - - "http_308_head.msg == 'Status code was 308 and not [200]: HTTP Error 308: UNKNOWN'" + - "'Status code was 308 and not [200]: HTTP Error 308: ' in http_308_head.msg" - 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' @@ -266,7 +266,7 @@ - http_308_get is failure - http_308_get.json is not defined - http_308_get.location == 'https://{{ httpbin_host }}/anything' - - "http_308_get.msg == 'Status code was 308 and not [200]: HTTP Error 308: UNKNOWN'" + - "'Status code was 308 and not [200]: HTTP Error 308: ' in http_308_get.msg" - 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' @@ -288,7 +288,7 @@ - http_308_post is failure - http_308_post.json is not defined - http_308_post.location == 'https://{{ httpbin_host }}/anything' - - "http_308_post.msg == 'Status code was 308 and not [200]: HTTP Error 308: UNKNOWN'" + - "'Status code was 308 and not [200]: HTTP Error 308: ' in http_308_post.msg" - http_308_post.redirected == false - http_308_post.status == 308 - http_308_post.url == 'https://{{ httpbin_host }}/redirect-to?status_code=308&url=https://{{ httpbin_host }}/anything' diff --git a/test/integration/targets/uri/tasks/use_gssapi.yml b/test/integration/targets/uri/tasks/use_gssapi.yml new file mode 100644 index 00000000..6629cee7 --- /dev/null +++ b/test/integration/targets/uri/tasks/use_gssapi.yml @@ -0,0 +1,62 @@ +- name: test that endpoint offers Negotiate auth + uri: + url: http://{{ httpbin_host }}/gssapi + status_code: 401 + register: no_auth_failure + failed_when: no_auth_failure.www_authenticate != 'Negotiate' + +- name: test Negotiate auth over HTTP with explicit credentials + uri: + url: http://{{ httpbin_host }}/gssapi + use_gssapi: yes + url_username: '{{ krb5_username }}' + url_password: '{{ krb5_password }}' + return_content: yes + register: http_explicit + +- name: test Negotiate auth over HTTPS with explicit credentials + uri: + url: https://{{ httpbin_host }}/gssapi + use_gssapi: yes + url_username: '{{ krb5_username }}' + url_password: '{{ krb5_password }}' + return_content: yes + register: https_explicit + +- name: assert test Negotiate auth with implicit credentials + assert: + that: + - http_explicit.status == 200 + - http_explicit.content | trim == 'Microsoft Rulz' + - https_explicit.status == 200 + - https_explicit.content | trim == 'Microsoft Rulz' + +- name: skip tests on macOS, I cannot seem to get it to read a credential from a custom ccache + when: ansible_facts.distribution != 'MacOSX' + block: + - name: get Kerberos ticket for implicit auth tests + httptester_kinit: + username: '{{ krb5_username }}' + password: '{{ krb5_password }}' + + - name: test Negotiate auth over HTTP with implicit credentials + uri: + url: http://{{ httpbin_host }}/gssapi + use_gssapi: yes + return_content: yes + register: http_implicit + + - name: test Negotiate auth over HTTPS with implicit credentials + uri: + url: https://{{ httpbin_host }}/gssapi + use_gssapi: yes + return_content: yes + register: https_implicit + + - name: assert test Negotiate auth with implicit credentials + assert: + that: + - http_implicit.status == 200 + - http_implicit.content | trim == 'Microsoft Rulz' + - https_implicit.status == 200 + - https_implicit.content | trim == 'Microsoft Rulz' diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml index 3b8ff377..d3bae056 100644 --- a/test/integration/targets/user/tasks/main.yml +++ b/test/integration/targets/user/tasks/main.yml @@ -17,6 +17,10 @@ # along with Ansible. If not, see <http://www.gnu.org/licenses/>. # +- name: skip broken distros + meta: end_host + when: ansible_distribution == 'Alpine' + - import_tasks: test_create_user.yml - import_tasks: test_create_system_user.yml - import_tasks: test_create_user_uid.yml @@ -27,6 +31,7 @@ - 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_min_max.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_expires_min_max.yml b/test/integration/targets/user/tasks/test_expires_min_max.yml new file mode 100644 index 00000000..80e607b6 --- /dev/null +++ b/test/integration/targets/user/tasks/test_expires_min_max.yml @@ -0,0 +1,55 @@ +# https://github.com/ansible/ansible/issues/68775 +- name: Test setting maximum expiration + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + block: + - name: create user + user: + name: ansibulluser + state: present + + - name: add maximum expire date for password + user: + name: ansibulluser + password_expire_max: 10 + register: pass_max_1_0 + + - name: again add maximum expire date for password + user: + name: ansibulluser + password_expire_max: 10 + register: pass_max_1_1 + + - name: validate result for maximum expire date + assert: + that: + - pass_max_1_0 is changed + - pass_max_1_1 is not changed + + - name: add minimum expire date for password + user: + name: ansibulluser + password_expire_min: 5 + register: pass_min_2_0 + + - name: again add minimum expire date for password + user: + name: ansibulluser + password_expire_min: 5 + register: pass_min_2_1 + + - name: validate result for minimum expire date + assert: + that: + - pass_min_2_0 is changed + - pass_min_2_1 is not changed + + - name: Get shadow data for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: Ensure password expiration was set properly + assert: + that: + - ansible_facts.getent_shadow['ansibulluser'][2] == '5' + - ansible_facts.getent_shadow['ansibulluser'][3] == '10' 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 f19cd1c5..fc31688b 100755 --- a/test/integration/targets/var_precedence/ansible-var-precedence-check.py +++ b/test/integration/targets/var_precedence/ansible-var-precedence-check.py @@ -3,6 +3,9 @@ # A tool to check the order of precedence for ansible variables # https://github.com/ansible/ansible/blob/devel/test/integration/test_var_precedence.yml +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import json import os import sys @@ -455,7 +458,7 @@ def main(): if f not in features: print('%s is not a valid feature' % f) sys.exit(1) - features = [x for x in options.feature] + features = list(options.feature) fdesc = { 'ini_host': 'host var inside the ini', diff --git a/test/integration/targets/var_reserved/aliases b/test/integration/targets/var_reserved/aliases new file mode 100644 index 00000000..765b70da --- /dev/null +++ b/test/integration/targets/var_reserved/aliases @@ -0,0 +1 @@ +shippable/posix/group2 diff --git a/test/integration/targets/var_reserved/reserved_varname_warning.yml b/test/integration/targets/var_reserved/reserved_varname_warning.yml new file mode 100644 index 00000000..1bdb34a0 --- /dev/null +++ b/test/integration/targets/var_reserved/reserved_varname_warning.yml @@ -0,0 +1,6 @@ +- hosts: localhost + gather_facts: false + vars: + lipsum: jinja2 uses me internally + tasks: + - debug: diff --git a/test/integration/targets/var_reserved/runme.sh b/test/integration/targets/var_reserved/runme.sh new file mode 100755 index 00000000..3c3befbd --- /dev/null +++ b/test/integration/targets/var_reserved/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook reserved_varname_warning.yml "$@" 2>&1| grep 'Found variable using reserved name: lipsum' diff --git a/test/integration/targets/var_templating/runme.sh b/test/integration/targets/var_templating/runme.sh index 0d3ac6bb..9363cb3a 100755 --- a/test/integration/targets/var_templating/runme.sh +++ b/test/integration/targets/var_templating/runme.sh @@ -14,4 +14,5 @@ ansible-playbook undall.yml -i inventory -v "$@" # test hostvars templating ansible-playbook task_vars_templating.yml -v "$@" -ansible-playbook test_connection_vars.yml -v "$@" 2>&1 | grep 'sudo' +# there should be an attempt to use 'sudo' in the connection debug output +ANSIBLE_BECOME_ALLOW_SAME_USER=true ansible-playbook test_connection_vars.yml -vvvv "$@" | tee /dev/stderr | grep 'sudo \-H \-S' diff --git a/test/integration/targets/vault/runme.sh b/test/integration/targets/vault/runme.sh index 197095bc..e3b21d7f 100755 --- a/test/integration/targets/vault/runme.sh +++ b/test/integration/targets/vault/runme.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -euvx - -export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh @@ -523,4 +521,4 @@ ansible-playbook -i ../../inventory -v "$@" --vault-password-file vault-password # Ensure we don't leave unencrypted temp files dangling ansible-playbook -v "$@" --vault-password-file vault-password test_dangling_temp.yml -ansible-playbook "$@" --vault-password-file vault-password single_vault_as_string.yml +ansible-playbook "$@" --vault-password-file vault-password single_vault_as_string.yml
\ No newline at end of file diff --git a/test/integration/targets/vault/test-vault-client.py b/test/integration/targets/vault/test-vault-client.py index a2f17dc5..ee461887 100755 --- a/test/integration/targets/vault/test-vault-client.py +++ b/test/integration/targets/vault/test-vault-client.py @@ -1,6 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + ANSIBLE_METADATA = {'status': ['preview'], 'supported_by': 'community', 'version': '1.0'} diff --git a/test/integration/targets/wait_for/files/testserver.py b/test/integration/targets/wait_for/files/testserver.py index 1f6f1187..2b728b6c 100644 --- a/test/integration/targets/wait_for/files/testserver.py +++ b/test/integration/targets/wait_for/files/testserver.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import sys if __name__ == '__main__': diff --git a/test/integration/targets/want_json_modules_posix/library/helloworld.py b/test/integration/targets/want_json_modules_posix/library/helloworld.py index ad0301cb..80f87617 100644 --- a/test/integration/targets/want_json_modules_posix/library/helloworld.py +++ b/test/integration/targets/want_json_modules_posix/library/helloworld.py @@ -16,6 +16,9 @@ # WANT_JSON +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + import json import sys diff --git a/test/integration/targets/yaml_parsing/aliases b/test/integration/targets/yaml_parsing/aliases new file mode 100644 index 00000000..b5983214 --- /dev/null +++ b/test/integration/targets/yaml_parsing/aliases @@ -0,0 +1 @@ +shippable/posix/group3 diff --git a/test/integration/targets/yaml_parsing/playbook.yml b/test/integration/targets/yaml_parsing/playbook.yml new file mode 100644 index 00000000..b3c9d5b3 --- /dev/null +++ b/test/integration/targets/yaml_parsing/playbook.yml @@ -0,0 +1,5 @@ +- hosts: localhost + gather_facts: false + vars: + foo: bar + foo: baz # yamllint disable rule:key-duplicates diff --git a/test/integration/targets/yaml_parsing/tasks/main.yml b/test/integration/targets/yaml_parsing/tasks/main.yml new file mode 100644 index 00000000..7d9c4aa6 --- /dev/null +++ b/test/integration/targets/yaml_parsing/tasks/main.yml @@ -0,0 +1,37 @@ +- name: Test ANSIBLE_DUPLICATE_YAML_DICT_KEY=warn + command: ansible-playbook {{ verbosity }} {{ role_path }}/playbook.yml + environment: + ANSIBLE_DUPLICATE_YAML_DICT_KEY: warn + register: duplicate_warn + +- assert: + that: + - '"found a duplicate dict key (foo)" in duplicate_warn.stderr' + - duplicate_warn.rc == 0 + +- name: Test ANSIBLE_DUPLICATE_YAML_DICT_KEY=error + command: ansible-playbook {{ verbosity }} {{ role_path }}/playbook.yml + failed_when: duplicate_error.rc != 4 + environment: + ANSIBLE_DUPLICATE_YAML_DICT_KEY: error + register: duplicate_error + +- assert: + that: + - '"found a duplicate dict key (foo)" in duplicate_error.stderr' + - duplicate_error.rc == 4 + +- name: Test ANSIBLE_DUPLICATE_YAML_DICT_KEY=ignore + command: ansible-playbook {{ verbosity }} {{ role_path }}/playbook.yml + environment: + ANSIBLE_DUPLICATE_YAML_DICT_KEY: ignore + register: duplicate_ignore + +- assert: + that: + - '"found a duplicate dict key (foo)" not in duplicate_ignore.stderr' + - duplicate_ignore.rc == 0 + + +- name: test unsafe YAMLism + import_tasks: unsafe.yml diff --git a/test/integration/targets/yaml_parsing/tasks/unsafe.yml b/test/integration/targets/yaml_parsing/tasks/unsafe.yml new file mode 100644 index 00000000..8d9d627b --- /dev/null +++ b/test/integration/targets/yaml_parsing/tasks/unsafe.yml @@ -0,0 +1,36 @@ +- name: ensure no templating unsafe + block: + - name: check unsafe string + assert: + that: + - regstr != resolved + - "'Fail' not in regstr" + - "'{' in regstr" + - "'}' in regstr" + vars: + regstr: !unsafe b{{nottemplate}} + + - name: check unsafe string in list + assert: + that: + - ulist[0] != resolved + - "'Fail' not in ulist[0]" + - "'{' in ulist[0]" + - "'}' in ulist[0]" + vars: + ulist: !unsafe [ 'b{{nottemplate}}', 'c', 'd'] + + - name: check unsafe string in dict + assert: + that: + - udict['a'] != resolved + - "'Fail' not in udict['a']" + - "'{' in udict['a']" + - "'}' in udict['a']" + vars: + udict: !unsafe + a: b{{nottemplate}} + c: d + vars: + nottemplate: FAIL + resolved: 'b{{nottemplate}}' diff --git a/test/integration/targets/yaml_parsing/vars/main.yml b/test/integration/targets/yaml_parsing/vars/main.yml new file mode 100644 index 00000000..ea65e0b5 --- /dev/null +++ b/test/integration/targets/yaml_parsing/vars/main.yml @@ -0,0 +1 @@ +verbosity: "{{ '' if not ansible_verbosity else '-' ~ ('v' * ansible_verbosity) }}" diff --git a/test/integration/targets/yum/tasks/proxy.yml b/test/integration/targets/yum/tasks/proxy.yml index f42eb179..00fcf488 100644 --- a/test/integration/targets/yum/tasks/proxy.yml +++ b/test/integration/targets/yum/tasks/proxy.yml @@ -2,7 +2,7 @@ block: - name: install tinyproxy yum: - name: 'https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/tinyproxy-1.10.0-3.el7.x86_64.rpm' + name: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yum/tinyproxy-1.10.0-3.el7.x86_64.rpm' state: installed # systemd doesn't play nice with this in a container for some reason @@ -25,7 +25,7 @@ - name: install ninvaders with unauthenticated proxy yum: - name: 'https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' + name: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' state: installed register: yum_proxy_result @@ -84,7 +84,7 @@ - name: install ninvaders with authenticated proxy yum: - name: 'https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' + name: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' state: installed register: yum_proxy_result @@ -134,7 +134,7 @@ - name: install ninvaders with proxy, proxy_username, and proxy_password config in yum.conf yum: - name: 'https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' + name: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yum/ninvaders-0.1.1-18.el7.x86_64.rpm' state: installed register: yum_proxy_result diff --git a/test/integration/targets/yum/tasks/yum.yml b/test/integration/targets/yum/tasks/yum.yml index 9ed00af8..7abfea17 100644 --- a/test/integration/targets/yum/tasks/yum.yml +++ b/test/integration/targets/yum/tasks/yum.yml @@ -532,7 +532,7 @@ - name: try to install from non existing url yum: - name: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/non-existing-1.0.0.fc26.noarch.rpm + name: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yum/non-existing-1.0.0.fc26.noarch.rpm state: present register: yum_result ignore_errors: yes @@ -580,7 +580,7 @@ - name: try to install uncompatible arch rpm on non-ppc64le, should fail yum: - name: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/banner-1.3.4-3.el7.ppc64le.rpm + name: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yum/banner-1.3.4-3.el7.ppc64le.rpm state: present register: yum_result ignore_errors: True @@ -597,7 +597,7 @@ - name: try to install uncompatible arch rpm on ppc64le, should fail yum: - name: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/tinyproxy-1.10.0-3.el7.x86_64.rpm + name: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yum/tinyproxy-1.10.0-3.el7.x86_64.rpm state: present register: yum_result ignore_errors: True @@ -615,30 +615,19 @@ # setup for testing installing an RPM from url - set_fact: - pkg_name: fpaste + pkg_name: noarchfake + pkg_path: '{{ repodir }}/noarchfake-1.0-1.noarch.rpm' - name: cleanup yum: name: "{{ pkg_name }}" state: absent -- set_fact: - pkg_url: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/fpaste-0.3.7.4.1-2.el7.noarch.rpm - when: ansible_python.version.major == 2 - -- set_fact: - pkg_url: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/yum/fpaste-0.3.9.2-1.fc28.noarch.rpm - when: ansible_python.version.major == 3 # setup end -- name: download an rpm - get_url: - url: "{{ pkg_url }}" - dest: "/tmp/{{ pkg_name }}.rpm" - -- name: install the downloaded rpm +- name: install a local noarch rpm from file yum: - name: "/tmp/{{ pkg_name }}.rpm" + name: "{{ pkg_path }}" state: present disable_gpg_check: true register: yum_result @@ -659,7 +648,7 @@ - name: install the downloaded rpm again yum: - name: "/tmp/{{ pkg_name }}.rpm" + name: "{{ pkg_path }}" state: present register: yum_result @@ -684,7 +673,7 @@ - name: install from url yum: - name: "{{ pkg_url }}" + name: "file://{{ pkg_path }}" state: present disable_gpg_check: true register: yum_result @@ -871,3 +860,10 @@ - remove is changed - "'toaster-1.2.3.4' not in rpmqa.stdout" - "'test-package-that-provides-toaster' in rpmqa.stdout" + always: + - name: Remove test packages + yum: + name: + - test-package-that-provides-toaster + - toaster + state: absent diff --git a/test/integration/targets/yum_repository/tasks/main.yml b/test/integration/targets/yum_repository/tasks/main.yml index 0ff0bcff..d8195775 100644 --- a/test/integration/targets/yum_repository/tasks/main.yml +++ b/test/integration/targets/yum_repository/tasks/main.yml @@ -103,6 +103,7 @@ file: "{{ yum_repository_test_repo.name ~ 2 }}" ip_resolve: 4 keepalive: no + module_hotfixes: no register: test_repo_add1 - name: check that options are correctly getting written to the repo file @@ -113,6 +114,7 @@ - "'enablegroups = 0' in repo_file_contents" - "'ip_resolve = 4' in repo_file_contents" - "'keepalive = 0' in repo_file_contents" + - "'module_hotfixes = 0' in repo_file_contents" vars: repo_file: "{{ '/etc/yum.repos.d/' ~ yum_repository_test_repo.name ~ '2.repo' }}" repo_file_contents: "{{ lookup('file', repo_file) }}" @@ -127,6 +129,7 @@ file: "{{ yum_repository_test_repo.name ~ 2 }}" ip_resolve: 4 keepalive: no + module_hotfixes: no register: test_repo_add2 - name: check Idempotant |