summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLee Garrett <lgarrett@rocketjump.eu>2021-01-19 05:35:08 +0100
committerLee Garrett <lgarrett@rocketjump.eu>2021-01-19 05:35:08 +0100
commit0addf1f445fd4e07fc0be2f62931208314ac5603 (patch)
treed8f6655f57698db86eedb5f7be5e6390bb9ae079 /test
parent3647b7e79c9fb3566efe439bb35176bddf4822d0 (diff)
downloaddebian-ansible-core-0addf1f445fd4e07fc0be2f62931208314ac5603.zip
New upstream version 2.10.5+dfsg
Diffstat (limited to 'test')
-rwxr-xr-xtest/integration/targets/ansible-galaxy/runme.sh11
-rw-r--r--test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/filter/check_pylint.py21
-rw-r--r--test/integration/targets/ansible-test/ansible_collections/ns/col/plugins/modules/bad.py34
-rw-r--r--test/integration/targets/ansible-test/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py16
-rw-r--r--test/integration/targets/ansible-test/ansible_collections/ns/col/tests/sanity/ignore.txt6
-rwxr-xr-xtest/integration/targets/ansible-test/collection-tests/venv.sh4
-rw-r--r--test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/common_handlers/handlers/main.yml21
-rw-r--r--test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/roles/test_fqcn_handlers/tasks/main.yml11
-rwxr-xr-xtest/integration/targets/collections_runtime_pythonpath/runme.sh2
-rwxr-xr-xtest/integration/targets/connection/test.sh2
-rw-r--r--test/integration/targets/connection/test_reset_connection.yml5
-rw-r--r--test/integration/targets/copy/tasks/selinux.yml35
-rwxr-xr-xtest/integration/targets/delegate_to/runme.sh1
-rw-r--r--test/integration/targets/expect/aliases1
-rw-r--r--test/integration/targets/expect/tasks/main.yml5
-rwxr-xr-xtest/integration/targets/filter_urls/runme.sh1
-rwxr-xr-xtest/integration/targets/groupby_filter/runme.sh1
-rwxr-xr-xtest/integration/targets/hash/runme.sh3
-rw-r--r--test/integration/targets/hash/test_inv1.yml10
-rw-r--r--test/integration/targets/hash/test_inv2.yml8
-rw-r--r--test/integration/targets/hash/test_inventory_hash.yml41
-rwxr-xr-xtest/integration/targets/incidental_inventory_docker_swarm/runme.sh1
-rwxr-xr-xtest/integration/targets/lookup_password/runme.sh1
-rw-r--r--test/integration/targets/noexec/aliases3
-rw-r--r--test/integration/targets/noexec/inventory1
-rwxr-xr-xtest/integration/targets/noexec/runme.sh9
-rw-r--r--test/integration/targets/noexec/test-noexec.yml8
-rwxr-xr-xtest/integration/targets/old_style_cache_plugins/runme.sh1
-rw-r--r--test/integration/targets/pip/tasks/main.yml34
-rw-r--r--test/integration/targets/pip/tasks/pip.yml4
-rw-r--r--test/integration/targets/setup_paramiko/install-Darwin-python-3.yml (renamed from test/integration/targets/setup_paramiko/install-MacOSX-10-python-3.yml)0
-rw-r--r--test/integration/targets/setup_paramiko/install.yml1
-rw-r--r--test/integration/targets/setup_paramiko/setup.sh1
-rw-r--r--test/integration/targets/setup_paramiko/uninstall-Darwin-python-3.yml (renamed from test/integration/targets/setup_paramiko/uninstall-MacOSX-10-python-3.yml)0
-rw-r--r--test/integration/targets/setup_paramiko/uninstall.yml1
-rw-r--r--test/integration/targets/setup_pexpect/files/constraints.txt2
-rw-r--r--test/integration/targets/setup_pexpect/meta/main.yml2
-rw-r--r--test/integration/targets/setup_pexpect/tasks/main.yml6
-rw-r--r--test/integration/targets/systemd/handlers/main.yml4
-rw-r--r--test/integration/targets/systemd/tasks/main.yml133
-rw-r--r--test/integration/targets/systemd/tasks/test_unit_template.yml50
-rw-r--r--test/integration/targets/systemd/templates/sleeper@.service8
-rw-r--r--test/integration/targets/systemd/vars/Debian.yml2
-rw-r--r--test/integration/targets/systemd/vars/default.yml2
-rwxr-xr-xtest/integration/targets/template_jinja2_latest/runme.sh1
-rw-r--r--test/integration/targets/user/tasks/main.yml1132
-rw-r--r--test/integration/targets/user/tasks/test_create_system_user.yml12
-rw-r--r--test/integration/targets/user/tasks/test_create_user.yml67
-rw-r--r--test/integration/targets/user/tasks/test_create_user_home.yml136
-rw-r--r--test/integration/targets/user/tasks/test_create_user_password.yml90
-rw-r--r--test/integration/targets/user/tasks/test_create_user_uid.yml26
-rw-r--r--test/integration/targets/user/tasks/test_expires.yml147
-rw-r--r--test/integration/targets/user/tasks/test_expires_new_account.yml55
-rw-r--r--test/integration/targets/user/tasks/test_expires_new_account_epoch_negative.yml112
-rw-r--r--test/integration/targets/user/tasks/test_local.yml169
-rw-r--r--test/integration/targets/user/tasks/test_local_expires.yml (renamed from test/integration/targets/user/tasks/expires_local.yml)0
-rw-r--r--test/integration/targets/user/tasks/test_no_home_fallback.yml106
-rw-r--r--test/integration/targets/user/tasks/test_password_lock.yml140
-rw-r--r--test/integration/targets/user/tasks/test_password_lock_new_user.yml63
-rw-r--r--test/integration/targets/user/tasks/test_remove_user.yml19
-rw-r--r--test/integration/targets/user/tasks/test_shadow_backup.yml21
-rw-r--r--test/integration/targets/user/tasks/test_ssh_key_passphrase.yml29
-rwxr-xr-xtest/integration/targets/vault/runme.sh4
-rw-r--r--test/lib/ansible_test/_data/completion/remote.txt1
-rw-r--r--test/lib/ansible_test/_data/injector/virtualenv-isolated.sh8
-rw-r--r--test/lib/ansible_test/_data/injector/virtualenv.sh8
-rw-r--r--test/lib/ansible_test/_data/requirements/constraints.txt6
-rw-r--r--test/lib/ansible_test/_data/requirements/sanity.pylint.txt2
-rw-r--r--test/lib/ansible_test/_data/sanity/pylint/plugins/unwanted.py (renamed from test/lib/ansible_test/_data/sanity/pylint/plugins/blacklist.py)108
-rw-r--r--test/lib/ansible_test/_data/setup/remote.sh10
-rw-r--r--test/lib/ansible_test/_internal/sanity/pylint.py8
-rw-r--r--test/sanity/ignore.txt4
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 (&lt;unknown&gt;, line 109)'
+# Python 3.9 fails with astroid 2.2.5 but works on 2.3.3
+# syntax-error: Cannot import 'string' due to syntax error 'invalid syntax (&lt;unknown&gt;, line 104)'
+import string
+
+# 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