summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLee Garrett <lgarrett@rocketjump.eu>2023-07-18 13:23:44 +0200
committerLee Garrett <lgarrett@rocketjump.eu>2023-07-18 13:23:44 +0200
commitaff27d44d75c760b1288814b4948fc2c4a937d6e (patch)
treecf44068a623e41bae78b03c7ee52e2e35273c20f
parenta00ca87e07387d5be8152f7e1d2a69701f9949d6 (diff)
downloaddebian-ansible-core-aff27d44d75c760b1288814b4948fc2c4a937d6e.zip
New upstream version 2.14.8
-rw-r--r--PKG-INFO2
-rw-r--r--changelogs/CHANGELOG-v2.14.rst22
-rw-r--r--changelogs/changelog.yaml34
-rw-r--r--docs/man/man1/ansible-config.12
-rw-r--r--docs/man/man1/ansible-console.112
-rw-r--r--docs/man/man1/ansible-doc.15
-rw-r--r--docs/man/man1/ansible-galaxy.12
-rw-r--r--docs/man/man1/ansible-inventory.16
-rw-r--r--docs/man/man1/ansible-playbook.112
-rw-r--r--docs/man/man1/ansible-pull.111
-rw-r--r--docs/man/man1/ansible-vault.12
-rw-r--r--docs/man/man1/ansible.112
-rw-r--r--lib/ansible/galaxy/collection/__init__.py27
-rw-r--r--lib/ansible/module_utils/ansible_release.py2
-rw-r--r--lib/ansible/playbook/base.py16
-rw-r--r--lib/ansible/release.py2
-rw-r--r--lib/ansible_core.egg-info/PKG-INFO2
-rw-r--r--lib/ansible_core.egg-info/SOURCES.txt8
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md (renamed from test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.rst)0
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/galaxy.yml2
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md (renamed from test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.rst)0
-rw-r--r--test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/galaxy.yml2
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md (renamed from test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.rst)0
-rw-r--r--test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/galaxy.yml2
-rw-r--r--test/integration/targets/dnf/tasks/main.yml1
-rw-r--r--test/integration/targets/dnf/vars/RedHat-9.yml5
-rw-r--r--test/integration/targets/reboot/tasks/main.yml1
-rw-r--r--test/integration/targets/reboot/tasks/test_reboot_command.yml22
-rw-r--r--test/lib/ansible_test/_internal/__init__.py7
-rw-r--r--test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py4
-rw-r--r--test/lib/ansible_test/_internal/commands/sanity/validate_modules.py4
-rw-r--r--test/lib/ansible_test/_internal/containers.py4
-rw-r--r--test/lib/ansible_test/_internal/coverage_util.py4
-rw-r--r--test/lib/ansible_test/_internal/payload.py4
-rw-r--r--test/lib/ansible_test/_internal/provisioning.py7
-rw-r--r--test/lib/ansible_test/_internal/pypi_proxy.py8
-rw-r--r--test/lib/ansible_test/_internal/util_common.py44
-rw-r--r--test/units/cli/galaxy/test_collection_extract_tar.py15
-rw-r--r--test/units/plugins/action/test_reboot.py214
39 files changed, 402 insertions, 127 deletions
diff --git a/PKG-INFO b/PKG-INFO
index d2ff788a..4c212bb8 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: ansible-core
-Version: 2.14.7
+Version: 2.14.8
Summary: Radically simple IT automation
Home-page: https://ansible.com/
Author: Ansible, Inc.
diff --git a/changelogs/CHANGELOG-v2.14.rst b/changelogs/CHANGELOG-v2.14.rst
index 0c912e3b..4f4df76b 100644
--- a/changelogs/CHANGELOG-v2.14.rst
+++ b/changelogs/CHANGELOG-v2.14.rst
@@ -5,6 +5,28 @@ ansible-core 2.14 "C'mon Everybody" Release Notes
.. contents:: Topics
+v2.14.8
+=======
+
+Release Summary
+---------------
+
+| Release Date: 2023-07-18
+| `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+
+Minor Changes
+-------------
+
+- Cache field attributes list on the playbook classes
+- Playbook objects - Replace deprecated stacked ``@classmethod`` and ``@property``
+- ansible-test - Use a context manager to perform cleanup at exit instead of using the built-in ``atexit`` module.
+
+Bugfixes
+--------
+
+- ansible-galaxy - Fix issue installing collections containing directories with more than 100 characters on python versions before 3.10.6
+
v2.14.7
=======
diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml
index f0137291..1024e9f8 100644
--- a/changelogs/changelog.yaml
+++ b/changelogs/changelog.yaml
@@ -1342,3 +1342,37 @@ releases:
- man-page-build-docs-dependency.yml
- update-maybe-json-uri.yml
release_date: '2023-06-12'
+ 2.14.8:
+ changes:
+ release_summary: '| Release Date: 2023-07-18
+
+ | `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+ '
+ codename: C'mon Everybody
+ fragments:
+ - 2.14.8_summary.yaml
+ release_date: '2023-07-17'
+ 2.14.8rc1:
+ changes:
+ bugfixes:
+ - ansible-galaxy - Fix issue installing collections containing directories with
+ more than 100 characters on python versions before 3.10.6
+ minor_changes:
+ - Cache field attributes list on the playbook classes
+ - Playbook objects - Replace deprecated stacked ``@classmethod`` and ``@property``
+ - ansible-test - Use a context manager to perform cleanup at exit instead of
+ using the built-in ``atexit`` module.
+ release_summary: '| Release Date: 2023-07-10
+
+ | `Porting Guide <https://docs.ansible.com/ansible-core/2.14/porting_guides/porting_guide_core_2.14.html>`__
+
+ '
+ codename: C'mon Everybody
+ fragments:
+ - 2.14.8rc1_summary.yaml
+ - ansible-test-atexit.yml
+ - cache-fa-on-pb-cls.yml
+ - long-collection-paths-fix.yml
+ - no-stacked-descriptors.yaml
+ release_date: '2023-07-10'
diff --git a/docs/man/man1/ansible-config.1 b/docs/man/man1/ansible-config.1
index 009cb4ef..53983058 100644
--- a/docs/man/man1/ansible-config.1
+++ b/docs/man/man1/ansible-config.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-CONFIG" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-CONFIG" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-config \- View ansible configuration.
.SH SYNOPSIS
diff --git a/docs/man/man1/ansible-console.1 b/docs/man/man1/ansible-console.1
index 9ee48cfe..b7621fe6 100644
--- a/docs/man/man1/ansible-console.1
+++ b/docs/man/man1/ansible-console.1
@@ -27,17 +27,15 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-CONSOLE" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-CONSOLE" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-console \- REPL console for executing Ansible tasks.
.SH SYNOPSIS
.INDENT 0.0
.TP
-.B usage: ansible\-console [\-h] [\-\-version] [\-v] [\-b] [\-\-become\-method BECOME_METHOD] [\-\-become\-user BECOME_USER] [\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-i INVENTORY]
-[\-\-list\-hosts] [\-l SUBSET] [\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER] [\-c CONNECTION] [\-T TIMEOUT] [\-\-ssh\-common\-args SSH_COMMON_ARGS]
-[\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS] [\-\-ssh\-extra\-args SSH_EXTRA_ARGS] [\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE]
-[\-C] [\-D] [\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-f FORKS] [\-M MODULE_PATH] [\-\-playbook\-dir BASEDIR]
-[\-e EXTRA_VARS] [\-\-task\-timeout TASK_TIMEOUT] [\-\-step]
+.B usage: ansible\-console [\-h] [\-\-version] [\-v] [\-b] [\-\-become\-method BECOME_METHOD] [\-\-become\-user BECOME_USER] [\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-i INVENTORY] [\-\-list\-hosts] [\-l SUBSET] [\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER]
+[\-c CONNECTION] [\-T TIMEOUT] [\-\-ssh\-common\-args SSH_COMMON_ARGS] [\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS] [\-\-ssh\-extra\-args SSH_EXTRA_ARGS] [\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE] [\-C]
+[\-D] [\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-f FORKS] [\-M MODULE_PATH] [\-\-playbook\-dir BASEDIR] [\-e EXTRA_VARS] [\-\-task\-timeout TASK_TIMEOUT] [\-\-step]
[pattern]
.UNINDENT
.SH DESCRIPTION
@@ -52,7 +50,7 @@ runtime:
.IP \(bu 2
\fIcd [pattern]\fP: change host/group (you can use host patterns eg.:
.UNINDENT
-.IP "System Message: WARNING/2 (:, line 35)"
+.IP "System Message: WARNING/2 (:, line 33)"
Bullet list ends without a blank line; unexpected unindent.
.sp
app*.dc*:!app01*)
diff --git a/docs/man/man1/ansible-doc.1 b/docs/man/man1/ansible-doc.1
index fd92c60b..8e96a0a2 100644
--- a/docs/man/man1/ansible-doc.1
+++ b/docs/man/man1/ansible-doc.1
@@ -27,14 +27,13 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-DOC" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-DOC" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-doc \- plugin documentation tool
.SH SYNOPSIS
.INDENT 0.0
.TP
-.B usage: ansible\-doc [\-h] [\-\-version] [\-v] [\-M MODULE_PATH] [\-\-playbook\-dir BASEDIR]
-[\-t {become,cache,callback,cliconf,connection,httpapi,inventory,lookup,netconf,shell,vars,module,strategy,test,filter,role,keyword}] [\-j] [\-r ROLES_PATH]
+.B usage: ansible\-doc [\-h] [\-\-version] [\-v] [\-M MODULE_PATH] [\-\-playbook\-dir BASEDIR] [\-t {become,cache,callback,cliconf,connection,httpapi,inventory,lookup,netconf,shell,vars,module,strategy,test,filter,role,keyword}] [\-j] [\-r ROLES_PATH]
[\-e ENTRY_POINT | \-s | \-F | \-l | \-\-metadata\-dump] [\-\-no\-fail\-on\-errors]
[plugin ...]
.UNINDENT
diff --git a/docs/man/man1/ansible-galaxy.1 b/docs/man/man1/ansible-galaxy.1
index 8ed2713a..cc56ba24 100644
--- a/docs/man/man1/ansible-galaxy.1
+++ b/docs/man/man1/ansible-galaxy.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-GALAXY" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-GALAXY" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-galaxy \- Perform various Role and Collection related operations.
.SH SYNOPSIS
diff --git a/docs/man/man1/ansible-inventory.1 b/docs/man/man1/ansible-inventory.1
index eaff6ae0..6446b136 100644
--- a/docs/man/man1/ansible-inventory.1
+++ b/docs/man/man1/ansible-inventory.1
@@ -27,14 +27,14 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-INVENTORY" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-INVENTORY" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-inventory \- None
.SH SYNOPSIS
.INDENT 0.0
.TP
-.B usage: ansible\-inventory [\-h] [\-\-version] [\-v] [\-i INVENTORY] [\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-\-playbook\-dir BASEDIR]
-[\-e EXTRA_VARS] [\-\-list] [\-\-host HOST] [\-\-graph] [\-y] [\-\-toml] [\-\-vars] [\-\-export] [\-\-output OUTPUT_FILE]
+.B usage: ansible\-inventory [\-h] [\-\-version] [\-v] [\-i INVENTORY] [\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-\-playbook\-dir BASEDIR] [\-e EXTRA_VARS] [\-\-list] [\-\-host HOST] [\-\-graph] [\-y] [\-\-toml] [\-\-vars]
+[\-\-export] [\-\-output OUTPUT_FILE]
[host|group]
.UNINDENT
.SH DESCRIPTION
diff --git a/docs/man/man1/ansible-playbook.1 b/docs/man/man1/ansible-playbook.1
index 412e1ace..7e41c477 100644
--- a/docs/man/man1/ansible-playbook.1
+++ b/docs/man/man1/ansible-playbook.1
@@ -27,18 +27,16 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-PLAYBOOK" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-PLAYBOOK" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-playbook \- Runs Ansible playbooks, executing the defined tasks on the targeted hosts.
.SH SYNOPSIS
.INDENT 0.0
.TP
-.B usage: ansible\-playbook [\-h] [\-\-version] [\-v] [\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER] [\-c CONNECTION] [\-T TIMEOUT] [\-\-ssh\-common\-args SSH_COMMON_ARGS]
-[\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS] [\-\-ssh\-extra\-args SSH_EXTRA_ARGS]
-[\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE] [\-\-force\-handlers] [\-\-flush\-cache] [\-b] [\-\-become\-method BECOME_METHOD] [\-\-become\-user BECOME_USER]
-[\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-t TAGS] [\-\-skip\-tags SKIP_TAGS] [\-C] [\-D] [\-i INVENTORY] [\-\-list\-hosts] [\-l SUBSET] [\-e EXTRA_VARS]
-[\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-f FORKS] [\-M MODULE_PATH] [\-\-syntax\-check] [\-\-list\-tasks]
-[\-\-list\-tags] [\-\-step] [\-\-start\-at\-task START_AT_TASK]
+.B usage: ansible\-playbook [\-h] [\-\-version] [\-v] [\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER] [\-c CONNECTION] [\-T TIMEOUT] [\-\-ssh\-common\-args SSH_COMMON_ARGS] [\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS]
+[\-\-ssh\-extra\-args SSH_EXTRA_ARGS] [\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE] [\-\-force\-handlers] [\-\-flush\-cache] [\-b] [\-\-become\-method BECOME_METHOD] [\-\-become\-user BECOME_USER]
+[\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-t TAGS] [\-\-skip\-tags SKIP_TAGS] [\-C] [\-D] [\-i INVENTORY] [\-\-list\-hosts] [\-l SUBSET] [\-e EXTRA_VARS] [\-\-vault\-id VAULT_IDS]
+[\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-f FORKS] [\-M MODULE_PATH] [\-\-syntax\-check] [\-\-list\-tasks] [\-\-list\-tags] [\-\-step] [\-\-start\-at\-task START_AT_TASK]
playbook [playbook ...]
.UNINDENT
.SH DESCRIPTION
diff --git a/docs/man/man1/ansible-pull.1 b/docs/man/man1/ansible-pull.1
index 46b6099b..0c73a23b 100644
--- a/docs/man/man1/ansible-pull.1
+++ b/docs/man/man1/ansible-pull.1
@@ -27,17 +27,16 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-PULL" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-PULL" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-pull \- pulls playbooks from a VCS repo and executes them for the local host
.SH SYNOPSIS
.INDENT 0.0
.TP
-.B usage: ansible\-pull [\-h] [\-\-version] [\-v] [\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER] [\-c CONNECTION] [\-T TIMEOUT] [\-\-ssh\-common\-args SSH_COMMON_ARGS]
-[\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS] [\-\-ssh\-extra\-args SSH_EXTRA_ARGS] [\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE]
-[\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-e EXTRA_VARS] [\-t TAGS] [\-\-skip\-tags SKIP_TAGS] [\-i INVENTORY]
-[\-\-list\-hosts] [\-l SUBSET] [\-M MODULE_PATH] [\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-\-purge] [\-o] [\-s SLEEP] [\-f] [\-d DEST] [\-U URL] [\-\-full]
-[\-C CHECKOUT] [\-\-accept\-host\-key] [\-m MODULE_NAME] [\-\-verify\-commit] [\-\-clean] [\-\-track\-subs] [\-\-check] [\-\-diff]
+.B usage: ansible\-pull [\-h] [\-\-version] [\-v] [\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER] [\-c CONNECTION] [\-T TIMEOUT] [\-\-ssh\-common\-args SSH_COMMON_ARGS] [\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS]
+[\-\-ssh\-extra\-args SSH_EXTRA_ARGS] [\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE] [\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-e EXTRA_VARS] [\-t TAGS] [\-\-skip\-tags SKIP_TAGS]
+[\-i INVENTORY] [\-\-list\-hosts] [\-l SUBSET] [\-M MODULE_PATH] [\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-\-purge] [\-o] [\-s SLEEP] [\-f] [\-d DEST] [\-U URL] [\-\-full] [\-C CHECKOUT] [\-\-accept\-host\-key] [\-m MODULE_NAME] [\-\-verify\-commit]
+[\-\-clean] [\-\-track\-subs] [\-\-check] [\-\-diff]
[playbook.yml ...]
.UNINDENT
.SH DESCRIPTION
diff --git a/docs/man/man1/ansible-vault.1 b/docs/man/man1/ansible-vault.1
index 6bd3785b..b4478547 100644
--- a/docs/man/man1/ansible-vault.1
+++ b/docs/man/man1/ansible-vault.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE-VAULT" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE-VAULT" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible-vault \- encryption/decryption utility for Ansible data files
.SH SYNOPSIS
diff --git a/docs/man/man1/ansible.1 b/docs/man/man1/ansible.1
index 51295aff..4b59da05 100644
--- a/docs/man/man1/ansible.1
+++ b/docs/man/man1/ansible.1
@@ -27,18 +27,16 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "ANSIBLE" 1 "" "Ansible 2.14.7" "System administration commands"
+.TH "ANSIBLE" 1 "" "Ansible 2.14.8" "System administration commands"
.SH NAME
ansible \- Define and run a single task 'playbook' against a set of hosts
.SH SYNOPSIS
.INDENT 0.0
.TP
-.B usage: ansible [\-h] [\-\-version] [\-v] [\-b] [\-\-become\-method BECOME_METHOD] [\-\-become\-user BECOME_USER] [\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-i INVENTORY] [\-\-list\-hosts]
-[\-l SUBSET] [\-P POLL_INTERVAL] [\-B SECONDS] [\-o] [\-t TREE] [\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER] [\-c CONNECTION] [\-T TIMEOUT]
-[\-\-ssh\-common\-args SSH_COMMON_ARGS] [\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS] [\-\-ssh\-extra\-args SSH_EXTRA_ARGS]
-[\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE] [\-C] [\-D] [\-e EXTRA_VARS] [\-\-vault\-id VAULT_IDS]
-[\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-f FORKS] [\-M MODULE_PATH] [\-\-playbook\-dir BASEDIR] [\-\-task\-timeout TASK_TIMEOUT] [\-a MODULE_ARGS]
-[\-m MODULE_NAME]
+.B usage: ansible [\-h] [\-\-version] [\-v] [\-b] [\-\-become\-method BECOME_METHOD] [\-\-become\-user BECOME_USER] [\-K | \-\-become\-password\-file BECOME_PASSWORD_FILE] [\-i INVENTORY] [\-\-list\-hosts] [\-l SUBSET] [\-P POLL_INTERVAL] [\-B SECONDS] [\-o] [\-t TREE]
+[\-\-private\-key PRIVATE_KEY_FILE] [\-u REMOTE_USER] [\-c CONNECTION] [\-T TIMEOUT] [\-\-ssh\-common\-args SSH_COMMON_ARGS] [\-\-sftp\-extra\-args SFTP_EXTRA_ARGS] [\-\-scp\-extra\-args SCP_EXTRA_ARGS] [\-\-ssh\-extra\-args SSH_EXTRA_ARGS]
+[\-k | \-\-connection\-password\-file CONNECTION_PASSWORD_FILE] [\-C] [\-D] [\-e EXTRA_VARS] [\-\-vault\-id VAULT_IDS] [\-\-ask\-vault\-password | \-\-vault\-password\-file VAULT_PASSWORD_FILES] [\-f FORKS] [\-M MODULE_PATH] [\-\-playbook\-dir BASEDIR]
+[\-\-task\-timeout TASK_TIMEOUT] [\-a MODULE_ARGS] [\-m MODULE_NAME]
pattern
.UNINDENT
.SH DESCRIPTION
diff --git a/lib/ansible/galaxy/collection/__init__.py b/lib/ansible/galaxy/collection/__init__.py
index 23482665..75aec751 100644
--- a/lib/ansible/galaxy/collection/__init__.py
+++ b/lib/ansible/galaxy/collection/__init__.py
@@ -1516,6 +1516,13 @@ def install_artifact(b_coll_targz_path, b_collection_path, b_temp_path, signatur
"""
try:
with tarfile.open(b_coll_targz_path, mode='r') as collection_tar:
+ # Remove this once py3.11 is our controller minimum
+ # Workaround for https://bugs.python.org/issue47231
+ # See _extract_tar_dir
+ collection_tar._ansible_normalized_cache = {
+ m.name.removesuffix(os.path.sep): m for m in collection_tar.getmembers()
+ } # deprecated: description='TarFile member index' core_version='2.18' python_version='3.11'
+
# Verify the signature on the MANIFEST.json before extracting anything else
_extract_tar_file(collection_tar, MANIFEST_FILENAME, b_collection_path, b_temp_path)
@@ -1595,22 +1602,12 @@ def install_src(collection, b_collection_path, b_collection_output_path, artifac
def _extract_tar_dir(tar, dirname, b_dest):
""" Extracts a directory from a collection tar. """
- member_names = [to_native(dirname, errors='surrogate_or_strict')]
-
- # Create list of members with and without trailing separator
- if not member_names[-1].endswith(os.path.sep):
- member_names.append(member_names[-1] + os.path.sep)
+ dirname = to_native(dirname, errors='surrogate_or_strict').removesuffix(os.path.sep)
- # Try all of the member names and stop on the first one that are able to successfully get
- for member in member_names:
- try:
- tar_member = tar.getmember(member)
- except KeyError:
- continue
- break
- else:
- # If we still can't find the member, raise a nice error.
- raise AnsibleError("Unable to extract '%s' from collection" % to_native(member, errors='surrogate_or_strict'))
+ try:
+ tar_member = tar._ansible_normalized_cache[dirname]
+ except KeyError:
+ raise AnsibleError("Unable to extract '%s' from collection" % dirname)
b_dir_path = os.path.join(b_dest, to_bytes(dirname, errors='surrogate_or_strict'))
diff --git a/lib/ansible/module_utils/ansible_release.py b/lib/ansible/module_utils/ansible_release.py
index 67de85c3..3c677c43 100644
--- a/lib/ansible/module_utils/ansible_release.py
+++ b/lib/ansible/module_utils/ansible_release.py
@@ -19,6 +19,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-__version__ = '2.14.7'
+__version__ = '2.14.8'
__author__ = 'Ansible, Inc.'
__codename__ = "C'mon Everybody"
diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py
index 669aa0ad..c772df11 100644
--- a/lib/ansible/playbook/base.py
+++ b/lib/ansible/playbook/base.py
@@ -10,6 +10,7 @@ import operator
import os
from copy import copy as shallowcopy
+from functools import cache
from jinja2.exceptions import UndefinedError
@@ -69,12 +70,21 @@ def _validate_action_group_metadata(action, found_group_metadata, fq_group_name)
display.warning(" ".join(metadata_warnings))
+class _ClassProperty:
+ def __set_name__(self, owner, name):
+ self.name = name
+
+ def __get__(self, obj, objtype=None):
+ return getattr(objtype, f'_{self.name}')()
+
+
class FieldAttributeBase:
+ fattributes = _ClassProperty()
+
@classmethod
- @property
- def fattributes(cls):
- # FIXME is this worth caching?
+ @cache
+ def _fattributes(cls):
fattributes = {}
for class_obj in reversed(cls.__mro__):
for name, attr in list(class_obj.__dict__.items()):
diff --git a/lib/ansible/release.py b/lib/ansible/release.py
index 67de85c3..3c677c43 100644
--- a/lib/ansible/release.py
+++ b/lib/ansible/release.py
@@ -19,6 +19,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-__version__ = '2.14.7'
+__version__ = '2.14.8'
__author__ = 'Ansible, Inc.'
__codename__ = "C'mon Everybody"
diff --git a/lib/ansible_core.egg-info/PKG-INFO b/lib/ansible_core.egg-info/PKG-INFO
index d2ff788a..4c212bb8 100644
--- a/lib/ansible_core.egg-info/PKG-INFO
+++ b/lib/ansible_core.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: ansible-core
-Version: 2.14.7
+Version: 2.14.8
Summary: Radically simple IT automation
Home-page: https://ansible.com/
Author: Ansible, Inc.
diff --git a/lib/ansible_core.egg-info/SOURCES.txt b/lib/ansible_core.egg-info/SOURCES.txt
index 4e770d7c..637c5cbf 100644
--- a/lib/ansible_core.egg-info/SOURCES.txt
+++ b/lib/ansible_core.egg-info/SOURCES.txt
@@ -1692,12 +1692,12 @@ test/integration/targets/ansible-test-sanity-validate-modules/ansible_collection
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/no_callable.py
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/sidecar.py
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/col/plugins/modules/sidecar.yaml
-test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.rst
+test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/galaxy.yml
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/meta/main.yml
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/plugins/modules/failure_ps.ps1
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/plugins/modules/failure_ps.yml
-test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.rst
+test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/galaxy.yml
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/meta/runtime.yml
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/plugins/module_utils/share_module.psm1
@@ -1708,7 +1708,7 @@ test/integration/targets/ansible-test-sanity-validate-modules/ansible_collection
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/plugins/modules/sidecar.yml
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/plugins/modules/validate.ps1
test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/plugins/modules/validate.py
-test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.rst
+test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md
test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/galaxy.yml
test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/meta/runtime.yml
test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/filter/check_pylint.py
@@ -3682,7 +3682,6 @@ test/integration/targets/reboot/tasks/main.yml
test/integration/targets/reboot/tasks/test_invalid_parameter.yml
test/integration/targets/reboot/tasks/test_invalid_test_command.yml
test/integration/targets/reboot/tasks/test_molly_guard.yml
-test/integration/targets/reboot/tasks/test_reboot_command.yml
test/integration/targets/reboot/tasks/test_standard_scenarios.yml
test/integration/targets/reboot/vars/main.yml
test/integration/targets/register/aliases
@@ -5279,6 +5278,7 @@ test/units/plugins/action/test_action.py
test/units/plugins/action/test_gather_facts.py
test/units/plugins/action/test_pause.py
test/units/plugins/action/test_raw.py
+test/units/plugins/action/test_reboot.py
test/units/plugins/become/__init__.py
test/units/plugins/become/conftest.py
test/units/plugins/become/test_su.py
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.rst b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md
index bf1003fa..bf1003fa 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.rst
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/README.md
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/galaxy.yml b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/galaxy.yml
index 3b116713..fcb47c6b 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/galaxy.yml
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/failure/galaxy.yml
@@ -1,6 +1,6 @@
namespace: ns
name: failure
version: 1.0.0
-readme: README.rst
+readme: README.md
authors:
- Ansible
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.rst b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md
index bbdd5138..bbdd5138 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.rst
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/README.md
diff --git a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/galaxy.yml b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/galaxy.yml
index 0a78b9e1..96fddb7f 100644
--- a/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/galaxy.yml
+++ b/test/integration/targets/ansible-test-sanity-validate-modules/ansible_collections/ns/ps_only/galaxy.yml
@@ -1,6 +1,6 @@
namespace: ns
name: ps_only
version: 1.0.0
-readme: README.rst
+readme: README.md
authors:
- Ansible
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.rst b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md
index d8138d3b..d8138d3b 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.rst
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/README.md
diff --git a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/galaxy.yml b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/galaxy.yml
index 08a32e80..cb5403f1 100644
--- a/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/galaxy.yml
+++ b/test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/galaxy.yml
@@ -1,6 +1,6 @@
namespace: ns
name: col
version: 1.0.0
-readme: README.rst
+readme: README.md
authors:
- Ansible
diff --git a/test/integration/targets/dnf/tasks/main.yml b/test/integration/targets/dnf/tasks/main.yml
index 66a171ac..65b77ceb 100644
--- a/test/integration/targets/dnf/tasks/main.yml
+++ b/test/integration/targets/dnf/tasks/main.yml
@@ -59,7 +59,6 @@
- include_tasks: modularity.yml
when:
- - astream_name is defined
- (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('29', '>=')) or
(ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>='))
tags:
diff --git a/test/integration/targets/dnf/vars/RedHat-9.yml b/test/integration/targets/dnf/vars/RedHat-9.yml
index 5681e701..680157dc 100644
--- a/test/integration/targets/dnf/vars/RedHat-9.yml
+++ b/test/integration/targets/dnf/vars/RedHat-9.yml
@@ -1,3 +1,2 @@
-# RHEL9.0 contains no modules, to be re-introduced in 9.1
-# astream_name: '@container-tools:latest/common'
-# astream_name_no_stream: '@container-tools/common'
+astream_name: '@php:8.1/minimal'
+astream_name_no_stream: '@php/minimal'
diff --git a/test/integration/targets/reboot/tasks/main.yml b/test/integration/targets/reboot/tasks/main.yml
index 4884f104..e92c2662 100644
--- a/test/integration/targets/reboot/tasks/main.yml
+++ b/test/integration/targets/reboot/tasks/main.yml
@@ -37,7 +37,6 @@
when: not in_container_env and in_split_controller_mode
block:
- import_tasks: test_standard_scenarios.yml
- - import_tasks: test_reboot_command.yml
- import_tasks: test_invalid_parameter.yml
- import_tasks: test_invalid_test_command.yml
- import_tasks: test_molly_guard.yml
diff --git a/test/integration/targets/reboot/tasks/test_reboot_command.yml b/test/integration/targets/reboot/tasks/test_reboot_command.yml
deleted file mode 100644
index 779d380b..00000000
--- a/test/integration/targets/reboot/tasks/test_reboot_command.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-- import_tasks: get_boot_time.yml
-- name: Reboot with custom reboot_command using unqualified path
- reboot:
- reboot_command: reboot
- register: reboot_result
-- import_tasks: check_reboot.yml
-
-
-- import_tasks: get_boot_time.yml
-- name: Reboot with custom reboot_command using absolute path
- reboot:
- reboot_command: /sbin/reboot
- register: reboot_result
-- import_tasks: check_reboot.yml
-
-
-- import_tasks: get_boot_time.yml
-- name: Reboot with custom reboot_command with parameters
- reboot:
- reboot_command: shutdown -r now
- register: reboot_result
-- import_tasks: check_reboot.yml
diff --git a/test/lib/ansible_test/_internal/__init__.py b/test/lib/ansible_test/_internal/__init__.py
index ee24a852..35584746 100644
--- a/test/lib/ansible_test/_internal/__init__.py
+++ b/test/lib/ansible_test/_internal/__init__.py
@@ -43,6 +43,7 @@ from .data import (
from .util_common import (
CommonConfig,
+ ExitHandler,
)
from .cli import (
@@ -59,6 +60,12 @@ from .config import (
def main(cli_args: t.Optional[list[str]] = None) -> None:
+ """Wrapper around the main program function to invoke cleanup functions at exit."""
+ with ExitHandler.context():
+ main_internal(cli_args)
+
+
+def main_internal(cli_args: t.Optional[list[str]] = None) -> None:
"""Main program function."""
try:
os.chdir(data_context().content.root)
diff --git a/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py b/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py
index eac9265a..cf920bc9 100644
--- a/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py
+++ b/test/lib/ansible_test/_internal/commands/integration/cloud/__init__.py
@@ -2,7 +2,6 @@
from __future__ import annotations
import abc
-import atexit
import datetime
import os
import re
@@ -28,6 +27,7 @@ from ....util import (
)
from ....util_common import (
+ ExitHandler,
ResultType,
write_json_test_results,
)
@@ -306,7 +306,7 @@ class CloudProvider(CloudBase):
self.resource_prefix = self.ci_provider.generate_resource_prefix()
self.resource_prefix = re.sub(r'[^a-zA-Z0-9]+', '-', self.resource_prefix)[:63].lower().rstrip('-')
- atexit.register(self.cleanup)
+ ExitHandler.register(self.cleanup)
def cleanup(self) -> None:
"""Clean up the cloud resource and any temporary configuration files after tests complete."""
diff --git a/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py b/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
index ab7dd93c..72616e77 100644
--- a/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
+++ b/test/lib/ansible_test/_internal/commands/sanity/validate_modules.py
@@ -1,7 +1,6 @@
"""Sanity test using validate-modules."""
from __future__ import annotations
-import atexit
import collections
import contextlib
import json
@@ -37,6 +36,7 @@ from ...util import (
)
from ...util_common import (
+ ExitHandler,
process_scoped_temporary_directory,
run_command,
ResultType,
@@ -237,7 +237,7 @@ class ValidateModulesTest(SanitySingleVersion):
files = payload_config.files
files.append((path, os.path.relpath(path, data_context().content.root)))
- atexit.register(cleanup)
+ ExitHandler.register(cleanup)
data_context().register_payload_callback(git_callback)
make_dirs(os.path.dirname(path))
diff --git a/test/lib/ansible_test/_internal/containers.py b/test/lib/ansible_test/_internal/containers.py
index bfc36434..869f1fba 100644
--- a/test/lib/ansible_test/_internal/containers.py
+++ b/test/lib/ansible_test/_internal/containers.py
@@ -1,7 +1,6 @@
"""High level functions for working with containers."""
from __future__ import annotations
-import atexit
import collections.abc as c
import contextlib
import enum
@@ -20,6 +19,7 @@ from .util import (
)
from .util_common import (
+ ExitHandler,
named_temporary_file,
)
@@ -225,7 +225,7 @@ def run_support_container(
raise Exception(f'Container already defined: {name}')
if not support_containers:
- atexit.register(cleanup_containers, args)
+ ExitHandler.register(cleanup_containers, args)
support_containers[name] = descriptor
diff --git a/test/lib/ansible_test/_internal/coverage_util.py b/test/lib/ansible_test/_internal/coverage_util.py
index 0af1cac4..ae640249 100644
--- a/test/lib/ansible_test/_internal/coverage_util.py
+++ b/test/lib/ansible_test/_internal/coverage_util.py
@@ -1,7 +1,6 @@
"""Utility code for facilitating collection of code coverage when running tests."""
from __future__ import annotations
-import atexit
import dataclasses
import os
import sqlite3
@@ -34,6 +33,7 @@ from .data import (
)
from .util_common import (
+ ExitHandler,
intercept_python,
ResultType,
)
@@ -223,7 +223,7 @@ def get_coverage_config(args: TestConfig) -> str:
temp_dir = '/tmp/coverage-temp-dir'
else:
temp_dir = tempfile.mkdtemp()
- atexit.register(lambda: remove_tree(temp_dir))
+ ExitHandler.register(lambda: remove_tree(temp_dir))
path = os.path.join(temp_dir, COVERAGE_CONFIG_NAME)
diff --git a/test/lib/ansible_test/_internal/payload.py b/test/lib/ansible_test/_internal/payload.py
index 10dde7b8..ab9739b4 100644
--- a/test/lib/ansible_test/_internal/payload.py
+++ b/test/lib/ansible_test/_internal/payload.py
@@ -1,7 +1,6 @@
"""Payload management for sending Ansible files and test content to other systems (VMs, containers)."""
from __future__ import annotations
-import atexit
import os
import stat
import tarfile
@@ -32,6 +31,7 @@ from .data import (
from .util_common import (
CommonConfig,
+ ExitHandler,
)
# improve performance by disabling uid/gid lookups
@@ -192,7 +192,7 @@ def create_temporary_bin_files(args: CommonConfig) -> tuple[tuple[str, str], ...
temp_path = '/tmp/ansible-tmp-bin'
else:
temp_path = tempfile.mkdtemp(prefix='ansible', suffix='bin')
- atexit.register(remove_tree, temp_path)
+ ExitHandler.register(remove_tree, temp_path)
for name, dest in ANSIBLE_BIN_SYMLINK_MAP.items():
path = os.path.join(temp_path, name)
diff --git a/test/lib/ansible_test/_internal/provisioning.py b/test/lib/ansible_test/_internal/provisioning.py
index e7f0fd31..4710757b 100644
--- a/test/lib/ansible_test/_internal/provisioning.py
+++ b/test/lib/ansible_test/_internal/provisioning.py
@@ -1,7 +1,6 @@
"""Provision hosts for running tests."""
from __future__ import annotations
-import atexit
import collections.abc as c
import dataclasses
import functools
@@ -27,6 +26,10 @@ from .util import (
type_guard,
)
+from .util_common import (
+ ExitHandler,
+)
+
from .thread import (
WrappedThread,
)
@@ -124,7 +127,7 @@ def prepare_profiles(
raise PrimeContainers()
- atexit.register(functools.partial(cleanup_profiles, host_state))
+ ExitHandler.register(functools.partial(cleanup_profiles, host_state))
def provision(profile: HostProfile) -> None:
"""Provision the given profile."""
diff --git a/test/lib/ansible_test/_internal/pypi_proxy.py b/test/lib/ansible_test/_internal/pypi_proxy.py
index 97663ead..5380dd9b 100644
--- a/test/lib/ansible_test/_internal/pypi_proxy.py
+++ b/test/lib/ansible_test/_internal/pypi_proxy.py
@@ -1,7 +1,6 @@
"""PyPI proxy management."""
from __future__ import annotations
-import atexit
import os
import urllib.parse
@@ -23,6 +22,7 @@ from .util import (
)
from .util_common import (
+ ExitHandler,
process_scoped_temporary_file,
)
@@ -128,7 +128,7 @@ def configure_target_pypi_proxy(args: EnvironmentConfig, profile: HostProfile, p
run_playbook(args, inventory_path, 'pypi_proxy_prepare.yml', capture=True, variables=dict(
pypi_endpoint=pypi_endpoint, pypi_hostname=pypi_hostname, force=force))
- atexit.register(cleanup_pypi_proxy)
+ ExitHandler.register(cleanup_pypi_proxy)
def configure_pypi_proxy_pip(args: EnvironmentConfig, profile: HostProfile, pypi_endpoint: str, pypi_hostname: str) -> None:
@@ -153,7 +153,7 @@ trusted-host = {1}
if not args.explain:
write_text_file(pip_conf_path, pip_conf, True)
- atexit.register(pip_conf_cleanup)
+ ExitHandler.register(pip_conf_cleanup)
def configure_pypi_proxy_easy_install(args: EnvironmentConfig, profile: HostProfile, pypi_endpoint: str) -> None:
@@ -177,4 +177,4 @@ index_url = {0}
if not args.explain:
write_text_file(pydistutils_cfg_path, pydistutils_cfg, True)
- atexit.register(pydistutils_cfg_cleanup)
+ ExitHandler.register(pydistutils_cfg_cleanup)
diff --git a/test/lib/ansible_test/_internal/util_common.py b/test/lib/ansible_test/_internal/util_common.py
index 79ff6c03..222366e4 100644
--- a/test/lib/ansible_test/_internal/util_common.py
+++ b/test/lib/ansible_test/_internal/util_common.py
@@ -1,7 +1,6 @@
"""Common utility code that depends on CommonConfig."""
from __future__ import annotations
-import atexit
import collections.abc as c
import contextlib
import json
@@ -64,6 +63,39 @@ from .host_configs import (
CHECK_YAML_VERSIONS: dict[str, t.Any] = {}
+class ExitHandler:
+ """Simple exit handler implementation."""
+ _callbacks: list[tuple[t.Callable, tuple[t.Any, ...], dict[str, t.Any]]] = []
+
+ @staticmethod
+ def register(func: t.Callable, *args, **kwargs) -> None:
+ """Register the given function and args as a callback to execute during program termination."""
+ ExitHandler._callbacks.append((func, args, kwargs))
+
+ @staticmethod
+ @contextlib.contextmanager
+ def context() -> t.Generator[None, None, None]:
+ """Run all registered handlers when the context is exited."""
+ last_exception: BaseException | None = None
+
+ try:
+ yield
+ finally:
+ queue = list(ExitHandler._callbacks)
+
+ while queue:
+ func, args, kwargs = queue.pop()
+
+ try:
+ func(*args, **kwargs)
+ except BaseException as ex: # pylint: disable=broad-except
+ last_exception = ex
+ display.fatal(f'Exit handler failed: {ex}')
+
+ if last_exception:
+ raise last_exception
+
+
class ShellScriptTemplate:
"""A simple substitution template for shell scripts."""
@@ -211,7 +243,7 @@ def process_scoped_temporary_file(args: CommonConfig, prefix: t.Optional[str] =
else:
temp_fd, path = tempfile.mkstemp(prefix=prefix, suffix=suffix)
os.close(temp_fd)
- atexit.register(lambda: os.remove(path))
+ ExitHandler.register(lambda: os.remove(path))
return path
@@ -222,7 +254,7 @@ def process_scoped_temporary_directory(args: CommonConfig, prefix: t.Optional[st
path = os.path.join(tempfile.gettempdir(), f'{prefix or tempfile.gettempprefix()}{generate_name()}{suffix or ""}')
else:
path = tempfile.mkdtemp(prefix=prefix, suffix=suffix)
- atexit.register(lambda: remove_tree(path))
+ ExitHandler.register(lambda: remove_tree(path))
return path
@@ -296,7 +328,7 @@ def get_injector_path() -> str:
"""Remove the temporary injector directory."""
remove_tree(injector_path)
- atexit.register(cleanup_injector)
+ ExitHandler.register(cleanup_injector)
return injector_path
@@ -354,7 +386,7 @@ def get_python_path(interpreter: str) -> str:
verified_chmod(python_path, MODE_DIRECTORY)
if not PYTHON_PATHS:
- atexit.register(cleanup_python_paths)
+ ExitHandler.register(cleanup_python_paths)
PYTHON_PATHS[interpreter] = python_path
@@ -364,7 +396,7 @@ def get_python_path(interpreter: str) -> str:
def create_temp_dir(prefix: t.Optional[str] = None, suffix: t.Optional[str] = None, base_dir: t.Optional[str] = None) -> str:
"""Create a temporary directory that persists until the current process exits."""
temp_path = tempfile.mkdtemp(prefix=prefix or 'tmp', suffix=suffix or '', dir=base_dir)
- atexit.register(remove_tree, temp_path)
+ ExitHandler.register(remove_tree, temp_path)
return temp_path
diff --git a/test/units/cli/galaxy/test_collection_extract_tar.py b/test/units/cli/galaxy/test_collection_extract_tar.py
index 526442cc..b84f60b6 100644
--- a/test/units/cli/galaxy/test_collection_extract_tar.py
+++ b/test/units/cli/galaxy/test_collection_extract_tar.py
@@ -14,6 +14,7 @@ from ansible.galaxy.collection import _extract_tar_dir
@pytest.fixture
def fake_tar_obj(mocker):
m_tarfile = mocker.Mock()
+ m_tarfile._ansible_normalized_cache = {'/some/dir': mocker.Mock()}
m_tarfile.type = mocker.Mock(return_value=b'99')
m_tarfile.SYMTYPE = mocker.Mock(return_value=b'22')
@@ -22,23 +23,11 @@ def fake_tar_obj(mocker):
def test_extract_tar_member_trailing_sep(mocker):
m_tarfile = mocker.Mock()
- m_tarfile.getmember = mocker.Mock(side_effect=KeyError)
+ m_tarfile._ansible_normalized_cache = {}
with pytest.raises(AnsibleError, match='Unable to extract'):
_extract_tar_dir(m_tarfile, '/some/dir/', b'/some/dest')
- assert m_tarfile.getmember.call_count == 1
-
-
-def test_extract_tar_member_no_trailing_sep(mocker):
- m_tarfile = mocker.Mock()
- m_tarfile.getmember = mocker.Mock(side_effect=KeyError)
-
- with pytest.raises(AnsibleError, match='Unable to extract'):
- _extract_tar_dir(m_tarfile, '/some/dir', b'/some/dest')
-
- assert m_tarfile.getmember.call_count == 2
-
def test_extract_tar_dir_exists(mocker, fake_tar_obj):
mocker.patch('os.makedirs', return_value=None)
diff --git a/test/units/plugins/action/test_reboot.py b/test/units/plugins/action/test_reboot.py
new file mode 100644
index 00000000..36d9e12d
--- /dev/null
+++ b/test/units/plugins/action/test_reboot.py
@@ -0,0 +1,214 @@
+# Copyright (c) 2022 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""Tests for the reboot action plugin."""
+import os
+
+import pytest
+
+from ansible.errors import AnsibleConnectionFailure
+from ansible.playbook.task import Task
+from ansible.plugins.action.reboot import ActionModule as RebootAction
+from ansible.plugins.loader import connection_loader
+
+
+@pytest.fixture
+def task_args(request):
+ """Return playbook task args."""
+ return getattr(request, 'param', {})
+
+
+@pytest.fixture
+def module_task(mocker, task_args):
+ """Construct a task object."""
+ task = mocker.MagicMock(Task)
+ task.action = 'reboot'
+ task.args = task_args
+ task.async_val = False
+ return task
+
+
+@pytest.fixture
+def play_context(mocker):
+ """Construct a play context."""
+ ctx = mocker.MagicMock()
+ ctx.check_mode = False
+ ctx.shell = 'sh'
+ return ctx
+
+
+@pytest.fixture
+def action_plugin(play_context, module_task):
+ """Initialize an action plugin."""
+ connection = connection_loader.get('local', play_context, os.devnull)
+ loader = None
+ templar = None
+ shared_loader_obj = None
+
+ return RebootAction(
+ module_task,
+ connection,
+ play_context,
+ loader,
+ templar,
+ shared_loader_obj,
+ )
+
+
+_SENTINEL_REBOOT_COMMAND = '/reboot-command-mock --arg'
+_SENTINEL_SHORT_REBOOT_COMMAND = '/reboot-command-mock'
+_SENTINEL_TEST_COMMAND = 'cmd-stub'
+
+
+@pytest.mark.parametrize(
+ 'task_args',
+ (
+ {
+ 'reboot_timeout': 5,
+ 'reboot_command': _SENTINEL_REBOOT_COMMAND,
+ 'test_command': _SENTINEL_TEST_COMMAND,
+ },
+ {
+ 'reboot_timeout': 5,
+ 'reboot_command': _SENTINEL_SHORT_REBOOT_COMMAND,
+ 'test_command': _SENTINEL_TEST_COMMAND,
+ },
+ ),
+ ids=('reboot command with spaces', 'reboot command without spaces'),
+ indirect=('task_args', ),
+)
+def test_reboot_command(action_plugin, mocker, monkeypatch, task_args):
+ """Check that the reboot command gets called and reboot verified."""
+ def _patched_low_level_execute_command(cmd, *args, **kwargs):
+ return {
+ _SENTINEL_TEST_COMMAND: {
+ 'rc': 0,
+ 'stderr': '<test command stub-stderr>',
+ 'stdout': '<test command stub-stdout>',
+ },
+ _SENTINEL_REBOOT_COMMAND: {
+ 'rc': 0,
+ 'stderr': '<reboot command stub-stderr>',
+ 'stdout': '<reboot command stub-stdout>',
+ },
+ f'{_SENTINEL_SHORT_REBOOT_COMMAND} ': { # no args is concatenated
+ 'rc': 0,
+ 'stderr': '<short reboot command stub-stderr>',
+ 'stdout': '<short reboot command stub-stdout>',
+ },
+ }[cmd]
+
+ monkeypatch.setattr(
+ action_plugin,
+ '_low_level_execute_command',
+ _patched_low_level_execute_command,
+ )
+
+ action_plugin._connection = mocker.Mock()
+
+ monkeypatch.setattr(action_plugin, 'check_boot_time', lambda *_a, **_kw: 5)
+ monkeypatch.setattr(action_plugin, 'get_distribution', mocker.MagicMock())
+ monkeypatch.setattr(action_plugin, 'get_system_boot_time', lambda d: 0)
+
+ low_level_cmd_spy = mocker.spy(action_plugin, '_low_level_execute_command')
+
+ action_result = action_plugin.run()
+
+ assert low_level_cmd_spy.called
+
+ expected_reboot_command = (
+ task_args['reboot_command'] if ' ' in task_args['reboot_command']
+ else f'{task_args["reboot_command"] !s} '
+ )
+ low_level_cmd_spy.assert_any_call(expected_reboot_command, sudoable=True)
+ low_level_cmd_spy.assert_any_call(task_args['test_command'], sudoable=True)
+
+ assert low_level_cmd_spy.call_count == 2
+ assert low_level_cmd_spy.spy_return == {
+ 'rc': 0,
+ 'stderr': '<test command stub-stderr>',
+ 'stdout': '<test command stub-stdout>',
+ }
+ assert low_level_cmd_spy.spy_exception is None
+
+ assert 'failed' not in action_result
+ assert action_result == {'rebooted': True, 'changed': True, 'elapsed': 0}
+
+
+@pytest.mark.parametrize(
+ 'task_args',
+ (
+ {
+ 'reboot_timeout': 5,
+ 'reboot_command': _SENTINEL_REBOOT_COMMAND,
+ 'test_command': _SENTINEL_TEST_COMMAND,
+ },
+ ),
+ ids=('reboot command with spaces', ),
+ indirect=('task_args', ),
+)
+def test_reboot_command_connection_fail(action_plugin, mocker, monkeypatch, task_args):
+ """Check that the reboot command gets called and reboot verified."""
+ def _patched_low_level_execute_command(cmd, *args, **kwargs):
+ if cmd == _SENTINEL_REBOOT_COMMAND:
+ raise AnsibleConnectionFailure('Fake connection drop')
+ return {
+ _SENTINEL_TEST_COMMAND: {
+ 'rc': 0,
+ 'stderr': '<test command stub-stderr>',
+ 'stdout': '<test command stub-stdout>',
+ },
+ }[cmd]
+
+ monkeypatch.setattr(
+ action_plugin,
+ '_low_level_execute_command',
+ _patched_low_level_execute_command,
+ )
+
+ action_plugin._connection = mocker.Mock()
+
+ monkeypatch.setattr(action_plugin, 'check_boot_time', lambda *_a, **_kw: 5)
+ monkeypatch.setattr(action_plugin, 'get_distribution', mocker.MagicMock())
+ monkeypatch.setattr(action_plugin, 'get_system_boot_time', lambda d: 0)
+
+ low_level_cmd_spy = mocker.spy(action_plugin, '_low_level_execute_command')
+
+ action_result = action_plugin.run()
+
+ assert low_level_cmd_spy.called
+
+ low_level_cmd_spy.assert_any_call(
+ task_args['reboot_command'], sudoable=True,
+ )
+ low_level_cmd_spy.assert_any_call(task_args['test_command'], sudoable=True)
+
+ assert low_level_cmd_spy.call_count == 2
+ assert low_level_cmd_spy.spy_return == {
+ 'rc': 0,
+ 'stderr': '<test command stub-stderr>',
+ 'stdout': '<test command stub-stdout>',
+ }
+
+ assert 'failed' not in action_result
+ assert action_result == {'rebooted': True, 'changed': True, 'elapsed': 0}
+
+
+def test_reboot_connection_local(action_plugin, module_task):
+ """Verify that using local connection doesn't let reboot happen."""
+ expected_message = ' '.join(
+ (
+ 'Running', module_task.action,
+ 'with local connection would reboot the control node.',
+ ),
+ )
+ expected_action_result = {
+ 'changed': False,
+ 'elapsed': 0,
+ 'failed': True,
+ 'msg': expected_message,
+ 'rebooted': False,
+ }
+
+ action_result = action_plugin.run()
+
+ assert action_result == expected_action_result