diff options
author | Lee Garrett <lgarrett@rocketjump.eu> | 2021-01-19 05:35:08 +0100 |
---|---|---|
committer | Lee Garrett <lgarrett@rocketjump.eu> | 2021-01-19 05:35:08 +0100 |
commit | 0addf1f445fd4e07fc0be2f62931208314ac5603 (patch) | |
tree | d8f6655f57698db86eedb5f7be5e6390bb9ae079 /test | |
parent | 3647b7e79c9fb3566efe439bb35176bddf4822d0 (diff) | |
download | debian-ansible-core-0addf1f445fd4e07fc0be2f62931208314ac5603.zip |
New upstream version 2.10.5+dfsg
Diffstat (limited to 'test')
72 files changed, 1726 insertions, 1271 deletions
diff --git a/test/integration/targets/ansible-galaxy/runme.sh b/test/integration/targets/ansible-galaxy/runme.sh index 2cd59825..3a7c4a39 100755 --- a/test/integration/targets/ansible-galaxy/runme.sh +++ b/test/integration/targets/ansible-galaxy/runme.sh @@ -415,6 +415,17 @@ f_ansible_galaxy_status \ unset ANSIBLE_COLLECTIONS_PATH +f_ansible_galaxy_status \ + "collection list with collections installed from python package" + + mkdir -p test-site-packages + ln -s "${galaxy_testdir}/local/ansible_collections" test-site-packages/ansible_collections + ansible-galaxy collection list + PYTHONPATH="./test-site-packages/:$PYTHONPATH" ansible-galaxy collection list | tee out.txt + + grep ".ansible/collections/ansible_collections" out.txt + grep "test-site-packages/ansible_collections" out.txt + ## end ansible-galaxy collection list 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 new file mode 100644 index 00000000..359fbf07 --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/filter/check_pylint.py @@ -0,0 +1,21 @@ +""" +These test cases verify ansible-test version constraints for pylint and its dependencies across Python versions. +The initial test cases were discovered while testing various Python versions against ansible/ansible. +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +# Python 3.8 fails with astroid 2.2.5 but works on 2.3.3 +# syntax-error: Cannot import 'string' due to syntax error 'invalid syntax (<unknown>, line 109)' +# Python 3.9 fails with astroid 2.2.5 but works on 2.3.3 +# syntax-error: Cannot import 'string' due to syntax error 'invalid syntax (<unknown>, line 104)' +import string + +# Python 3.9 fails with pylint 2.3.1 or 2.4.4 with astroid 2.3.3 but works with pylint 2.5.0 and astroid 2.4.0 +# 'Call' object has no attribute 'value' +result = {None: None}[{}.get('something')] + +# pylint 2.3.1 and 2.4.4 report the following error but 2.5.0 and 2.6.0 do not +# blacklisted-name: Black listed name "foo" +# see: https://github.com/PyCQA/pylint/issues/3701 +foo = {}.keys() diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/modules/bad.py b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/modules/bad.py new file mode 100644 index 00000000..e79613bb --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/modules/bad.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +module: bad +short_description: Bad test module +description: Bad test module. +author: + - Ansible Core Team +''' + +EXAMPLES = ''' +- bad: +''' + +RETURN = '''''' + +from ansible.module_utils.basic import AnsibleModule +from ansible import constants # intentionally trigger pylint ansible-bad-module-import error + + +def main(): + module = AnsibleModule( + argument_spec=dict(), + ) + + module.exit_json() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/ansible-test/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py b/test/integration/targets/ansible-test/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py new file mode 100644 index 00000000..82215438 --- /dev/null +++ b/test/integration/targets/ansible-test/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py @@ -0,0 +1,16 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import tempfile + +try: + import urllib2 # intentionally trigger pylint ansible-bad-import error +except ImportError: + urllib2 = None + +try: + from urllib2 import Request # intentionally trigger pylint ansible-bad-import-from error +except ImportError: + Request = None + +tempfile.mktemp() # intentionally trigger pylint ansible-bad-function error 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 e69de29b..079d0161 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 @@ -0,0 +1,6 @@ +plugins/filter/check_pylint.py pylint:blacklisted-name +plugins/modules/bad.py import +plugins/modules/bad.py pylint:ansible-bad-module-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/venv.sh b/test/integration/targets/ansible-test/collection-tests/venv.sh index 6ff496b6..862c8ad9 100755 --- a/test/integration/targets/ansible-test/collection-tests/venv.sh +++ b/test/integration/targets/ansible-test/collection-tests/venv.sh @@ -5,6 +5,10 @@ set -eux -o pipefail cp -a "${TEST_DIR}/ansible_collections" "${WORK_DIR}" 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" + # common args for all tests # each test will be run in a separate venv to verify that requirements have been properly specified common=(--venv --python "${ANSIBLE_TEST_PYTHON_VERSION}" --color --truncate 0 "${@}") diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/common_handlers/handlers/main.yml b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/common_handlers/handlers/main.yml index d9f73231..186368f5 100644 --- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/common_handlers/handlers/main.yml +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/common_handlers/handlers/main.yml @@ -4,3 +4,24 @@ set_fact: handler_counter: '{{ handler_counter|int + 1 }}' failed_when: handler_counter|int > 1 + +# The following handler contains the role name and should be callable as: +# 'common_handlers test_fqcn_handler' +# 'common_handlers : common_handlers test_fqcn_handler` +# 'testns.testcoll.common_handlers : common_handlers test_fqcn_handler' +- name: common_handlers test_fqcn_handler + set_fact: + handler_counter: '{{ handler_counter|int + 1}}' + failed_when: handler_counter|int > 2 + +# The following handler starts with 'role name : ' and should _not_ be listed as: +# 'common_handlers : common_handlers : test_fqcn_handler` +# 'testns.testcoll.common_handlers : common_handlers : test_fqcn_handler' +- name: 'common_handlers : test_fqcn_handler' + meta: noop + +# The following handler starts with 'fqcn : ' and should _not_ be listed as: +# 'common_handlers : testns.testcoll.common_handlers : test_fqcn_handler` +# 'testns.testcoll.common_handlers : testns.testcoll.common_handlers : test_fqcn_handler' +- name: 'testns.testcoll.common_handlers : test_fqcn_handler' + meta: noop diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/test_fqcn_handlers/tasks/main.yml b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/test_fqcn_handlers/tasks/main.yml index db8767d2..6eadb7c2 100644 --- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/test_fqcn_handlers/tasks/main.yml +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/test_fqcn_handlers/tasks/main.yml @@ -1,7 +1,16 @@ -- debug: +- name: Fire fqcn handler 1 + debug: msg: Fire fqcn handler changed_when: true notify: - 'testns.testcoll.common_handlers : test_fqcn_handler' - 'common_handlers : test_fqcn_handler' - 'test_fqcn_handler' + +- debug: + msg: Fire fqcn handler with role name + changed_when: true + notify: + - 'testns.testcoll.common_handlers : common_handlers test_fqcn_handler' + - 'common_handlers : common_handlers test_fqcn_handler' + - 'common_handlers test_fqcn_handler' diff --git a/test/integration/targets/collections_runtime_pythonpath/runme.sh b/test/integration/targets/collections_runtime_pythonpath/runme.sh index 654104a1..41236e8b 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/connection/test.sh b/test/integration/targets/connection/test.sh index 18fb2b77..ad672e23 100755 --- a/test/integration/targets/connection/test.sh +++ b/test/integration/targets/connection/test.sh @@ -21,3 +21,5 @@ then else echo "SUCCESS: Connection vars not found" fi + +ansible-playbook test_reset_connection.yml -i "${INVENTORY}" "$@" diff --git a/test/integration/targets/connection/test_reset_connection.yml b/test/integration/targets/connection/test_reset_connection.yml new file mode 100644 index 00000000..2f6cb8dc --- /dev/null +++ b/test/integration/targets/connection/test_reset_connection.yml @@ -0,0 +1,5 @@ +- hosts: "{{ target_hosts }}" + gather_facts: no + tasks: + # https://github.com/ansible/ansible/issues/65812 + - meta: reset_connection diff --git a/test/integration/targets/copy/tasks/selinux.yml b/test/integration/targets/copy/tasks/selinux.yml new file mode 100644 index 00000000..6bd3b04f --- /dev/null +++ b/test/integration/targets/copy/tasks/selinux.yml @@ -0,0 +1,35 @@ +# Ensure that our logic for special filesystems works as intended +# https://github.com/ansible/ansible/issues/70244 +- block: + - name: Install dosfstools + yum: + name: dosfstools + state: present + + - name: Create a file to use for a fat16 filesystem + command: dd if=/dev/zero of=/fat16 bs=1024 count=10240 + + - name: mkfs.fat + command: mkfs.fat -F16 /fat16 + + - name: Mount it + command: mount /fat16 /mnt + + - name: Copy a file to it + copy: + src: /etc/fstab + dest: /mnt/fstab + always: + - name: Unmount it + command: umount /mnt + ignore_errors: true + + - name: Nuke /fat16 + file: + path: /fat16 + state: absent + + - name: Uninstall dosfstools + yum: + name: dosfstools + state: absent diff --git a/test/integration/targets/delegate_to/runme.sh b/test/integration/targets/delegate_to/runme.sh index 697fc393..44059552 100755 --- a/test/integration/targets/delegate_to/runme.sh +++ b/test/integration/targets/delegate_to/runme.sh @@ -63,6 +63,7 @@ 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 diff --git a/test/integration/targets/expect/aliases b/test/integration/targets/expect/aliases index ca7c9128..7211b8d0 100644 --- a/test/integration/targets/expect/aliases +++ b/test/integration/targets/expect/aliases @@ -1,2 +1,3 @@ shippable/posix/group2 destructive +needs/target/setup_pexpect diff --git a/test/integration/targets/expect/tasks/main.yml b/test/integration/targets/expect/tasks/main.yml index 7feaec4d..0c408d28 100644 --- a/test/integration/targets/expect/tasks/main.yml +++ b/test/integration/targets/expect/tasks/main.yml @@ -16,9 +16,8 @@ # 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 test requirements - pip: - name: pexpect - state: present + import_role: + name: setup_pexpect - name: record the test_command file set_fact: test_command_file={{output_dir | expanduser}}/test_command.py diff --git a/test/integration/targets/filter_urls/runme.sh b/test/integration/targets/filter_urls/runme.sh index f6460acb..9362a385 100755 --- a/test/integration/targets/filter_urls/runme.sh +++ b/test/integration/targets/filter_urls/runme.sh @@ -6,6 +6,7 @@ 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/groupby_filter/runme.sh b/test/integration/targets/groupby_filter/runme.sh index e5099aa1..07894b0f 100755 --- a/test/integration/targets/groupby_filter/runme.sh +++ b/test/integration/targets/groupby_filter/runme.sh @@ -2,6 +2,7 @@ set -eux +export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh pip install -U jinja2==2.9.4 diff --git a/test/integration/targets/hash/runme.sh b/test/integration/targets/hash/runme.sh index 9448e4e0..3689d83b 100755 --- a/test/integration/targets/hash/runme.sh +++ b/test/integration/targets/hash/runme.sh @@ -6,3 +6,6 @@ JSON_ARG='{"test_hash":{"extra_args":"this is an extra arg"}}' ANSIBLE_HASH_BEHAVIOUR=replace ansible-playbook test_hash.yml -i ../../inventory -v "$@" -e "${JSON_ARG}" ANSIBLE_HASH_BEHAVIOUR=merge ansible-playbook test_hash.yml -i ../../inventory -v "$@" -e "${JSON_ARG}" + +ANSIBLE_HASH_BEHAVIOUR=replace ansible-playbook test_inventory_hash.yml -i test_inv1.yml -i test_inv2.yml -v "$@" +ANSIBLE_HASH_BEHAVIOUR=merge ansible-playbook test_inventory_hash.yml -i test_inv1.yml -i test_inv2.yml -v "$@" diff --git a/test/integration/targets/hash/test_inv1.yml b/test/integration/targets/hash/test_inv1.yml new file mode 100644 index 00000000..02bd017f --- /dev/null +++ b/test/integration/targets/hash/test_inv1.yml @@ -0,0 +1,10 @@ +all: + hosts: + host1: + test_inventory_host_hash: + host_var1: "inventory 1" + host_var2: "inventory 1" + vars: + test_inventory_group_hash: + group_var1: "inventory 1" + group_var2: "inventory 1" diff --git a/test/integration/targets/hash/test_inv2.yml b/test/integration/targets/hash/test_inv2.yml new file mode 100644 index 00000000..6529b933 --- /dev/null +++ b/test/integration/targets/hash/test_inv2.yml @@ -0,0 +1,8 @@ +all: + hosts: + host1: + test_inventory_host_hash: + host_var1: "inventory 2" + vars: + test_inventory_group_hash: + group_var1: "inventory 2" diff --git a/test/integration/targets/hash/test_inventory_hash.yml b/test/integration/targets/hash/test_inventory_hash.yml new file mode 100644 index 00000000..1091b135 --- /dev/null +++ b/test/integration/targets/hash/test_inventory_hash.yml @@ -0,0 +1,41 @@ +--- +- hosts: localhost + gather_facts: no + vars: + host_hash_merged: {'host_var1': 'inventory 2', 'host_var2': 'inventory 1'} + host_hash_replaced: {'host_var1': 'inventory 2'} + group_hash_merged: {'group_var1': 'inventory 2', 'group_var2': 'inventory 1'} + group_hash_replaced: {'group_var1': 'inventory 2'} + tasks: + + - name: debug hash behaviour result + debug: + var: "{{ lookup('env', 'ANSIBLE_HASH_BEHAVIOUR') }}" + verbosity: 2 + + - name: assert hash behaviour is merge or replace + assert: + that: + - lookup('env', 'ANSIBLE_HASH_BEHAVIOUR') in ('merge', 'replace') + + - name: debug test_inventory_host_hash + debug: + var: hostvars['host1']['test_inventory_host_hash'] + verbosity: 2 + + - name: debug test_inventory_group_hash + debug: + var: test_inventory_group_hash + verbosity: 2 + + - assert: + that: + - hostvars['host1']['test_inventory_host_hash'] == host_hash_replaced + - test_inventory_group_hash == group_hash_replaced + when: "lookup('env', 'ANSIBLE_HASH_BEHAVIOUR') == 'replace'" + + - assert: + that: + - hostvars['host1']['test_inventory_host_hash'] == host_hash_merged + - test_inventory_group_hash == group_hash_merged + when: "lookup('env', 'ANSIBLE_HASH_BEHAVIOUR') == 'merge'" diff --git a/test/integration/targets/incidental_inventory_docker_swarm/runme.sh b/test/integration/targets/incidental_inventory_docker_swarm/runme.sh index e2ba6869..b93d386a 100755 --- a/test/integration/targets/incidental_inventory_docker_swarm/runme.sh +++ b/test/integration/targets/incidental_inventory_docker_swarm/runme.sh @@ -8,7 +8,6 @@ cleanup() { echo "Cleanup" ansible-playbook playbooks/swarm_cleanup.yml echo "Done" - exit 0 } trap cleanup INT TERM EXIT diff --git a/test/integration/targets/lookup_password/runme.sh b/test/integration/targets/lookup_password/runme.sh index a3637a7e..ac2c1704 100755 --- a/test/integration/targets/lookup_password/runme.sh +++ b/test/integration/targets/lookup_password/runme.sh @@ -2,6 +2,7 @@ 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/noexec/aliases b/test/integration/targets/noexec/aliases new file mode 100644 index 00000000..66a77c7b --- /dev/null +++ b/test/integration/targets/noexec/aliases @@ -0,0 +1,3 @@ +shippable/posix/group2 +skip/docker +skip/macos diff --git a/test/integration/targets/noexec/inventory b/test/integration/targets/noexec/inventory new file mode 100644 index 00000000..ab9b62c8 --- /dev/null +++ b/test/integration/targets/noexec/inventory @@ -0,0 +1 @@ +not_empty # avoid empty empty hosts list warning without defining explicit localhost diff --git a/test/integration/targets/noexec/runme.sh b/test/integration/targets/noexec/runme.sh new file mode 100755 index 00000000..ff706558 --- /dev/null +++ b/test/integration/targets/noexec/runme.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -eux + +trap 'umount "${OUTPUT_DIR}/ramdisk"' EXIT + +mkdir "${OUTPUT_DIR}/ramdisk" +mount -t tmpfs -o size=32m,noexec,rw tmpfs "${OUTPUT_DIR}/ramdisk" +ANSIBLE_REMOTE_TMP="${OUTPUT_DIR}/ramdisk" ansible-playbook -i inventory "$@" test-noexec.yml diff --git a/test/integration/targets/noexec/test-noexec.yml b/test/integration/targets/noexec/test-noexec.yml new file mode 100644 index 00000000..3c7d756b --- /dev/null +++ b/test/integration/targets/noexec/test-noexec.yml @@ -0,0 +1,8 @@ +- hosts: localhost + gather_facts: false + tasks: + - ping: + + - command: sleep 1 + async: 2 + poll: 1 diff --git a/test/integration/targets/old_style_cache_plugins/runme.sh b/test/integration/targets/old_style_cache_plugins/runme.sh index 13911bd5..86d2433b 100755 --- a/test/integration/targets/old_style_cache_plugins/runme.sh +++ b/test/integration/targets/old_style_cache_plugins/runme.sh @@ -2,6 +2,7 @@ set -eux +export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh # Run test if dependencies are installed diff --git a/test/integration/targets/pip/tasks/main.yml b/test/integration/targets/pip/tasks/main.yml index 05879c18..c0a36c43 100644 --- a/test/integration/targets/pip/tasks/main.yml +++ b/test/integration/targets/pip/tasks/main.yml @@ -1,20 +1,30 @@ # Current pip unconditionally uses md5. # We can re-enable if pip switches to a different hash or allows us to not check md5. -- name: find virtualenv command - command: "which virtualenv virtualenv-{{ ansible_python.version.major }}.{{ ansible_python.version.minor }}" - register: command - ignore_errors: true +- name: Python 2 + when: ansible_python.version.major == 2 + block: + - name: find virtualenv command + command: "which virtualenv virtualenv-{{ ansible_python.version.major }}.{{ ansible_python.version.minor }}" + register: command + ignore_errors: true -- name: is virtualenv available to python -m - command: '{{ ansible_python_interpreter }} -m virtualenv' - register: python_m - when: not command.stdout_lines - failed_when: python_m.rc != 2 + - name: is virtualenv available to python -m + command: '{{ ansible_python_interpreter }} -m virtualenv' + register: python_m + when: not command.stdout_lines + failed_when: python_m.rc != 2 -- name: remember selected virtualenv command - set_fact: - virtualenv: "{{ command.stdout_lines[0] if command is successful else ansible_python_interpreter ~ ' -m virtualenv' }}" + - name: remember selected virtualenv command + set_fact: + virtualenv: "{{ command.stdout_lines[0] if command is successful else ansible_python_interpreter ~ ' -m virtualenv' }}" + +- name: Python 3+ + when: ansible_python.version.major > 2 + block: + - name: remember selected virtualenv command + set_fact: + virtualenv: "{{ ansible_python_interpreter ~ ' -m venv' }}" - block: - name: install git, needed for repo installs diff --git a/test/integration/targets/pip/tasks/pip.yml b/test/integration/targets/pip/tasks/pip.yml index 6281bbe8..572c7b6f 100644 --- a/test/integration/targets/pip/tasks/pip.yml +++ b/test/integration/targets/pip/tasks/pip.yml @@ -521,7 +521,7 @@ ### test virtualenv_command begin ### - name: Test virtualenv command with arguments - when: "ansible_system == 'Linux'" + when: ansible_python.version.major == 2 block: - name: make sure the virtualenv does not exist file: @@ -533,7 +533,7 @@ pip: name: "{{ pip_test_package }}" virtualenv: "{{ output_dir }}/pipenv" - virtualenv_command: "virtualenv --verbose" + virtualenv_command: "{{ command.stdout_lines[0] | basename }} --verbose" state: present register: version13 diff --git a/test/integration/targets/setup_paramiko/install-MacOSX-10-python-3.yml b/test/integration/targets/setup_paramiko/install-Darwin-python-3.yml index a156f806..a156f806 100644 --- a/test/integration/targets/setup_paramiko/install-MacOSX-10-python-3.yml +++ b/test/integration/targets/setup_paramiko/install-Darwin-python-3.yml diff --git a/test/integration/targets/setup_paramiko/install.yml b/test/integration/targets/setup_paramiko/install.yml index 194bd51f..e98abe33 100644 --- a/test/integration/targets/setup_paramiko/install.yml +++ b/test/integration/targets/setup_paramiko/install.yml @@ -13,5 +13,6 @@ with_first_found: - "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" - "install-python-{{ ansible_python.version.major }}.yml" - "install-fail.yml" diff --git a/test/integration/targets/setup_paramiko/setup.sh b/test/integration/targets/setup_paramiko/setup.sh index 64b935cd..8c4f6f1c 100644 --- a/test/integration/targets/setup_paramiko/setup.sh +++ b/test/integration/targets/setup_paramiko/setup.sh @@ -3,6 +3,7 @@ set -eux +export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh # for pip installs, if needed, otherwise unused 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-MacOSX-10-python-3.yml b/test/integration/targets/setup_paramiko/uninstall-Darwin-python-3.yml index 69a68e42..69a68e42 100644 --- a/test/integration/targets/setup_paramiko/uninstall-MacOSX-10-python-3.yml +++ b/test/integration/targets/setup_paramiko/uninstall-Darwin-python-3.yml diff --git a/test/integration/targets/setup_paramiko/uninstall.yml b/test/integration/targets/setup_paramiko/uninstall.yml index 46a16d91..48ff68e6 100644 --- a/test/integration/targets/setup_paramiko/uninstall.yml +++ b/test/integration/targets/setup_paramiko/uninstall.yml @@ -10,6 +10,7 @@ with_first_found: - "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" - "uninstall-{{ ansible_pkg_mgr }}-python-{{ ansible_python.version.major }}.yml" - "uninstall-{{ ansible_pkg_mgr }}.yml" - "uninstall-fail.yml" diff --git a/test/integration/targets/setup_pexpect/files/constraints.txt b/test/integration/targets/setup_pexpect/files/constraints.txt new file mode 100644 index 00000000..c78ecdad --- /dev/null +++ b/test/integration/targets/setup_pexpect/files/constraints.txt @@ -0,0 +1,2 @@ +pexpect == 4.8.0 +ptyprocess < 0.7.0 ; python_version < '2.7' # ptyprocess >= 0.7.0 not compatible with Python 2.6 diff --git a/test/integration/targets/setup_pexpect/meta/main.yml b/test/integration/targets/setup_pexpect/meta/main.yml new file mode 100644 index 00000000..1810d4be --- /dev/null +++ b/test/integration/targets/setup_pexpect/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/test/integration/targets/setup_pexpect/tasks/main.yml b/test/integration/targets/setup_pexpect/tasks/main.yml index ef57fe6f..690fe441 100644 --- a/test/integration/targets/setup_pexpect/tasks/main.yml +++ b/test/integration/targets/setup_pexpect/tasks/main.yml @@ -1,4 +1,10 @@ +- name: Copy constraints file + copy: + src: constraints.txt + dest: "{{ remote_tmp_dir }}/pexpect-constraints.txt" + - name: Install pexpect pip: name: pexpect + extra_args: '--constraint "{{ remote_tmp_dir }}/pexpect-constraints.txt"' state: present diff --git a/test/integration/targets/systemd/handlers/main.yml b/test/integration/targets/systemd/handlers/main.yml new file mode 100644 index 00000000..8643a2a0 --- /dev/null +++ b/test/integration/targets/systemd/handlers/main.yml @@ -0,0 +1,4 @@ +- name: remove unit file + file: + path: /etc/systemd/system/sleeper@.service + state: absent diff --git a/test/integration/targets/systemd/tasks/main.yml b/test/integration/targets/systemd/tasks/main.yml index 867a554d..96781eb8 100644 --- a/test/integration/targets/systemd/tasks/main.yml +++ b/test/integration/targets/systemd/tasks/main.yml @@ -20,70 +20,73 @@ ## systemctl ## -- name: check for systemctl command - shell: which systemctl - failed_when: False - register: systemctl_check - -- meta: end_host - when: systemctl_check.rc != 0 - -- set_fact: - ssh_service: '{{ "ssh" if ansible_os_family == "Debian" else "sshd" }}' - -- block: - - name: get a list of running services - shell: systemctl | fgrep 'running' | awk '{print $1}' | sed 's/\.service//g' | fgrep -v '.' | egrep ^[a-z] - register: running_names - - debug: var=running_names - - - name: check running state - systemd: - name: "{{ running_names.stdout_lines|random }}" - state: started - register: systemd_test0 - - debug: var=systemd_test0 - - name: validate results for test0 - assert: - that: - - 'systemd_test0.changed is defined' - - 'systemd_test0.name is defined' - - 'systemd_test0.state is defined' - - 'systemd_test0.status is defined' - - 'not systemd_test0.changed' - - 'systemd_test0.state == "started"' - - - name: the module must fail when a service is not found - systemd: - name: '{{ fake_service }}' - state: stopped - register: result - ignore_errors: yes - - - assert: - that: - - result is failed - - 'result is search("Could not find the requested service {{ fake_service }}")' - - - name: the module must fail in check_mode as well when a service is not found - systemd: - name: '{{ fake_service }}' - state: stopped - register: result - check_mode: yes - ignore_errors: yes - - - assert: - that: - - result is failed - - 'result is search("Could not find the requested service {{ fake_service }}")' - - - name: check that the module works even when systemd is offline (eg in chroot) - systemd: - name: "{{ running_names.stdout_lines|random }}" - state: started - environment: - SYSTEMD_OFFLINE: 1 +- name: End if this system does not use systemd + meta: end_host + when: ansible_facts.service_mgr != 'systemd' + +- 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: + - vars + +- name: get a list of running services + shell: systemctl | fgrep 'running' | awk '{print $1}' | sed 's/\.service//g' | fgrep -v '.' | egrep ^[a-z] + register: running_names +- debug: var=running_names + +- name: check running state + systemd: + name: "{{ running_names.stdout_lines|random }}" + state: started + register: systemd_test0 +- debug: var=systemd_test0 +- name: validate results for test0 + assert: + that: + - 'systemd_test0.changed is defined' + - 'systemd_test0.name is defined' + - 'systemd_test0.state is defined' + - 'systemd_test0.status is defined' + - 'not systemd_test0.changed' + - 'systemd_test0.state == "started"' + +- name: the module must fail when a service is not found + systemd: + name: '{{ fake_service }}' + state: stopped + register: result + ignore_errors: yes + +- assert: + that: + - result is failed + - 'result is search("Could not find the requested service {{ fake_service }}")' + +- name: the module must fail in check_mode as well when a service is not found + systemd: + name: '{{ fake_service }}' + state: stopped + register: result + check_mode: yes + ignore_errors: yes + +- assert: + that: + - result is failed + - 'result is search("Could not find the requested service {{ fake_service }}")' + +- name: check that the module works even when systemd is offline (eg in chroot) + systemd: + name: "{{ running_names.stdout_lines|random }}" + state: started + environment: + SYSTEMD_OFFLINE: 1 - name: Disable ssh 1 systemd: @@ -114,3 +117,5 @@ - systemd_disable_ssh_2 is not changed - systemd_enable_ssh_1 is changed - systemd_enable_ssh_2 is not changed + +- import_tasks: test_unit_template.yml diff --git a/test/integration/targets/systemd/tasks/test_unit_template.yml b/test/integration/targets/systemd/tasks/test_unit_template.yml new file mode 100644 index 00000000..47cb1c78 --- /dev/null +++ b/test/integration/targets/systemd/tasks/test_unit_template.yml @@ -0,0 +1,50 @@ +- name: Copy service file + template: + src: sleeper@.service + dest: /etc/systemd/system/sleeper@.service + owner: root + group: root + mode: '0644' + notify: remove unit file + +- name: Reload systemd + systemd: + daemon_reload: yes + +- name: Start and enable service using unit template + systemd: + name: sleeper@100.service + state: started + enabled: yes + register: template_test_1 + +- name: Start and enable service using unit template again + systemd: + name: sleeper@100.service + state: started + enabled: yes + register: template_test_2 + +- name: Stop and disable service using unit template + systemd: + name: sleeper@100.service + state: stopped + enabled: no + register: template_test_3 + +- name: Stop and disable service using unit template again + systemd: + name: sleeper@100.service + state: stopped + enabled: no + register: template_test_4 + +- name: + assert: + that: + - template_test_1 is changed + - template_test_1 is success + - template_test_2 is not changed + - template_test_2 is success + - template_test_3 is changed + - template_test_4 is not changed diff --git a/test/integration/targets/systemd/templates/sleeper@.service b/test/integration/targets/systemd/templates/sleeper@.service new file mode 100644 index 00000000..8b47982a --- /dev/null +++ b/test/integration/targets/systemd/templates/sleeper@.service @@ -0,0 +1,8 @@ +[Unit] +Description=Basic service to use as a template + +[Service] +ExecStart={{ sleep_bin_path }} %i + +[Install] +WantedBy=multi-user.target diff --git a/test/integration/targets/systemd/vars/Debian.yml b/test/integration/targets/systemd/vars/Debian.yml new file mode 100644 index 00000000..9760744d --- /dev/null +++ b/test/integration/targets/systemd/vars/Debian.yml @@ -0,0 +1,2 @@ +ssh_service: ssh +sleep_bin_path: /bin/sleep diff --git a/test/integration/targets/systemd/vars/default.yml b/test/integration/targets/systemd/vars/default.yml new file mode 100644 index 00000000..57491ff0 --- /dev/null +++ b/test/integration/targets/systemd/vars/default.yml @@ -0,0 +1,2 @@ +ssh_service: sshd +sleep_bin_path: /usr/bin/sleep diff --git a/test/integration/targets/template_jinja2_latest/runme.sh b/test/integration/targets/template_jinja2_latest/runme.sh index 6a20eb5d..d6a09677 100755 --- a/test/integration/targets/template_jinja2_latest/runme.sh +++ b/test/integration/targets/template_jinja2_latest/runme.sh @@ -2,6 +2,7 @@ set -eux +export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh pip install -U -r requirements.txt diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml index 19b12742..3b8ff377 100644 --- a/test/integration/targets/user/tasks/main.yml +++ b/test/integration/targets/user/tasks/main.yml @@ -17,1120 +17,18 @@ # along with Ansible. If not, see <http://www.gnu.org/licenses/>. # -## user add - -- name: remove the test user - user: - name: ansibulluser - state: absent - -- name: try to create a user - user: - name: ansibulluser - state: present - register: user_test0_0 - -- name: create the user again - user: - name: ansibulluser - state: present - register: user_test0_1 - -- debug: - var: user_test0 - verbosity: 2 - -- name: make a list of users - script: userlist.sh {{ ansible_facts.distribution }} - register: user_names - -- debug: - var: user_names - verbosity: 2 - -- name: validate results for testcase 0 - assert: - that: - - user_test0_0 is changed - - user_test0_1 is not changed - - '"ansibulluser" in user_names.stdout_lines' - -# create system user - -- name: remove user - user: - name: ansibulluser - state: absent - -- name: create system user - user: - name: ansibulluser - state: present - system: yes - -# test adding user with uid -# https://github.com/ansible/ansible/issues/62969 -- name: remove the test user - user: - name: ansibulluser - state: absent - -- name: try to create a user with uid - user: - name: ansibulluser - state: present - uid: 572 - register: user_test01_0 - -- name: create the user again - user: - name: ansibulluser - state: present - uid: 572 - register: user_test01_1 - -- name: validate results for testcase 0 - assert: - that: - - user_test01_0 is changed - - user_test01_1 is not changed - -# test user add with password -- name: add an encrypted password for user - user: - name: ansibulluser - password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." - state: present - update_password: always - register: test_user_encrypt0 - -- name: there should not be warnings - assert: - that: "'warnings' not in test_user_encrypt0" - -# https://github.com/ansible/ansible/issues/65711 -- name: Test updating password only on creation - user: - name: ansibulluser - password: '*' - update_password: on_create - register: test_user_update_password - -- name: Ensure password was not changed - assert: - that: - - test_user_update_password is not changed - -- name: Verify password hash for Linux - when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] - block: - - name: LINUX | Get shadow entry for ansibulluser - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure password hash was not removed - assert: - that: - - getent_shadow['ansibulluser'][1] != '*' - -- block: - - name: add an plaintext password for user - user: - name: ansibulluser - password: "plaintextpassword" - state: present - update_password: always - register: test_user_encrypt1 - - - name: there should be a warning complains that the password is plaintext - assert: - that: "'warnings' in test_user_encrypt1" - - - name: add an invalid hashed password - user: - name: ansibulluser - password: "$6$rounds=656000$tgK3gYTyRLUmhyv2$lAFrYUQwn7E6VsjPOwQwoSx30lmpiU9r/E0Al7tzKrR9mkodcMEZGe9OXD0H/clOn6qdsUnaL4zefy5fG+++++" - state: present - update_password: always - register: test_user_encrypt2 - - - name: there should be a warning complains about the character set of password - assert: - that: "'warnings' in test_user_encrypt2" - - - name: change password to '!' - user: - name: ansibulluser - password: '!' - register: test_user_encrypt3 - - - name: change password to '*' - user: - name: ansibulluser - password: '*' - register: test_user_encrypt4 - - - name: change password to '*************' - user: - name: ansibulluser - password: '*************' - register: test_user_encrypt5 - - - name: there should be no warnings when setting the password to '!', '*' or '*************' - assert: - that: - - "'warnings' not in test_user_encrypt3" - - "'warnings' not in test_user_encrypt4" - - "'warnings' not in test_user_encrypt5" - when: ansible_facts.system != 'Darwin' - - -# https://github.com/ansible/ansible/issues/42484 -# Skipping macOS for now since there is a bug when changing home directory -- block: - - name: create user specifying home - user: - name: ansibulluser - state: present - home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" - register: user_test3_0 - - - name: create user again specifying home - user: - name: ansibulluser - state: present - home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" - register: user_test3_1 - - - name: change user home - user: - name: ansibulluser - state: present - home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser-mod" - register: user_test3_2 - - - name: change user home back - user: - name: ansibulluser - state: present - home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" - register: user_test3_3 - - - name: validate results for testcase 3 - assert: - that: - - user_test3_0 is not changed - - user_test3_1 is not changed - - user_test3_2 is changed - - user_test3_3 is changed - when: ansible_facts.system != 'Darwin' - -# https://github.com/ansible/ansible/issues/41393 -# Create a new user account with a path that has parent directories that do not exist -- name: Create user with home path that has parents that do not exist - user: - name: ansibulluser2 - state: present - home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" - register: create_home_with_no_parent_1 - -- name: Create user with home path that has parents that do not exist again - user: - name: ansibulluser2 - state: present - home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" - register: create_home_with_no_parent_2 - -- name: Check the created home directory - stat: - path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" - register: home_with_no_parent_3 - -- name: Ensure user with non-existing parent paths was created successfully - assert: - that: - - create_home_with_no_parent_1 is changed - - create_home_with_no_parent_1.home == user_home_prefix[ansible_facts.system] ~ '/in2deep/ansibulluser2' - - create_home_with_no_parent_2 is not changed - - home_with_no_parent_3.stat.uid == create_home_with_no_parent_1.uid - - home_with_no_parent_3.stat.gr_name == default_user_group[ansible_facts.distribution] | default('ansibulluser2') - -- name: Cleanup test account - user: - name: ansibulluser2 - home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" - state: absent - remove: yes - -- name: Remove testing dir - file: - path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/" - state: absent - - -# https://github.com/ansible/ansible/issues/60307 -# Make sure we can create a user when the home directory is missing -- name: Create user with home path that does not exist - user: - name: ansibulluser3 - state: present - home: "{{ user_home_prefix[ansible_facts.system] }}/nosuchdir" - createhome: no - -- name: Cleanup test account - user: - name: ansibulluser3 - state: absent - remove: yes - -# https://github.com/ansible/ansible/issues/70589 -# Create user with create_home: no and parent directory does not exist. -- name: "Check if parent dir for home dir for user exists (before)" - stat: - path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir" - register: create_user_no_create_home_with_no_parent_parent_dir_before - -- name: "Create user with create_home == no and home path parent dir does not exist" - user: - name: randomuser - state: present - create_home: false - home: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir/randomuser" - register: create_user_no_create_home_with_no_parent - -- name: "Check if parent dir for home dir for user exists (after)" - stat: - path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir" - register: create_user_no_create_home_with_no_parent_parent_dir_after - -- name: "Check if home for user is created" - stat: - path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir/randomuser" - register: create_user_no_create_home_with_no_parent_home_dir - -- name: "Ensure user with non-existing parent paths with create_home: no was created successfully" - assert: - that: - - not create_user_no_create_home_with_no_parent_parent_dir_before.stat.exists - - not create_user_no_create_home_with_no_parent_parent_dir_after.stat.isdir is defined - - not create_user_no_create_home_with_no_parent_home_dir.stat.exists - -- name: Cleanup test account - user: - name: randomuser - state: absent - remove: yes - -## user check - -- name: run existing user check tests - user: - name: "{{ user_names.stdout_lines | random }}" - state: present - create_home: no - loop: "{{ range(1, 5+1) | list }}" - register: user_test1 - -- debug: - var: user_test1 - verbosity: 2 - -- name: validate results for testcase 1 - assert: - that: - - user_test1.results is defined - - user_test1.results | length == 5 - -- name: validate changed results for testcase 1 - assert: - that: - - "user_test1.results[0] is not changed" - - "user_test1.results[1] is not changed" - - "user_test1.results[2] is not changed" - - "user_test1.results[3] is not changed" - - "user_test1.results[4] is not changed" - - "user_test1.results[0]['state'] == 'present'" - - "user_test1.results[1]['state'] == 'present'" - - "user_test1.results[2]['state'] == 'present'" - - "user_test1.results[3]['state'] == 'present'" - - "user_test1.results[4]['state'] == 'present'" - - -## user remove - -- name: try to delete the user - user: - name: ansibulluser - state: absent - force: true - register: user_test2 - -- name: make a new list of users - script: userlist.sh {{ ansible_facts.distribution }} - register: user_names2 - -- debug: - var: user_names2 - verbosity: 2 - -- name: validate results for testcase 2 - assert: - that: - - '"ansibulluser" not in user_names2.stdout_lines' - - -## create user without home and test fallback home dir create - -- block: - - name: create the user - user: - name: ansibulluser - - - name: delete the user and home dir - user: - name: ansibulluser - state: absent - force: true - remove: true - - - name: create the user without home - user: - name: ansibulluser - create_home: no - - - name: create the user home dir - user: - name: ansibulluser - register: user_create_home_fallback - - - name: stat home dir - stat: - path: '{{ user_create_home_fallback.home }}' - register: user_create_home_fallback_dir - - - name: read UMASK from /etc/login.defs and return mode - shell: | - import re - import os - try: - for line in open('/etc/login.defs').readlines(): - m = re.match(r'^UMASK\s+(\d+)$', line) - if m: - umask = int(m.group(1), 8) - except: - umask = os.umask(0) - mode = oct(0o777 & ~umask) - print(str(mode).replace('o', '')) - args: - executable: "{{ ansible_python_interpreter }}" - register: user_login_defs_umask - - - name: validate that user home dir is created - assert: - that: - - user_create_home_fallback is changed - - user_create_home_fallback_dir.stat.exists - - user_create_home_fallback_dir.stat.isdir - - user_create_home_fallback_dir.stat.pw_name == 'ansibulluser' - - user_create_home_fallback_dir.stat.mode == user_login_defs_umask.stdout - when: ansible_facts.system != 'Darwin' - -- block: - - name: create non-system user on macOS to test the shell is set to /bin/bash - user: - name: macosuser - register: macosuser_output - - - name: validate the shell is set to /bin/bash - assert: - that: - - 'macosuser_output.shell == "/bin/bash"' - - - name: cleanup - user: - name: macosuser - state: absent - - - name: create system user on macos to test the shell is set to /usr/bin/false - user: - name: macosuser - system: yes - register: macosuser_output - - - name: validate the shell is set to /usr/bin/false - assert: - that: - - 'macosuser_output.shell == "/usr/bin/false"' - - - name: cleanup - user: - name: macosuser - state: absent - - - name: create non-system user on macos and set the shell to /bin/sh - user: - name: macosuser - shell: /bin/sh - register: macosuser_output - - - name: validate the shell is set to /bin/sh - assert: - that: - - 'macosuser_output.shell == "/bin/sh"' - - - name: cleanup - user: - name: macosuser - state: absent - when: ansible_facts.distribution == "MacOSX" - - -## user expires -# Date is March 3, 2050 -- name: Set user expiration - user: - name: ansibulluser - state: present - expires: 2529881062 - register: user_test_expires1 - tags: - - timezone - -- name: Set user expiration again to ensure no change is made - user: - name: ansibulluser - state: present - expires: 2529881062 - register: user_test_expires2 - tags: - - timezone - -- name: Ensure that account with expiration was created and did not change on subsequent run - assert: - that: - - user_test_expires1 is changed - - user_test_expires2 is not changed - -- name: Verify expiration date for Linux - block: - - name: LINUX | Get expiration date for ansibulluser - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure proper expiration date was set - assert: - that: - - getent_shadow['ansibulluser'][6] == '29281' - when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] - - -- name: Verify expiration date for BSD - block: - - name: BSD | Get expiration date for ansibulluser - shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' - changed_when: no - register: bsd_account_expiration - - - name: BSD | Ensure proper expiration date was set - assert: - that: - - bsd_account_expiration.stdout == '2529881062' - when: ansible_facts.os_family == 'FreeBSD' - -- name: Change timezone - timezone: - name: America/Denver - register: original_timezone - tags: - - timezone - -- name: Change system timezone to make sure expiration comparison works properly - block: - - name: Create user with expiration again to ensure no change is made in a new timezone - user: - name: ansibulluser - state: present - expires: 2529881062 - register: user_test_different_tz - tags: - - timezone - - - name: Ensure that no change was reported - assert: - that: - - user_test_different_tz is not changed - tags: - - timezone - - always: - - name: Restore original timezone - {{ original_timezone.diff.before.name }} - timezone: - name: "{{ original_timezone.diff.before.name }}" - when: original_timezone.diff.before.name != "n/a" - tags: - - timezone - - - name: Restore original timezone when n/a - file: - path: /etc/sysconfig/clock - state: absent - when: - - original_timezone.diff.before.name == "n/a" - - "'/etc/sysconfig/clock' in original_timezone.msg" - tags: - - timezone - - -- name: Unexpire user - user: - name: ansibulluser - state: present - expires: -1 - register: user_test_expires3 - -- name: Verify un expiration date for Linux - block: - - name: LINUX | Get expiration date for ansibulluser - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure proper expiration date was set - assert: - msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" - that: - - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 - when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] - -- name: Verify un expiration date for Linux/BSD - block: - - name: Unexpire user again to check for change - user: - name: ansibulluser - state: present - expires: -1 - register: user_test_expires4 - - - name: Ensure first expiration reported a change and second did not - assert: - msg: The second run of the expiration removal task reported a change when it should not - that: - - user_test_expires3 is changed - - user_test_expires4 is not changed - when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse', 'FreeBSD'] - -- name: Verify un expiration date for BSD - block: - - name: BSD | Get expiration date for ansibulluser - shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' - changed_when: no - register: bsd_account_expiration - - - name: BSD | Ensure proper expiration date was set - assert: - msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" - that: - - bsd_account_expiration.stdout == '0' - when: ansible_facts.os_family == 'FreeBSD' - -# Test setting no expiration when creating a new account -# https://github.com/ansible/ansible/issues/44155 -- name: Remove ansibulluser - user: - name: ansibulluser - state: absent - -- name: Create user account without expiration - user: - name: ansibulluser - state: present - expires: -1 - register: user_test_create_no_expires_1 - -- name: Create user account without expiration again - user: - name: ansibulluser - state: present - expires: -1 - register: user_test_create_no_expires_2 - -- name: Ensure changes were made appropriately - assert: - msg: Setting 'expires='-1 resulted in incorrect changes - that: - - user_test_create_no_expires_1 is changed - - user_test_create_no_expires_2 is not changed - -- name: Verify un expiration date for Linux - block: - - name: LINUX | Get expiration date for ansibulluser - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure proper expiration date was set - assert: - msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" - that: - - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 - when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] - -- name: Verify un expiration date for BSD - block: - - name: BSD | Get expiration date for ansibulluser - shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' - changed_when: no - register: bsd_account_expiration - - - name: BSD | Ensure proper expiration date was set - assert: - msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" - that: - - bsd_account_expiration.stdout == '0' - when: ansible_facts.os_family == 'FreeBSD' - -# Test setting epoch 0 expiration when creating a new account, then removing the expiry -# https://github.com/ansible/ansible/issues/47114 -- name: Remove ansibulluser - user: - name: ansibulluser - state: absent - -- name: Create user account with epoch 0 expiration - user: - name: ansibulluser - state: present - expires: 0 - register: user_test_expires_create0_1 - -- name: Create user account with epoch 0 expiration again - user: - name: ansibulluser - state: present - expires: 0 - register: user_test_expires_create0_2 - -- name: Change the user account to remove the expiry time - user: - name: ansibulluser - expires: -1 - register: user_test_remove_expires_1 - -- name: Change the user account to remove the expiry time again - user: - name: ansibulluser - expires: -1 - register: user_test_remove_expires_2 - - -- name: Verify un expiration date for Linux - block: - - name: LINUX | Ensure changes were made appropriately - assert: - msg: Creating an account with 'expries=0' then removing that expriation with 'expires=-1' resulted in incorrect changes - that: - - user_test_expires_create0_1 is changed - - user_test_expires_create0_2 is not changed - - user_test_remove_expires_1 is changed - - user_test_remove_expires_2 is not changed - - - name: LINUX | Get expiration date for ansibulluser - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure proper expiration date was set - assert: - msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" - that: - - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 - when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] - - -- name: Verify proper expiration behavior for BSD - block: - - name: BSD | Ensure changes were made appropriately - assert: - msg: Creating an account with 'expries=0' then removing that expriation with 'expires=-1' resulted in incorrect changes - that: - - user_test_expires_create0_1 is changed - - user_test_expires_create0_2 is not changed - - user_test_remove_expires_1 is not changed - - user_test_remove_expires_2 is not changed - when: ansible_facts.os_family == 'FreeBSD' - -# Test expiration with a very large negative number. This should have the same -# result as setting -1. -- name: Set expiration date using very long negative number - user: - name: ansibulluser - state: present - expires: -2529881062 - register: user_test_expires5 - -- name: Ensure no change was made - assert: - that: - - user_test_expires5 is not changed - -- name: Verify un expiration date for Linux - block: - - name: LINUX | Get expiration date for ansibulluser - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure proper expiration date was set - assert: - msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" - that: - - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 - when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] - -- name: Verify un expiration date for BSD - block: - - name: BSD | Get expiration date for ansibulluser - shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' - changed_when: no - register: bsd_account_expiration - - - name: BSD | Ensure proper expiration date was set - assert: - msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" - that: - - bsd_account_expiration.stdout == '0' - when: ansible_facts.os_family == 'FreeBSD' - - -## shadow backup -- block: - - name: Create a user to test shadow file backup - user: - name: ansibulluser - state: present - register: result - - - name: Find shadow backup files - find: - path: /etc - patterns: 'shadow\..*~$' - use_regex: yes - register: shadow_backups - - - name: Assert that a backup file was created - assert: - that: - - result.bakup - - shadow_backups.files | map(attribute='path') | list | length > 0 - when: ansible_facts.os_family == 'Solaris' - - -# Test creating ssh key with passphrase -- name: Remove ansibulluser - user: - name: ansibulluser - state: absent - -- name: Create user with ssh key - user: - name: ansibulluser - state: present - generate_ssh_key: yes - force: yes - ssh_key_file: "{{ output_dir }}/test_id_rsa" - ssh_key_passphrase: secret_passphrase - -- name: Unlock ssh key - command: "ssh-keygen -y -f {{ output_dir }}/test_id_rsa -P secret_passphrase" - register: result - -- name: Check that ssh key was unlocked successfully - assert: - that: - - result.rc == 0 - -- name: Clean ssh key - file: - path: "{{ output_dir }}/test_id_rsa" - state: absent - when: ansible_os_family == 'FreeBSD' - - -## password lock -- block: - - name: Set password for ansibulluser - user: - name: ansibulluser - password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." - - - name: Lock account - user: - name: ansibulluser - password_lock: yes - register: password_lock_1 - - - name: Lock account again - user: - name: ansibulluser - password_lock: yes - register: password_lock_2 - - - name: Unlock account - user: - name: ansibulluser - password_lock: no - register: password_lock_3 - - - name: Unlock account again - user: - name: ansibulluser - password_lock: no - register: password_lock_4 - - - name: Ensure task reported changes appropriately - assert: - msg: The password_lock tasks did not make changes appropriately - that: - - password_lock_1 is changed - - password_lock_2 is not changed - - password_lock_3 is changed - - password_lock_4 is not changed - - - name: Lock account - user: - name: ansibulluser - password_lock: yes - - - name: Verify account lock for BSD - block: - - name: BSD | Get account status - shell: "{{ status_command[ansible_facts['system']] }}" - register: account_status_locked - - - name: Unlock account - user: - name: ansibulluser - password_lock: no - - - name: BSD | Get account status - shell: "{{ status_command[ansible_facts['system']] }}" - register: account_status_unlocked - - - name: FreeBSD | Ensure account is locked - assert: - that: - - "'LOCKED' in account_status_locked.stdout" - - "'LOCKED' not in account_status_unlocked.stdout" - when: ansible_facts['system'] == 'FreeBSD' - - when: ansible_facts['system'] in ['FreeBSD', 'OpenBSD'] - - - name: Verify account lock for Linux - block: - - name: LINUX | Get account status - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure account is locked - assert: - that: - - getent_shadow['ansibulluser'][0].startswith('!') - - - name: Unlock account - user: - name: ansibulluser - password_lock: no - - - name: LINUX | Get account status - getent: - database: shadow - key: ansibulluser - - - name: LINUX | Ensure account is unlocked - assert: - that: - - not getent_shadow['ansibulluser'][0].startswith('!') - - when: ansible_facts['system'] == 'Linux' - - always: - - name: Unlock account - user: - name: ansibulluser - password_lock: no - - when: ansible_facts['system'] in ['FreeBSD', 'OpenBSD', 'Linux'] - - - ## Check local mode - # Even if we don't have a system that is bound to a directory, it's useful - # to run with local: true to exercise the code path that reads through the local - # user database file. - # https://github.com/ansible/ansible/issues/50947 - -- name: Create /etc/gshadow - file: - path: /etc/gshadow - state: touch - when: ansible_facts.os_family == 'Suse' - tags: - - user_test_local_mode - -- name: Create /etc/libuser.conf - file: - path: /etc/libuser.conf - state: touch - when: - - ansible_facts.distribution == 'Ubuntu' - - ansible_facts.distribution_major_version is version_compare('16', '==') - tags: - - user_test_local_mode - -- name: Ensure luseradd is present - action: "{{ ansible_facts.pkg_mgr }}" - args: - name: libuser - state: present - when: ansible_facts.system in ['Linux'] - tags: - - user_test_local_mode - -- name: Create local account that already exists to check for warning - user: - name: root - local: yes - register: local_existing - tags: - - user_test_local_mode - -- name: Create local_ansibulluser - user: - name: local_ansibulluser - state: present - local: yes - register: local_user_test_1 - tags: - - user_test_local_mode - -- name: Create local_ansibulluser again - user: - name: local_ansibulluser - state: present - local: yes - register: local_user_test_2 - tags: - - user_test_local_mode - -- name: Remove local_ansibulluser - user: - name: local_ansibulluser - state: absent - remove: yes - local: yes - register: local_user_test_remove_1 - tags: - - user_test_local_mode - -- name: Remove local_ansibulluser again - user: - name: local_ansibulluser - state: absent - remove: yes - local: yes - register: local_user_test_remove_2 - tags: - - user_test_local_mode - -- name: Create test groups - group: - name: "{{ item }}" - loop: - - testgroup1 - - testgroup2 - - testgroup3 - - testgroup4 - tags: - - user_test_local_mode - -- name: Create local_ansibulluser with groups - user: - name: local_ansibulluser - state: present - local: yes - groups: ['testgroup1', 'testgroup2'] - register: local_user_test_3 - ignore_errors: yes - tags: - - user_test_local_mode - -- name: Append groups for local_ansibulluser - user: - name: local_ansibulluser - state: present - local: yes - groups: ['testgroup3', 'testgroup4'] - append: yes - register: local_user_test_4 - ignore_errors: yes - tags: - - user_test_local_mode - -- name: Test append without groups for local_ansibulluser - user: - name: local_ansibulluser - state: present - append: yes - register: local_user_test_5 - ignore_errors: yes - tags: - - user_test_local_mode - -- name: Remove local_ansibulluser again - user: - name: local_ansibulluser - state: absent - remove: yes - local: yes - tags: - - user_test_local_mode - -- name: Remove test groups - group: - name: "{{ item }}" - state: absent - loop: - - testgroup1 - - testgroup2 - - testgroup3 - - testgroup4 - tags: - - user_test_local_mode - -- name: Ensure local user accounts were created and removed properly - assert: - that: - - local_user_test_1 is changed - - local_user_test_2 is not changed - - local_user_test_3 is changed - - local_user_test_4 is changed - - local_user_test_remove_1 is changed - - local_user_test_remove_2 is not changed - tags: - - user_test_local_mode - -- name: Ensure warnings were displayed properly - assert: - that: - - local_user_test_1['warnings'] | length > 0 - - local_user_test_1['warnings'] | first is search('The local user account may already exist') - - local_user_test_5['warnings'] is search("'append' is set, but no 'groups' are specified. Use 'groups'") - - local_existing['warnings'] is not defined - when: ansible_facts.system in ['Linux'] - tags: - - user_test_local_mode - -- name: Test expires for local users - import_tasks: expires_local.yml +- import_tasks: test_create_user.yml +- import_tasks: test_create_system_user.yml +- import_tasks: test_create_user_uid.yml +- import_tasks: test_create_user_password.yml +- import_tasks: test_create_user_home.yml +- import_tasks: test_remove_user.yml +- import_tasks: test_no_home_fallback.yml +- import_tasks: test_expires.yml +- import_tasks: test_expires_new_account.yml +- import_tasks: test_expires_new_account_epoch_negative.yml +- import_tasks: test_shadow_backup.yml +- import_tasks: test_ssh_key_passphrase.yml +- import_tasks: test_password_lock.yml +- import_tasks: test_password_lock_new_user.yml +- import_tasks: test_local.yml diff --git a/test/integration/targets/user/tasks/test_create_system_user.yml b/test/integration/targets/user/tasks/test_create_system_user.yml new file mode 100644 index 00000000..da746c50 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_system_user.yml @@ -0,0 +1,12 @@ +# create system user + +- name: remove user + user: + name: ansibulluser + state: absent + +- name: create system user + user: + name: ansibulluser + state: present + system: yes diff --git a/test/integration/targets/user/tasks/test_create_user.yml b/test/integration/targets/user/tasks/test_create_user.yml new file mode 100644 index 00000000..bced7905 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user.yml @@ -0,0 +1,67 @@ +- name: remove the test user + user: + name: ansibulluser + state: absent + +- name: try to create a user + user: + name: ansibulluser + state: present + register: user_test0_0 + +- name: create the user again + user: + name: ansibulluser + state: present + register: user_test0_1 + +- debug: + var: user_test0 + verbosity: 2 + +- name: make a list of users + script: userlist.sh {{ ansible_facts.distribution }} + register: user_names + +- debug: + var: user_names + verbosity: 2 + +- name: validate results for testcase 0 + assert: + that: + - user_test0_0 is changed + - user_test0_1 is not changed + - '"ansibulluser" in user_names.stdout_lines' + +- name: run existing user check tests + user: + name: "{{ user_names.stdout_lines | random }}" + state: present + create_home: no + loop: "{{ range(1, 5+1) | list }}" + register: user_test1 + +- debug: + var: user_test1 + verbosity: 2 + +- name: validate results for testcase 1 + assert: + that: + - user_test1.results is defined + - user_test1.results | length == 5 + +- name: validate changed results for testcase 1 + assert: + that: + - "user_test1.results[0] is not changed" + - "user_test1.results[1] is not changed" + - "user_test1.results[2] is not changed" + - "user_test1.results[3] is not changed" + - "user_test1.results[4] is not changed" + - "user_test1.results[0]['state'] == 'present'" + - "user_test1.results[1]['state'] == 'present'" + - "user_test1.results[2]['state'] == 'present'" + - "user_test1.results[3]['state'] == 'present'" + - "user_test1.results[4]['state'] == 'present'" diff --git a/test/integration/targets/user/tasks/test_create_user_home.yml b/test/integration/targets/user/tasks/test_create_user_home.yml new file mode 100644 index 00000000..1b529f76 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user_home.yml @@ -0,0 +1,136 @@ +# https://github.com/ansible/ansible/issues/42484 +# Skipping macOS for now since there is a bug when changing home directory +- name: Test home directory creation + when: ansible_facts.system != 'Darwin' + block: + - name: create user specifying home + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" + register: user_test3_0 + + - name: create user again specifying home + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" + register: user_test3_1 + + - name: change user home + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser-mod" + register: user_test3_2 + + - name: change user home back + user: + name: ansibulluser + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/ansibulluser" + register: user_test3_3 + + - name: validate results for testcase 3 + assert: + that: + - user_test3_0 is not changed + - user_test3_1 is not changed + - user_test3_2 is changed + - user_test3_3 is changed + +# https://github.com/ansible/ansible/issues/41393 +# Create a new user account with a path that has parent directories that do not exist +- name: Create user with home path that has parents that do not exist + user: + name: ansibulluser2 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_with_no_parent_1 + +- name: Create user with home path that has parents that do not exist again + user: + name: ansibulluser2 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_with_no_parent_2 + +- name: Check the created home directory + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: home_with_no_parent_3 + +- name: Ensure user with non-existing parent paths was created successfully + assert: + that: + - create_home_with_no_parent_1 is changed + - create_home_with_no_parent_1.home == user_home_prefix[ansible_facts.system] ~ '/in2deep/ansibulluser2' + - create_home_with_no_parent_2 is not changed + - home_with_no_parent_3.stat.uid == create_home_with_no_parent_1.uid + - home_with_no_parent_3.stat.gr_name == default_user_group[ansible_facts.distribution] | default('ansibulluser2') + +- name: Cleanup test account + user: + name: ansibulluser2 + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + state: absent + remove: yes + +- name: Remove testing dir + file: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/" + state: absent + + +# https://github.com/ansible/ansible/issues/60307 +# Make sure we can create a user when the home directory is missing +- name: Create user with home path that does not exist + user: + name: ansibulluser3 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/nosuchdir" + createhome: no + +- name: Cleanup test account + user: + name: ansibulluser3 + state: absent + remove: yes + +# https://github.com/ansible/ansible/issues/70589 +# Create user with create_home: no and parent directory does not exist. +- name: "Check if parent dir for home dir for user exists (before)" + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir" + register: create_user_no_create_home_with_no_parent_parent_dir_before + +- name: "Create user with create_home == no and home path parent dir does not exist" + user: + name: randomuser + state: present + create_home: false + home: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir/randomuser" + register: create_user_no_create_home_with_no_parent + +- name: "Check if parent dir for home dir for user exists (after)" + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir" + register: create_user_no_create_home_with_no_parent_parent_dir_after + +- name: "Check if home for user is created" + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/thereisnodir/randomuser" + register: create_user_no_create_home_with_no_parent_home_dir + +- name: "Ensure user with non-existing parent paths with create_home: no was created successfully" + assert: + that: + - not create_user_no_create_home_with_no_parent_parent_dir_before.stat.exists + - not create_user_no_create_home_with_no_parent_parent_dir_after.stat.isdir is defined + - not create_user_no_create_home_with_no_parent_home_dir.stat.exists + +- name: Cleanup test account + user: + name: randomuser + state: absent + remove: yes diff --git a/test/integration/targets/user/tasks/test_create_user_password.yml b/test/integration/targets/user/tasks/test_create_user_password.yml new file mode 100644 index 00000000..02aae003 --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user_password.yml @@ -0,0 +1,90 @@ +# test user add with password +- name: add an encrypted password for user + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + state: present + update_password: always + register: test_user_encrypt0 + +- name: there should not be warnings + assert: + that: "'warnings' not in test_user_encrypt0" + +# https://github.com/ansible/ansible/issues/65711 +- name: Test updating password only on creation + user: + name: ansibulluser + password: '*' + update_password: on_create + register: test_user_update_password + +- name: Ensure password was not changed + assert: + that: + - test_user_update_password is not changed + +- name: Verify password hash for Linux + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + block: + - name: LINUX | Get shadow entry for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure password hash was not removed + assert: + that: + - getent_shadow['ansibulluser'][1] != '*' + +- name: Test plaintext warning + when: ansible_facts.system != 'Darwin' + block: + - name: add an plaintext password for user + user: + name: ansibulluser + password: "plaintextpassword" + state: present + update_password: always + register: test_user_encrypt1 + + - name: there should be a warning complains that the password is plaintext + assert: + that: "'warnings' in test_user_encrypt1" + + - name: add an invalid hashed password + user: + name: ansibulluser + password: "$6$rounds=656000$tgK3gYTyRLUmhyv2$lAFrYUQwn7E6VsjPOwQwoSx30lmpiU9r/E0Al7tzKrR9mkodcMEZGe9OXD0H/clOn6qdsUnaL4zefy5fG+++++" + state: present + update_password: always + register: test_user_encrypt2 + + - name: there should be a warning complains about the character set of password + assert: + that: "'warnings' in test_user_encrypt2" + + - name: change password to '!' + user: + name: ansibulluser + password: '!' + register: test_user_encrypt3 + + - name: change password to '*' + user: + name: ansibulluser + password: '*' + register: test_user_encrypt4 + + - name: change password to '*************' + user: + name: ansibulluser + password: '*************' + register: test_user_encrypt5 + + - name: there should be no warnings when setting the password to '!', '*' or '*************' + assert: + that: + - "'warnings' not in test_user_encrypt3" + - "'warnings' not in test_user_encrypt4" + - "'warnings' not in test_user_encrypt5" diff --git a/test/integration/targets/user/tasks/test_create_user_uid.yml b/test/integration/targets/user/tasks/test_create_user_uid.yml new file mode 100644 index 00000000..9ac8a96f --- /dev/null +++ b/test/integration/targets/user/tasks/test_create_user_uid.yml @@ -0,0 +1,26 @@ +# test adding user with uid +# https://github.com/ansible/ansible/issues/62969 +- name: remove the test user + user: + name: ansibulluser + state: absent + +- name: try to create a user with uid + user: + name: ansibulluser + state: present + uid: 572 + register: user_test01_0 + +- name: create the user again + user: + name: ansibulluser + state: present + uid: 572 + register: user_test01_1 + +- name: validate results for testcase 0 + assert: + that: + - user_test01_0 is changed + - user_test01_1 is not changed diff --git a/test/integration/targets/user/tasks/test_expires.yml b/test/integration/targets/user/tasks/test_expires.yml new file mode 100644 index 00000000..8c238934 --- /dev/null +++ b/test/integration/targets/user/tasks/test_expires.yml @@ -0,0 +1,147 @@ +# Date is March 3, 2050 +- name: Set user expiration + user: + name: ansibulluser + state: present + expires: 2529881062 + register: user_test_expires1 + tags: + - timezone + +- name: Set user expiration again to ensure no change is made + user: + name: ansibulluser + state: present + expires: 2529881062 + register: user_test_expires2 + tags: + - timezone + +- name: Ensure that account with expiration was created and did not change on subsequent run + assert: + that: + - user_test_expires1 is changed + - user_test_expires2 is not changed + +- name: Verify expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + that: + - getent_shadow['ansibulluser'][6] == '29281' + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + + +- name: Verify expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + that: + - bsd_account_expiration.stdout == '2529881062' + when: ansible_facts.os_family == 'FreeBSD' + +- name: Change timezone + timezone: + name: America/Denver + register: original_timezone + tags: + - timezone + +- name: Change system timezone to make sure expiration comparison works properly + block: + - name: Create user with expiration again to ensure no change is made in a new timezone + user: + name: ansibulluser + state: present + expires: 2529881062 + register: user_test_different_tz + tags: + - timezone + + - name: Ensure that no change was reported + assert: + that: + - user_test_different_tz is not changed + tags: + - timezone + + always: + - name: Restore original timezone - {{ original_timezone.diff.before.name }} + timezone: + name: "{{ original_timezone.diff.before.name }}" + when: original_timezone.diff.before.name != "n/a" + tags: + - timezone + + - name: Restore original timezone when n/a + file: + path: /etc/sysconfig/clock + state: absent + when: + - original_timezone.diff.before.name == "n/a" + - "'/etc/sysconfig/clock' in original_timezone.msg" + tags: + - timezone + + +- name: Unexpire user + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_expires3 + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Verify un expiration date for Linux/BSD + block: + - name: Unexpire user again to check for change + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_expires4 + + - name: Ensure first expiration reported a change and second did not + assert: + msg: The second run of the expiration removal task reported a change when it should not + that: + - user_test_expires3 is changed + - user_test_expires4 is not changed + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse', 'FreeBSD'] + +- name: Verify un expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" + that: + - bsd_account_expiration.stdout == '0' + when: ansible_facts.os_family == 'FreeBSD' diff --git a/test/integration/targets/user/tasks/test_expires_new_account.yml b/test/integration/targets/user/tasks/test_expires_new_account.yml new file mode 100644 index 00000000..b77d137f --- /dev/null +++ b/test/integration/targets/user/tasks/test_expires_new_account.yml @@ -0,0 +1,55 @@ +# Test setting no expiration when creating a new account +# https://github.com/ansible/ansible/issues/44155 +- name: Remove ansibulluser + user: + name: ansibulluser + state: absent + +- name: Create user account without expiration + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_create_no_expires_1 + +- name: Create user account without expiration again + user: + name: ansibulluser + state: present + expires: -1 + register: user_test_create_no_expires_2 + +- name: Ensure changes were made appropriately + assert: + msg: Setting 'expires='-1 resulted in incorrect changes + that: + - user_test_create_no_expires_1 is changed + - user_test_create_no_expires_2 is not changed + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Verify un expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" + that: + - bsd_account_expiration.stdout == '0' + when: ansible_facts.os_family == 'FreeBSD' diff --git a/test/integration/targets/user/tasks/test_expires_new_account_epoch_negative.yml b/test/integration/targets/user/tasks/test_expires_new_account_epoch_negative.yml new file mode 100644 index 00000000..77a07c4a --- /dev/null +++ b/test/integration/targets/user/tasks/test_expires_new_account_epoch_negative.yml @@ -0,0 +1,112 @@ +# Test setting epoch 0 expiration when creating a new account, then removing the expiry +# https://github.com/ansible/ansible/issues/47114 +- name: Remove ansibulluser + user: + name: ansibulluser + state: absent + +- name: Create user account with epoch 0 expiration + user: + name: ansibulluser + state: present + expires: 0 + register: user_test_expires_create0_1 + +- name: Create user account with epoch 0 expiration again + user: + name: ansibulluser + state: present + expires: 0 + register: user_test_expires_create0_2 + +- name: Change the user account to remove the expiry time + user: + name: ansibulluser + expires: -1 + register: user_test_remove_expires_1 + +- name: Change the user account to remove the expiry time again + user: + name: ansibulluser + expires: -1 + register: user_test_remove_expires_2 + + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Ensure changes were made appropriately + assert: + msg: Creating an account with 'expries=0' then removing that expriation with 'expires=-1' resulted in incorrect changes + that: + - user_test_expires_create0_1 is changed + - user_test_expires_create0_2 is not changed + - user_test_remove_expires_1 is changed + - user_test_remove_expires_2 is not changed + + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + + +- name: Verify proper expiration behavior for BSD + block: + - name: BSD | Ensure changes were made appropriately + assert: + msg: Creating an account with 'expries=0' then removing that expriation with 'expires=-1' resulted in incorrect changes + that: + - user_test_expires_create0_1 is changed + - user_test_expires_create0_2 is not changed + - user_test_remove_expires_1 is not changed + - user_test_remove_expires_2 is not changed + when: ansible_facts.os_family == 'FreeBSD' + + +# Test expiration with a very large negative number. This should have the same +# result as setting -1. +- name: Set expiration date using very long negative number + user: + name: ansibulluser + state: present + expires: -2529881062 + register: user_test_expires5 + +- name: Ensure no change was made + assert: + that: + - user_test_expires5 is not changed + +- name: Verify un expiration date for Linux + block: + - name: LINUX | Get expiration date for ansibulluser + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be empty or -1, not {{ getent_shadow['ansibulluser'][6] }}" + that: + - not getent_shadow['ansibulluser'][6] or getent_shadow['ansibulluser'][6] | int < 0 + when: ansible_facts.os_family in ['RedHat', 'Debian', 'Suse'] + +- name: Verify un expiration date for BSD + block: + - name: BSD | Get expiration date for ansibulluser + shell: 'grep ansibulluser /etc/master.passwd | cut -d: -f 7' + changed_when: no + register: bsd_account_expiration + + - name: BSD | Ensure proper expiration date was set + assert: + msg: "expiry is supposed to be '0', not {{ bsd_account_expiration.stdout }}" + that: + - bsd_account_expiration.stdout == '0' + when: ansible_facts.os_family == 'FreeBSD' diff --git a/test/integration/targets/user/tasks/test_local.yml b/test/integration/targets/user/tasks/test_local.yml new file mode 100644 index 00000000..16c79c57 --- /dev/null +++ b/test/integration/targets/user/tasks/test_local.yml @@ -0,0 +1,169 @@ +## Check local mode +# Even if we don't have a system that is bound to a directory, it's useful +# to run with local: true to exercise the code path that reads through the local +# user database file. +# https://github.com/ansible/ansible/issues/50947 + +- name: Create /etc/gshadow + file: + path: /etc/gshadow + state: touch + when: ansible_facts.os_family == 'Suse' + tags: + - user_test_local_mode + +- name: Create /etc/libuser.conf + file: + path: /etc/libuser.conf + state: touch + when: + - ansible_facts.distribution == 'Ubuntu' + - ansible_facts.distribution_major_version is version_compare('16', '==') + tags: + - user_test_local_mode + +- name: Ensure luseradd is present + action: "{{ ansible_facts.pkg_mgr }}" + args: + name: libuser + state: present + when: ansible_facts.system in ['Linux'] + tags: + - user_test_local_mode + +- name: Create local account that already exists to check for warning + user: + name: root + local: yes + register: local_existing + tags: + - user_test_local_mode + +- name: Create local_ansibulluser + user: + name: local_ansibulluser + state: present + local: yes + register: local_user_test_1 + tags: + - user_test_local_mode + +- name: Create local_ansibulluser again + user: + name: local_ansibulluser + state: present + local: yes + register: local_user_test_2 + tags: + - user_test_local_mode + +- name: Remove local_ansibulluser + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + register: local_user_test_remove_1 + tags: + - user_test_local_mode + +- name: Remove local_ansibulluser again + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + register: local_user_test_remove_2 + tags: + - user_test_local_mode + +- name: Create test groups + group: + name: "{{ item }}" + loop: + - testgroup1 + - testgroup2 + - testgroup3 + - testgroup4 + tags: + - user_test_local_mode + +- name: Create local_ansibulluser with groups + user: + name: local_ansibulluser + state: present + local: yes + groups: ['testgroup1', 'testgroup2'] + register: local_user_test_3 + ignore_errors: yes + tags: + - user_test_local_mode + +- name: Append groups for local_ansibulluser + user: + name: local_ansibulluser + state: present + local: yes + groups: ['testgroup3', 'testgroup4'] + append: yes + register: local_user_test_4 + ignore_errors: yes + tags: + - user_test_local_mode + +- name: Test append without groups for local_ansibulluser + user: + name: local_ansibulluser + state: present + append: yes + register: local_user_test_5 + ignore_errors: yes + tags: + - user_test_local_mode + +- name: Remove local_ansibulluser again + user: + name: local_ansibulluser + state: absent + remove: yes + local: yes + tags: + - user_test_local_mode + +- name: Remove test groups + group: + name: "{{ item }}" + state: absent + loop: + - testgroup1 + - testgroup2 + - testgroup3 + - testgroup4 + tags: + - user_test_local_mode + +- name: Ensure local user accounts were created and removed properly + assert: + that: + - local_user_test_1 is changed + - local_user_test_2 is not changed + - local_user_test_3 is changed + - local_user_test_4 is changed + - local_user_test_remove_1 is changed + - local_user_test_remove_2 is not changed + tags: + - user_test_local_mode + +- name: Ensure warnings were displayed properly + assert: + that: + - local_user_test_1['warnings'] | length > 0 + - local_user_test_1['warnings'] | first is search('The local user account may already exist') + - local_user_test_5['warnings'] is search("'append' is set, but no 'groups' are specified. Use 'groups'") + - local_existing['warnings'] is not defined + when: ansible_facts.system in ['Linux'] + tags: + - user_test_local_mode + +- name: Test expires for local users + import_tasks: test_local_expires.yml diff --git a/test/integration/targets/user/tasks/expires_local.yml b/test/integration/targets/user/tasks/test_local_expires.yml index e6620353..e6620353 100644 --- a/test/integration/targets/user/tasks/expires_local.yml +++ b/test/integration/targets/user/tasks/test_local_expires.yml diff --git a/test/integration/targets/user/tasks/test_no_home_fallback.yml b/test/integration/targets/user/tasks/test_no_home_fallback.yml new file mode 100644 index 00000000..f7627fae --- /dev/null +++ b/test/integration/targets/user/tasks/test_no_home_fallback.yml @@ -0,0 +1,106 @@ +## create user without home and test fallback home dir create + +- name: Test home directory creation + when: ansible_facts.system != 'Darwin' + block: + - name: create the user + user: + name: ansibulluser + + - name: delete the user and home dir + user: + name: ansibulluser + state: absent + force: true + remove: true + + - name: create the user without home + user: + name: ansibulluser + create_home: no + + - name: create the user home dir + user: + name: ansibulluser + register: user_create_home_fallback + + - name: stat home dir + stat: + path: '{{ user_create_home_fallback.home }}' + register: user_create_home_fallback_dir + + - name: read UMASK from /etc/login.defs and return mode + shell: | + import re + import os + try: + for line in open('/etc/login.defs').readlines(): + m = re.match(r'^UMASK\s+(\d+)$', line) + if m: + umask = int(m.group(1), 8) + except: + umask = os.umask(0) + mode = oct(0o777 & ~umask) + print(str(mode).replace('o', '')) + args: + executable: "{{ ansible_python_interpreter }}" + register: user_login_defs_umask + + - name: validate that user home dir is created + assert: + that: + - user_create_home_fallback is changed + - user_create_home_fallback_dir.stat.exists + - user_create_home_fallback_dir.stat.isdir + - user_create_home_fallback_dir.stat.pw_name == 'ansibulluser' + - user_create_home_fallback_dir.stat.mode == user_login_defs_umask.stdout + +- name: Create non-system user + when: ansible_facts.distribution == "MacOSX" + block: + - name: create non-system user on macOS to test the shell is set to /bin/bash + user: + name: macosuser + register: macosuser_output + + - name: validate the shell is set to /bin/bash + assert: + that: + - 'macosuser_output.shell == "/bin/bash"' + + - name: cleanup + user: + name: macosuser + state: absent + + - name: create system user on macOS to test the shell is set to /usr/bin/false + user: + name: macosuser + system: yes + register: macosuser_output + + - name: validate the shell is set to /usr/bin/false + assert: + that: + - 'macosuser_output.shell == "/usr/bin/false"' + + - name: cleanup + user: + name: macosuser + state: absent + + - name: create non-system user on macos and set the shell to /bin/sh + user: + name: macosuser + shell: /bin/sh + register: macosuser_output + + - name: validate the shell is set to /bin/sh + assert: + that: + - 'macosuser_output.shell == "/bin/sh"' + + - name: cleanup + user: + name: macosuser + state: absent diff --git a/test/integration/targets/user/tasks/test_password_lock.yml b/test/integration/targets/user/tasks/test_password_lock.yml new file mode 100644 index 00000000..dde374ee --- /dev/null +++ b/test/integration/targets/user/tasks/test_password_lock.yml @@ -0,0 +1,140 @@ +- name: Test password lock + when: ansible_facts.system in ['FreeBSD', 'OpenBSD', 'Linux'] + block: + - name: Remove ansibulluser + user: + name: ansibulluser + state: absent + remove: yes + + - name: Create ansibulluser with password + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + + - name: Lock account without password parameter + user: + name: ansibulluser + password_lock: yes + register: password_lock_1 + + - name: Lock account without password parameter again + user: + name: ansibulluser + password_lock: yes + register: password_lock_2 + + - name: Unlock account without password parameter + user: + name: ansibulluser + password_lock: no + register: password_lock_3 + + - name: Unlock account without password parameter again + user: + name: ansibulluser + password_lock: no + register: password_lock_4 + + - name: Lock account with password parameter + user: + name: ansibulluser + password_lock: yes + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_5 + + - name: Lock account with password parameter again + user: + name: ansibulluser + password_lock: yes + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_6 + + - name: Unlock account with password parameter + user: + name: ansibulluser + password_lock: no + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_7 + + - name: Unlock account with password parameter again + user: + name: ansibulluser + password_lock: no + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + register: password_lock_8 + + - name: Ensure task reported changes appropriately + assert: + msg: The password_lock tasks did not make changes appropriately + that: + - password_lock_1 is changed + - password_lock_2 is not changed + - password_lock_3 is changed + - password_lock_4 is not changed + - password_lock_5 is changed + - password_lock_6 is not changed + - password_lock_7 is changed + - password_lock_8 is not changed + + - name: Lock account + user: + name: ansibulluser + password_lock: yes + + - name: Verify account lock for BSD + when: ansible_facts.system in ['FreeBSD', 'OpenBSD'] + block: + - name: BSD | Get account status + shell: "{{ status_command[ansible_facts['system']] }}" + register: account_status_locked + + - name: Unlock account + user: + name: ansibulluser + password_lock: no + + - name: BSD | Get account status + shell: "{{ status_command[ansible_facts['system']] }}" + register: account_status_unlocked + + - name: FreeBSD | Ensure account is locked + assert: + that: + - "'LOCKED' in account_status_locked.stdout" + - "'LOCKED' not in account_status_unlocked.stdout" + when: ansible_facts['system'] == 'FreeBSD' + + - name: Verify account lock for Linux + when: ansible_facts.system == 'Linux' + block: + - name: LINUX | Get account status + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure account is locked + assert: + that: + - getent_shadow['ansibulluser'][0].startswith('!') + + - name: Unlock account + user: + name: ansibulluser + password_lock: no + + - name: LINUX | Get account status + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure account is unlocked + assert: + that: + - not getent_shadow['ansibulluser'][0].startswith('!') + + always: + - name: Unlock account + user: + name: ansibulluser + password_lock: no diff --git a/test/integration/targets/user/tasks/test_password_lock_new_user.yml b/test/integration/targets/user/tasks/test_password_lock_new_user.yml new file mode 100644 index 00000000..dd4f23da --- /dev/null +++ b/test/integration/targets/user/tasks/test_password_lock_new_user.yml @@ -0,0 +1,63 @@ +- name: Test password lock + when: ansible_facts.system in ['FreeBSD', 'OpenBSD', 'Linux'] + block: + - name: Remove ansibulluser + user: + name: ansibulluser + state: absent + remove: yes + + - name: Create ansibulluser with password and locked + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + password_lock: yes + register: create_with_lock_1 + + - name: Create ansibulluser with password and locked again + user: + name: ansibulluser + password: "$6$rounds=656000$TT4O7jz2M57npccl$33LF6FcUMSW11qrESXL1HX0BS.bsiT6aenFLLiVpsQh6hDtI9pJh5iY7x8J7ePkN4fP8hmElidHXaeD51pbGS." + password_lock: yes + register: create_with_lock_2 + + - name: Ensure task reported changes appropriately + assert: + msg: The password_lock tasks did not make changes appropriately + that: + - create_with_lock_1 is changed + - create_with_lock_2 is not changed + + - name: Verify account lock for BSD + when: ansible_facts.system in ['FreeBSD', 'OpenBSD'] + block: + - name: BSD | Get account status + shell: "{{ status_command[ansible_facts['system']] }}" + register: account_status_locked + + - name: FreeBSD | Ensure account is locked + assert: + that: + - "'LOCKED' in account_status_locked.stdout" + when: ansible_facts.system == 'FreeBSD' + + + - name: Verify account lock for Linux + when: ansible_facts.system == 'Linux' + block: + - name: LINUX | Get account status + getent: + database: shadow + key: ansibulluser + + - name: LINUX | Ensure account is locked + assert: + that: + - getent_shadow['ansibulluser'][0].startswith('!') + + + always: + - name: Unlock account + user: + name: ansibulluser + password_lock: no diff --git a/test/integration/targets/user/tasks/test_remove_user.yml b/test/integration/targets/user/tasks/test_remove_user.yml new file mode 100644 index 00000000..dea71cbf --- /dev/null +++ b/test/integration/targets/user/tasks/test_remove_user.yml @@ -0,0 +1,19 @@ +- name: try to delete the user + user: + name: ansibulluser + state: absent + force: true + register: user_test2 + +- name: make a new list of users + script: userlist.sh {{ ansible_facts.distribution }} + register: user_names2 + +- debug: + var: user_names2 + verbosity: 2 + +- name: validate results for testcase 2 + assert: + that: + - '"ansibulluser" not in user_names2.stdout_lines' diff --git a/test/integration/targets/user/tasks/test_shadow_backup.yml b/test/integration/targets/user/tasks/test_shadow_backup.yml new file mode 100644 index 00000000..2655fbf2 --- /dev/null +++ b/test/integration/targets/user/tasks/test_shadow_backup.yml @@ -0,0 +1,21 @@ +- name: Test shadow backup on Solaris + when: ansible_facts.os_family == 'Solaris' + block: + - name: Create a user to test shadow file backup + user: + name: ansibulluser + state: present + register: result + + - name: Find shadow backup files + find: + path: /etc + patterns: 'shadow\..*~$' + use_regex: yes + register: shadow_backups + + - name: Assert that a backup file was created + assert: + that: + - result.bakup + - shadow_backups.files | map(attribute='path') | list | length > 0 diff --git a/test/integration/targets/user/tasks/test_ssh_key_passphrase.yml b/test/integration/targets/user/tasks/test_ssh_key_passphrase.yml new file mode 100644 index 00000000..bb0486da --- /dev/null +++ b/test/integration/targets/user/tasks/test_ssh_key_passphrase.yml @@ -0,0 +1,29 @@ +# Test creating ssh key with passphrase +- name: Remove ansibulluser + user: + name: ansibulluser + state: absent + +- name: Create user with ssh key + user: + name: ansibulluser + state: present + generate_ssh_key: yes + force: yes + ssh_key_file: "{{ output_dir }}/test_id_rsa" + ssh_key_passphrase: secret_passphrase + +- name: Unlock ssh key + command: "ssh-keygen -y -f {{ output_dir }}/test_id_rsa -P secret_passphrase" + register: result + +- name: Check that ssh key was unlocked successfully + assert: + that: + - result.rc == 0 + +- name: Clean ssh key + file: + path: "{{ output_dir }}/test_id_rsa" + state: absent + when: ansible_os_family == 'FreeBSD' diff --git a/test/integration/targets/vault/runme.sh b/test/integration/targets/vault/runme.sh index e3b21d7f..197095bc 100755 --- a/test/integration/targets/vault/runme.sh +++ b/test/integration/targets/vault/runme.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -euvx + +export ANSIBLE_TEST_PREFER_VENV=1 source virtualenv.sh @@ -521,4 +523,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
\ No newline at end of file +ansible-playbook "$@" --vault-password-file vault-password single_vault_as_string.yml diff --git a/test/lib/ansible_test/_data/completion/remote.txt b/test/lib/ansible_test/_data/completion/remote.txt index 109a8088..dea4367b 100644 --- a/test/lib/ansible_test/_data/completion/remote.txt +++ b/test/lib/ansible_test/_data/completion/remote.txt @@ -2,6 +2,7 @@ freebsd/11.1 python=2.7,3.6 python_dir=/usr/local/bin freebsd/12.1 python=3.6,2.7 python_dir=/usr/local/bin osx/10.11 python=2.7 python_dir=/usr/local/bin macos/10.15 python=3.8 python_dir=/usr/local/bin +macos/11.1 python=3.9 python_dir=/usr/local/bin rhel/7.6 python=2.7 rhel/7.8 python=2.7 rhel/7.9 python=2.7 diff --git a/test/lib/ansible_test/_data/injector/virtualenv-isolated.sh b/test/lib/ansible_test/_data/injector/virtualenv-isolated.sh index 82f79980..af92a056 100644 --- a/test/lib/ansible_test/_data/injector/virtualenv-isolated.sh +++ b/test/lib/ansible_test/_data/injector/virtualenv-isolated.sh @@ -2,7 +2,13 @@ # Create and activate a fresh virtual environment with `source virtualenv-isolated.sh`. rm -rf "${OUTPUT_DIR}/venv" -"${ANSIBLE_TEST_PYTHON_INTERPRETER}" -m virtualenv --python "${ANSIBLE_TEST_PYTHON_INTERPRETER}" "${OUTPUT_DIR}/venv" + +# Try to use 'venv' if it is available, then fallback to 'virtualenv' since some systems provide 'venv' although it is non-functional. +if [ -z "${ANSIBLE_TEST_PREFER_VENV:-}" ] || [[ "${ANSIBLE_TEST_PYTHON_VERSION}" =~ ^2\. ]] || ! "${ANSIBLE_TEST_PYTHON_INTERPRETER}" -m venv "${OUTPUT_DIR}/venv" > /dev/null 2>&1; then + rm -rf "${OUTPUT_DIR}/venv" + "${ANSIBLE_TEST_PYTHON_INTERPRETER}" -m virtualenv --python "${ANSIBLE_TEST_PYTHON_INTERPRETER}" "${OUTPUT_DIR}/venv" +fi + set +ux source "${OUTPUT_DIR}/venv/bin/activate" set -ux diff --git a/test/lib/ansible_test/_data/injector/virtualenv.sh b/test/lib/ansible_test/_data/injector/virtualenv.sh index ccde2974..282e6074 100644 --- a/test/lib/ansible_test/_data/injector/virtualenv.sh +++ b/test/lib/ansible_test/_data/injector/virtualenv.sh @@ -2,7 +2,13 @@ # Create and activate a fresh virtual environment with `source virtualenv.sh`. rm -rf "${OUTPUT_DIR}/venv" -"${ANSIBLE_TEST_PYTHON_INTERPRETER}" -m virtualenv --system-site-packages --python "${ANSIBLE_TEST_PYTHON_INTERPRETER}" "${OUTPUT_DIR}/venv" + +# Try to use 'venv' if it is available, then fallback to 'virtualenv' since some systems provide 'venv' although it is non-functional. +if [ -z "${ANSIBLE_TEST_PREFER_VENV:-}" ] || [[ "${ANSIBLE_TEST_PYTHON_VERSION}" =~ ^2\. ]] || ! "${ANSIBLE_TEST_PYTHON_INTERPRETER}" -m venv --system-site-packages "${OUTPUT_DIR}/venv" > /dev/null 2>&1; then + rm -rf "${OUTPUT_DIR}/venv" + "${ANSIBLE_TEST_PYTHON_INTERPRETER}" -m virtualenv --system-site-packages --python "${ANSIBLE_TEST_PYTHON_INTERPRETER}" "${OUTPUT_DIR}/venv" +fi + set +ux source "${OUTPUT_DIR}/venv/bin/activate" set -ux diff --git a/test/lib/ansible_test/_data/requirements/constraints.txt b/test/lib/ansible_test/_data/requirements/constraints.txt index f613ef0e..e043bc52 100644 --- a/test/lib/ansible_test/_data/requirements/constraints.txt +++ b/test/lib/ansible_test/_data/requirements/constraints.txt @@ -52,12 +52,12 @@ antsibull-changelog == 0.7.0 antsibull >= 0.21.0 # freeze pylint and its requirements for consistent test results -astroid == 2.2.5 +astroid == 2.3.3 isort == 4.3.15 -lazy-object-proxy == 1.3.1 +lazy-object-proxy == 1.4.3 mccabe == 0.6.1 pylint == 2.3.1 -typed-ast == 1.4.0 # 1.4.0 is required to compile on Python 3.8 +typed-ast == 1.4.1 wrapt == 1.11.1 # freeze pycodestyle for consistent test results diff --git a/test/lib/ansible_test/_data/requirements/sanity.pylint.txt b/test/lib/ansible_test/_data/requirements/sanity.pylint.txt index 1b800bd0..438ca51d 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.pylint.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.pylint.txt @@ -1,3 +1,3 @@ -pylint ; python_version < '3.9' # installation fails on python 3.9.0b1 +pylint pyyaml # needed for collection_detail.py mccabe # pylint complexity testing diff --git a/test/lib/ansible_test/_data/sanity/pylint/plugins/blacklist.py b/test/lib/ansible_test/_data/sanity/pylint/plugins/unwanted.py index ac53aeda..7012feaa 100644 --- a/test/lib/ansible_test/_data/sanity/pylint/plugins/blacklist.py +++ b/test/lib/ansible_test/_data/sanity/pylint/plugins/unwanted.py @@ -14,8 +14,8 @@ ANSIBLE_TEST_MODULES_PATH = os.environ['ANSIBLE_TEST_MODULES_PATH'] ANSIBLE_TEST_MODULE_UTILS_PATH = os.environ['ANSIBLE_TEST_MODULE_UTILS_PATH'] -class BlacklistEntry: - """Defines a import blacklist entry.""" +class UnwantedEntry: + """Defines an unwanted import.""" def __init__(self, alternative, modules_only=False, names=None, ignore_paths=None): """ :type alternative: str @@ -58,11 +58,11 @@ def is_module_path(path): return path.startswith(ANSIBLE_TEST_MODULES_PATH) or path.startswith(ANSIBLE_TEST_MODULE_UTILS_PATH) -class AnsibleBlacklistChecker(BaseChecker): - """Checker for blacklisted imports and functions.""" +class AnsibleUnwantedChecker(BaseChecker): + """Checker for unwanted imports and functions.""" __implements__ = (IAstroidChecker,) - name = 'blacklist' + name = 'unwanted' BAD_IMPORT = 'ansible-bad-import' BAD_IMPORT_FROM = 'ansible-bad-import-from' @@ -84,58 +84,58 @@ class AnsibleBlacklistChecker(BaseChecker): 'Identifies imports which should not be used.'), ) - blacklist_imports = dict( + unwanted_imports = dict( # Additional imports that we may want to start checking: - # boto=BlacklistEntry('boto3', modules_only=True), - # requests=BlacklistEntry('ansible.module_utils.urls', modules_only=True), - # urllib=BlacklistEntry('ansible.module_utils.urls', modules_only=True), + # boto=UnwantedEntry('boto3', modules_only=True), + # requests=UnwantedEntry('ansible.module_utils.urls', modules_only=True), + # urllib=UnwantedEntry('ansible.module_utils.urls', modules_only=True), # see https://docs.python.org/2/library/urllib2.html - urllib2=BlacklistEntry('ansible.module_utils.urls', - ignore_paths=( - '/lib/ansible/module_utils/urls.py', - )), + urllib2=UnwantedEntry('ansible.module_utils.urls', + ignore_paths=( + '/lib/ansible/module_utils/urls.py', + )), # see https://docs.python.org/3.7/library/collections.abc.html - collections=BlacklistEntry('ansible.module_utils.common._collections_compat', - ignore_paths=( - '/lib/ansible/module_utils/common/_collections_compat.py', - ), - names=( - 'MappingView', - 'ItemsView', - 'KeysView', - 'ValuesView', - 'Mapping', 'MutableMapping', - 'Sequence', 'MutableSequence', - 'Set', 'MutableSet', - 'Container', - 'Hashable', - 'Sized', - 'Callable', - 'Iterable', - 'Iterator', - )), + collections=UnwantedEntry('ansible.module_utils.common._collections_compat', + ignore_paths=( + '/lib/ansible/module_utils/common/_collections_compat.py', + ), + names=( + 'MappingView', + 'ItemsView', + 'KeysView', + 'ValuesView', + 'Mapping', 'MutableMapping', + 'Sequence', 'MutableSequence', + 'Set', 'MutableSet', + 'Container', + 'Hashable', + 'Sized', + 'Callable', + 'Iterable', + 'Iterator', + )), ) - blacklist_functions = { + unwanted_functions = { # see https://docs.python.org/2/library/tempfile.html#tempfile.mktemp - 'tempfile.mktemp': BlacklistEntry('tempfile.mkstemp'), - - 'sys.exit': BlacklistEntry('exit_json or fail_json', - ignore_paths=( - '/lib/ansible/module_utils/basic.py', - '/lib/ansible/modules/async_wrapper.py', - '/lib/ansible/module_utils/common/removed.py', - ), - modules_only=True), - - 'builtins.print': BlacklistEntry('module.log or module.debug', - ignore_paths=( - '/lib/ansible/module_utils/basic.py', - '/lib/ansible/module_utils/common/removed.py', - ), - modules_only=True), + 'tempfile.mktemp': UnwantedEntry('tempfile.mkstemp'), + + 'sys.exit': UnwantedEntry('exit_json or fail_json', + ignore_paths=( + '/lib/ansible/module_utils/basic.py', + '/lib/ansible/modules/async_wrapper.py', + '/lib/ansible/module_utils/common/removed.py', + ), + modules_only=True), + + 'builtins.print': UnwantedEntry('module.log or module.debug', + ignore_paths=( + '/lib/ansible/module_utils/basic.py', + '/lib/ansible/module_utils/common/removed.py', + ), + modules_only=True), } def visit_import(self, node): @@ -163,7 +163,7 @@ class AnsibleBlacklistChecker(BaseChecker): module = last_child.name - entry = self.blacklist_imports.get(module) + entry = self.unwanted_imports.get(module) if entry and entry.names: if entry.applies_to(self.linter.current_file, node.attrname): @@ -183,7 +183,7 @@ class AnsibleBlacklistChecker(BaseChecker): if not func: continue - entry = self.blacklist_functions.get(func) + entry = self.unwanted_functions.get(func) if entry and entry.applies_to(self.linter.current_file): self.add_message(self.BAD_FUNCTION, args=(entry.alternative, func), node=node) @@ -197,7 +197,7 @@ class AnsibleBlacklistChecker(BaseChecker): """ self._check_module_import(node, modname) - entry = self.blacklist_imports.get(modname) + entry = self.unwanted_imports.get(modname) if not entry: return @@ -213,7 +213,7 @@ class AnsibleBlacklistChecker(BaseChecker): """ self._check_module_import(node, modname) - entry = self.blacklist_imports.get(modname) + entry = self.unwanted_imports.get(modname) if not entry: return @@ -239,4 +239,4 @@ class AnsibleBlacklistChecker(BaseChecker): def register(linter): """required method to auto register this checker """ - linter.register_checker(AnsibleBlacklistChecker(linter)) + linter.register_checker(AnsibleUnwantedChecker(linter)) diff --git a/test/lib/ansible_test/_data/setup/remote.sh b/test/lib/ansible_test/_data/setup/remote.sh index 654f678d..93dead5d 100644 --- a/test/lib/ansible_test/_data/setup/remote.sh +++ b/test/lib/ansible_test/_data/setup/remote.sh @@ -88,18 +88,10 @@ elif [ "${platform}" = "centos" ]; then done install_pip -elif [ "${platform}" = "macos" ]; then - while true; do - pip3 install --disable-pip-version-check --quiet \ - 'virtualenv<20' \ - && break - echo "Failed to install packages. Sleeping before trying again..." - sleep 10 - done elif [ "${platform}" = "osx" ]; then while true; do pip install --disable-pip-version-check --quiet \ - 'virtualenv<20' \ + 'virtualenv==16.7.10' \ && break echo "Failed to install packages. Sleeping before trying again..." sleep 10 diff --git a/test/lib/ansible_test/_internal/sanity/pylint.py b/test/lib/ansible_test/_internal/sanity/pylint.py index 769a1717..324e5873 100644 --- a/test/lib/ansible_test/_internal/sanity/pylint.py +++ b/test/lib/ansible_test/_internal/sanity/pylint.py @@ -64,6 +64,14 @@ class PylintTest(SanitySingleVersion): """Error code for ansible-test matching the format used by the underlying test program, or None if the program does not use error codes.""" return 'ansible-test' + @property + def supported_python_versions(self): # type: () -> t.Optional[t.Tuple[str, ...]] + """A tuple of supported Python versions or None if the test does not depend on specific Python versions.""" + # Python 3.9 is not supported on pylint < 2.5.0. + # Unfortunately pylint 2.5.0 and later include an unfixed regression. + # See: https://github.com/PyCQA/pylint/issues/3701 + return tuple(python_version for python_version in super(PylintTest, self).supported_python_versions if python_version not in ('3.9',)) + def filter_targets(self, targets): # type: (t.List[TestTarget]) -> t.List[TestTarget] """Return the given list of test targets, filtered to include only those relevant for the test.""" return [target for target in targets if os.path.splitext(target.path)[1] == '.py' or is_subdir(target.path, 'bin')] diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index e1b2b007..f1b926e6 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -211,6 +211,10 @@ test/integration/targets/ansible-runner/files/adhoc_example1.py future-import-bo test/integration/targets/ansible-runner/files/adhoc_example1.py metaclass-boilerplate test/integration/targets/ansible-runner/files/playbook_example1.py future-import-boilerplate test/integration/targets/ansible-runner/files/playbook_example1.py metaclass-boilerplate +test/integration/targets/ansible-test/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import +test/integration/targets/ansible-test/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import-from +test/integration/targets/ansible-test/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py pylint:ansible-bad-function +test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/filter/check_pylint.py pylint:blacklisted-name test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/modules/hello.py pylint:relative-beyond-top-level test/integration/targets/ansible-test/ansible_collections/ns/col/tests/unit/plugins/module_utils/test_my_util.py pylint:relative-beyond-top-level test/integration/targets/ansible-test/ansible_collections/ns/col/tests/unit/plugins/modules/test_hello.py pylint:relative-beyond-top-level |