ansible-core 2.17.4rc1__py3-none-any.whl → 2.18.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ansible-core might be problematic. Click here for more details.
- ansible/__main__.py +2 -17
- ansible/cli/__init__.py +3 -15
- ansible/cli/config.py +187 -24
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +38 -16
- ansible/cli/galaxy.py +30 -53
- ansible/cli/inventory.py +2 -2
- ansible/cli/pull.py +2 -2
- ansible/cli/scripts/ansible_connection_cli_stub.py +1 -10
- ansible/config/base.yml +127 -57
- ansible/config/manager.py +89 -11
- ansible/constants.py +32 -9
- ansible/errors/__init__.py +5 -0
- ansible/executor/interpreter_discovery.py +1 -1
- ansible/executor/play_iterator.py +16 -0
- ansible/executor/playbook_executor.py +1 -4
- ansible/executor/powershell/become_wrapper.ps1 +4 -5
- ansible/executor/powershell/bootstrap_wrapper.ps1 +2 -3
- ansible/executor/powershell/exec_wrapper.ps1 +1 -1
- ansible/executor/powershell/module_manifest.py +2 -2
- ansible/executor/task_executor.py +50 -39
- ansible/executor/task_queue_manager.py +1 -1
- ansible/executor/task_result.py +1 -1
- ansible/galaxy/api.py +3 -4
- ansible/galaxy/collection/__init__.py +21 -10
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -2
- ansible/galaxy/collection/galaxy_api_proxy.py +10 -16
- ansible/galaxy/collection/gpg.py +17 -23
- ansible/galaxy/data/COPYING +7 -0
- ansible/galaxy/data/apb/Dockerfile.j2 +1 -0
- ansible/galaxy/data/apb/Makefile.j2 +1 -0
- ansible/galaxy/data/apb/README.md +7 -3
- ansible/galaxy/data/apb/apb.yml.j2 +1 -0
- ansible/galaxy/data/apb/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/playbooks/deprovision.yml.j2 +1 -0
- ansible/galaxy/data/apb/playbooks/provision.yml.j2 +1 -0
- ansible/galaxy/data/apb/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/apb/tests/ansible.cfg +1 -0
- ansible/galaxy/data/apb/tests/inventory +1 -0
- ansible/galaxy/data/apb/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/apb/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/collections_galaxy_meta.yml +1 -0
- ansible/galaxy/data/container/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/container/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/container/meta/container.yml.j2 +1 -0
- ansible/galaxy/data/container/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/container/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/container/tests/ansible.cfg +1 -0
- ansible/galaxy/data/container/tests/inventory +1 -0
- ansible/galaxy/data/container/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/container/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/default/collection/README.md.j2 +1 -0
- ansible/galaxy/data/default/collection/galaxy.yml.j2 +1 -0
- ansible/galaxy/data/default/collection/meta/runtime.yml +1 -0
- ansible/galaxy/data/default/collection/plugins/README.md.j2 +1 -0
- ansible/galaxy/data/default/role/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/handlers/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/default/role/tests/inventory +1 -0
- ansible/galaxy/data/default/role/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/default/role/vars/main.yml.j2 +1 -0
- ansible/galaxy/data/network/cliconf_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/defaults/main.yml.j2 +1 -0
- ansible/galaxy/data/network/library/example_command.py.j2 +1 -0
- ansible/galaxy/data/network/library/example_config.py.j2 +1 -0
- ansible/galaxy/data/network/library/example_facts.py.j2 +1 -0
- ansible/galaxy/data/network/meta/main.yml.j2 +1 -0
- ansible/galaxy/data/network/module_utils/example.py.j2 +1 -0
- ansible/galaxy/data/network/netconf_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/tasks/main.yml.j2 +1 -0
- ansible/galaxy/data/network/terminal_plugins/example.py.j2 +1 -0
- ansible/galaxy/data/network/tests/inventory +1 -0
- ansible/galaxy/data/network/tests/test.yml.j2 +1 -0
- ansible/galaxy/data/network/vars/main.yml.j2 +1 -0
- ansible/galaxy/dependency_resolution/providers.py +3 -3
- ansible/galaxy/role.py +1 -1
- ansible/galaxy/token.py +20 -8
- ansible/keyword_desc.yml +1 -1
- ansible/module_utils/_internal/__init__.py +0 -0
- ansible/module_utils/_internal/_concurrent/__init__.py +0 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +28 -0
- ansible/module_utils/_internal/_concurrent/_futures.py +21 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +2 -2
- ansible/module_utils/basic.py +14 -11
- ansible/module_utils/common/collections.py +1 -1
- ansible/module_utils/common/file.py +0 -6
- ansible/module_utils/common/process.py +22 -9
- ansible/module_utils/common/text/converters.py +5 -8
- ansible/module_utils/common/text/formatters.py +20 -4
- ansible/module_utils/common/validation.py +33 -25
- ansible/module_utils/compat/paramiko.py +6 -1
- ansible/module_utils/compat/selinux.py +2 -2
- ansible/module_utils/connection.py +8 -24
- ansible/module_utils/csharp/Ansible.Become.cs +14 -25
- ansible/module_utils/csharp/Ansible.Process.cs +1 -1
- ansible/module_utils/distro/__init__.py +1 -1
- ansible/module_utils/distro/_distro.py +8 -4
- ansible/module_utils/facts/collector.py +2 -0
- ansible/module_utils/facts/default_collectors.py +3 -1
- ansible/module_utils/facts/hardware/aix.py +54 -52
- ansible/module_utils/facts/hardware/darwin.py +37 -34
- ansible/module_utils/facts/hardware/freebsd.py +55 -15
- ansible/module_utils/facts/hardware/hpux.py +3 -0
- ansible/module_utils/facts/hardware/linux.py +101 -57
- ansible/module_utils/facts/hardware/netbsd.py +3 -0
- ansible/module_utils/facts/hardware/openbsd.py +4 -1
- ansible/module_utils/facts/hardware/sunos.py +7 -1
- ansible/module_utils/facts/network/aix.py +16 -17
- ansible/module_utils/facts/network/fc_wwn.py +4 -1
- ansible/module_utils/facts/network/hpux.py +21 -4
- ansible/module_utils/facts/network/iscsi.py +7 -8
- ansible/module_utils/facts/network/linux.py +0 -2
- ansible/module_utils/facts/other/facter.py +9 -4
- ansible/module_utils/facts/other/ohai.py +5 -5
- ansible/module_utils/facts/packages.py +49 -7
- ansible/module_utils/facts/sysctl.py +33 -31
- ansible/module_utils/facts/system/distribution.py +1 -1
- ansible/module_utils/facts/system/local.py +12 -22
- ansible/module_utils/facts/system/service_mgr.py +3 -1
- ansible/module_utils/facts/system/systemd.py +47 -0
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/splitter.py +1 -1
- ansible/modules/add_host.py +1 -1
- ansible/modules/apt.py +43 -32
- ansible/modules/apt_key.py +6 -6
- ansible/modules/apt_repository.py +23 -14
- ansible/modules/assemble.py +7 -2
- ansible/modules/assert.py +4 -4
- ansible/modules/blockinfile.py +3 -6
- ansible/modules/command.py +1 -1
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +13 -10
- ansible/modules/deb822_repository.py +16 -17
- ansible/modules/debconf.py +9 -9
- ansible/modules/debug.py +1 -1
- ansible/modules/dnf.py +79 -164
- ansible/modules/dnf5.py +48 -31
- ansible/modules/dpkg_selections.py +2 -2
- ansible/modules/expect.py +2 -2
- ansible/modules/fetch.py +2 -2
- ansible/modules/file.py +5 -3
- ansible/modules/find.py +40 -12
- ansible/modules/gather_facts.py +4 -2
- ansible/modules/get_url.py +29 -24
- ansible/modules/git.py +35 -35
- ansible/modules/group.py +71 -1
- ansible/modules/hostname.py +2 -4
- ansible/modules/include_vars.py +5 -5
- ansible/modules/iptables.py +13 -16
- ansible/modules/known_hosts.py +16 -13
- ansible/modules/lineinfile.py +1 -4
- ansible/modules/meta.py +6 -1
- ansible/modules/mount_facts.py +651 -0
- ansible/modules/package_facts.py +63 -80
- ansible/modules/pause.py +4 -3
- ansible/modules/pip.py +14 -14
- ansible/modules/replace.py +1 -4
- ansible/modules/rpm_key.py +31 -11
- ansible/modules/service.py +8 -8
- ansible/modules/service_facts.py +20 -5
- ansible/modules/set_stats.py +1 -1
- ansible/modules/setup.py +3 -3
- ansible/modules/stat.py +3 -3
- ansible/modules/subversion.py +1 -1
- ansible/modules/systemd.py +16 -10
- ansible/modules/systemd_service.py +16 -10
- ansible/modules/sysvinit.py +4 -4
- ansible/modules/unarchive.py +35 -22
- ansible/modules/uri.py +24 -18
- ansible/modules/user.py +145 -12
- ansible/modules/validate_argument_spec.py +3 -3
- ansible/modules/wait_for_connection.py +2 -1
- ansible/modules/yum_repository.py +136 -179
- ansible/parsing/dataloader.py +2 -2
- ansible/parsing/mod_args.py +11 -10
- ansible/parsing/vault/__init__.py +8 -3
- ansible/parsing/yaml/constructor.py +10 -8
- ansible/parsing/yaml/objects.py +1 -1
- ansible/playbook/base.py +12 -23
- ansible/playbook/helpers.py +4 -0
- ansible/playbook/loop_control.py +8 -0
- ansible/playbook/play.py +4 -22
- ansible/playbook/play_context.py +0 -16
- ansible/playbook/playbook_include.py +2 -2
- ansible/playbook/role/__init__.py +2 -2
- ansible/playbook/task.py +1 -1
- ansible/plugins/__init__.py +2 -0
- ansible/plugins/action/__init__.py +7 -9
- ansible/plugins/action/reboot.py +2 -2
- ansible/plugins/become/__init__.py +1 -1
- ansible/plugins/callback/__init__.py +44 -3
- ansible/plugins/callback/default.py +1 -1
- ansible/plugins/cliconf/__init__.py +1 -1
- ansible/plugins/connection/paramiko_ssh.py +2 -80
- ansible/plugins/connection/psrp.py +33 -82
- ansible/plugins/connection/ssh.py +0 -8
- ansible/plugins/connection/winrm.py +46 -1
- ansible/plugins/doc_fragments/connection_pipelining.py +2 -2
- ansible/plugins/doc_fragments/constructed.py +10 -10
- ansible/plugins/doc_fragments/default_callback.py +8 -8
- ansible/plugins/doc_fragments/files.py +5 -5
- ansible/plugins/doc_fragments/inventory_cache.py +2 -2
- ansible/plugins/doc_fragments/result_format_callback.py +6 -6
- ansible/plugins/doc_fragments/return_common.py +1 -1
- ansible/plugins/doc_fragments/shell_common.py +2 -10
- ansible/plugins/doc_fragments/shell_windows.py +0 -9
- ansible/plugins/doc_fragments/url.py +2 -2
- ansible/plugins/doc_fragments/url_windows.py +4 -5
- ansible/plugins/doc_fragments/validate.py +1 -1
- ansible/plugins/filter/core.py +2 -0
- ansible/plugins/filter/human_to_bytes.yml +9 -0
- ansible/plugins/filter/password_hash.yml +1 -1
- ansible/plugins/filter/strftime.yml +1 -1
- ansible/plugins/filter/to_nice_json.yml +7 -3
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +1 -1
- ansible/plugins/loader.py +0 -11
- ansible/plugins/lookup/config.py +1 -1
- ansible/plugins/lookup/csvfile.py +21 -9
- ansible/plugins/lookup/env.py +8 -9
- ansible/plugins/lookup/ini.py +10 -1
- ansible/plugins/lookup/random_choice.py +2 -2
- ansible/plugins/lookup/url.py +7 -2
- ansible/plugins/shell/__init__.py +15 -20
- ansible/plugins/shell/powershell.py +9 -6
- ansible/plugins/strategy/__init__.py +16 -7
- ansible/plugins/test/core.py +23 -1
- ansible/plugins/test/issubset.yml +1 -1
- ansible/plugins/test/subset.yml +1 -1
- ansible/plugins/test/timedout.yml +20 -0
- ansible/plugins/test/vault_encrypted.yml +6 -6
- ansible/plugins/test/vaulted_file.yml +19 -0
- ansible/release.py +2 -2
- ansible/template/__init__.py +3 -8
- ansible/utils/collection_loader/_collection_finder.py +23 -55
- ansible/utils/display.py +44 -31
- ansible/utils/jsonrpc.py +1 -1
- ansible/utils/listify.py +1 -5
- ansible/utils/path.py +3 -0
- ansible/utils/vars.py +18 -27
- ansible/vars/manager.py +7 -150
- ansible/vars/plugins.py +1 -1
- ansible_core-2.18.0b1.dist-info/Apache-License.txt +202 -0
- {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/METADATA +36 -23
- ansible_core-2.18.0b1.dist-info/MIT-license.txt +14 -0
- ansible_core-2.18.0b1.dist-info/PSF-license.txt +48 -0
- {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/RECORD +311 -306
- {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/WHEEL +1 -1
- {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/entry_points.txt +1 -1
- ansible_core-2.18.0b1.dist-info/simplified_bsd.txt +8 -0
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/remote.txt +5 -4
- ansible_test/_data/completion/windows.txt +4 -4
- ansible_test/_data/requirements/ansible-test.txt +1 -2
- ansible_test/_data/requirements/constraints.txt +1 -2
- ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
- ansible_test/_data/requirements/sanity.changelog.in +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +4 -4
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.import.txt +1 -1
- ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +5 -7
- ansible_test/_data/requirements/sanity.runtime-metadata.txt +2 -2
- ansible_test/_data/requirements/sanity.validate-modules.txt +3 -3
- ansible_test/_data/requirements/sanity.yamllint.in +1 -0
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/ansible_util.py +8 -35
- ansible_test/_internal/ci/azp.py +1 -1
- ansible_test/_internal/classification/__init__.py +0 -2
- ansible_test/_internal/cli/parsers/key_value_parsers.py +3 -0
- ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -1
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/cloud/nios.py +1 -1
- ansible_test/_internal/commands/sanity/__init__.py +96 -19
- ansible_test/_internal/commands/sanity/pylint.py +20 -24
- ansible_test/_internal/completion.py +2 -0
- ansible_test/_internal/constants.py +0 -1
- ansible_test/_internal/coverage_util.py +1 -2
- ansible_test/_internal/docker_util.py +1 -1
- ansible_test/_internal/host_configs.py +10 -0
- ansible_test/_internal/host_profiles.py +9 -13
- ansible_test/_internal/pypi_proxy.py +1 -1
- ansible_test/_internal/python_requirements.py +5 -14
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +40 -0
- ansible_test/_internal/util_common.py +5 -1
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.json +3 -1
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +6 -3
- ansible_test/_util/controller/sanity/code-smell/empty-init.json +0 -2
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -19
- ansible_test/_util/controller/sanity/shellcheck/exclude.txt +1 -0
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +67 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +27 -5
- ansible_test/_util/target/cli/ansible_test_cli_stub.py +0 -0
- ansible_test/_util/target/common/constants.py +2 -2
- ansible_test/_util/target/injector/python.py +5 -0
- ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +6 -0
- ansible_test/_util/target/sanity/import/importer.py +1 -1
- ansible_test/_util/target/setup/bootstrap.sh +6 -17
- ansible_test/_util/target/setup/requirements.py +14 -20
- ansible_test/config/config.yml +1 -1
- ansible_core-2.17.4rc1.data/scripts/ansible-test +0 -44
- ansible_test/_data/requirements/sanity.mypy.in +0 -10
- ansible_test/_data/requirements/sanity.mypy.txt +0 -18
- ansible_test/_internal/commands/sanity/mypy.py +0 -274
- ansible_test/_util/controller/sanity/mypy/ansible-core.ini +0 -116
- ansible_test/_util/controller/sanity/mypy/ansible-test.ini +0 -27
- ansible_test/_util/controller/sanity/mypy/modules.ini +0 -92
- ansible_test/_util/controller/sanity/mypy/packaging.ini +0 -20
- {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/COPYING +0 -0
- {ansible_core-2.17.4rc1.dist-info → ansible_core-2.18.0b1.dist-info}/top_level.txt +0 -0
ansible/constants.py
CHANGED
|
@@ -15,6 +15,10 @@ from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE
|
|
|
15
15
|
from ansible.release import __version__
|
|
16
16
|
from ansible.utils.fqcn import add_internal_fqcns
|
|
17
17
|
|
|
18
|
+
# initialize config manager/config data to read/store global settings
|
|
19
|
+
# and generate 'pseudo constants' for app consumption.
|
|
20
|
+
config = ConfigManager()
|
|
21
|
+
|
|
18
22
|
|
|
19
23
|
def _warning(msg):
|
|
20
24
|
''' display is not guaranteed here, nor it being the full class, but try anyways, fallback to sys.stderr.write '''
|
|
@@ -36,6 +40,28 @@ def _deprecated(msg, version):
|
|
|
36
40
|
sys.stderr.write(' [DEPRECATED] %s, to be removed in %s\n' % (msg, version))
|
|
37
41
|
|
|
38
42
|
|
|
43
|
+
def handle_config_noise(display=None):
|
|
44
|
+
|
|
45
|
+
if display is not None:
|
|
46
|
+
w = display.warning
|
|
47
|
+
d = display.deprecated
|
|
48
|
+
else:
|
|
49
|
+
w = _warning
|
|
50
|
+
d = _deprecated
|
|
51
|
+
|
|
52
|
+
while config.WARNINGS:
|
|
53
|
+
warn = config.WARNINGS.pop()
|
|
54
|
+
w(warn)
|
|
55
|
+
|
|
56
|
+
while config.DEPRECATED:
|
|
57
|
+
# tuple with name and options
|
|
58
|
+
dep = config.DEPRECATED.pop(0)
|
|
59
|
+
msg = config.get_deprecated_msg_from_config(dep[1])
|
|
60
|
+
# use tabs only for ansible-doc?
|
|
61
|
+
msg = msg.replace("\t", "")
|
|
62
|
+
d(f"{dep[0]} option. {msg}", version=dep[1]['version'])
|
|
63
|
+
|
|
64
|
+
|
|
39
65
|
def set_constant(name, value, export=vars()):
|
|
40
66
|
''' sets constants and returns resolved options dict '''
|
|
41
67
|
export[name] = value
|
|
@@ -152,10 +178,10 @@ INTERNAL_STATIC_VARS = frozenset(
|
|
|
152
178
|
]
|
|
153
179
|
)
|
|
154
180
|
LOCALHOST = ('127.0.0.1', 'localhost', '::1')
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
181
|
+
WIN_MOVED = ['ansible.windows.win_command', 'ansible.windows.win_shell']
|
|
182
|
+
MODULE_REQUIRE_ARGS_SIMPLE = ['command', 'raw', 'script', 'shell', 'win_command', 'win_shell']
|
|
183
|
+
MODULE_REQUIRE_ARGS = tuple(add_internal_fqcns(MODULE_REQUIRE_ARGS_SIMPLE) + WIN_MOVED)
|
|
184
|
+
MODULE_NO_JSON = tuple(add_internal_fqcns(('command', 'win_command', 'shell', 'win_shell', 'raw')) + WIN_MOVED)
|
|
159
185
|
RESTRICTED_RESULT_KEYS = ('ansible_rsync_path', 'ansible_playbook_python', 'ansible_facts')
|
|
160
186
|
SYNTHETIC_COLLECTIONS = ('ansible.builtin', 'ansible.legacy')
|
|
161
187
|
TREE_DIR = None
|
|
@@ -218,11 +244,8 @@ MAGIC_VARIABLE_MAPPING = dict(
|
|
|
218
244
|
)
|
|
219
245
|
|
|
220
246
|
# POPULATE SETTINGS FROM CONFIG ###
|
|
221
|
-
config = ConfigManager()
|
|
222
|
-
|
|
223
|
-
# Generate constants from config
|
|
224
247
|
for setting in config.get_configuration_definitions():
|
|
225
248
|
set_constant(setting, config.get_config_value(setting, variables=vars()))
|
|
226
249
|
|
|
227
|
-
|
|
228
|
-
|
|
250
|
+
# emit any warnings or deprecations
|
|
251
|
+
handle_config_noise()
|
ansible/errors/__init__.py
CHANGED
|
@@ -227,6 +227,11 @@ class AnsibleOptionsError(AnsibleError):
|
|
|
227
227
|
pass
|
|
228
228
|
|
|
229
229
|
|
|
230
|
+
class AnsibleRequiredOptionError(AnsibleOptionsError):
|
|
231
|
+
''' bad or incomplete options passed '''
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
|
|
230
235
|
class AnsibleParserError(AnsibleError):
|
|
231
236
|
''' something was detected early that is wrong about a playbook or data file '''
|
|
232
237
|
pass
|
|
@@ -41,7 +41,7 @@ class InterpreterDiscoveryRequiredError(Exception):
|
|
|
41
41
|
def discover_interpreter(action, interpreter_name, discovery_mode, task_vars):
|
|
42
42
|
# interpreter discovery is a 2-step process with the target. First, we use a simple shell-agnostic bootstrap to
|
|
43
43
|
# get the system type from uname, and find any random Python that can get us the info we need. For supported
|
|
44
|
-
# target OS types, we'll dispatch a Python script that calls
|
|
44
|
+
# target OS types, we'll dispatch a Python script that calls platform.dist() (for older platforms, where available)
|
|
45
45
|
# and brings back /etc/os-release (if present). The proper Python path is looked up in a table of known
|
|
46
46
|
# distros/versions with included Pythons; if nothing is found, depending on the discovery mode, either the
|
|
47
47
|
# default fallback of /usr/bin/python is used (if we know it's there), or discovery fails.
|
|
@@ -635,3 +635,19 @@ class PlayIterator:
|
|
|
635
635
|
|
|
636
636
|
def clear_notification(self, hostname: str, notification: str) -> None:
|
|
637
637
|
self._host_states[hostname].handler_notifications.remove(notification)
|
|
638
|
+
|
|
639
|
+
def end_host(self, hostname: str) -> None:
|
|
640
|
+
"""Used by ``end_host``, ``end_batch`` and ``end_play`` meta tasks to end executing given host."""
|
|
641
|
+
state = self.get_active_state(self.get_state_for_host(hostname))
|
|
642
|
+
if state.run_state == IteratingStates.RESCUE:
|
|
643
|
+
# This is a special case for when ending a host occurs in rescue.
|
|
644
|
+
# By definition the meta task responsible for ending the host
|
|
645
|
+
# is the last task, so we need to clear the fail state to mark
|
|
646
|
+
# the host as rescued.
|
|
647
|
+
# The reason we need to do that is because this operation is
|
|
648
|
+
# normally done when PlayIterator transitions from rescue to
|
|
649
|
+
# always when only then we can say that rescue didn't fail
|
|
650
|
+
# but with ending a host via meta task, we don't get to that transition.
|
|
651
|
+
self.set_fail_state_for_host(hostname, FailedStates.NONE)
|
|
652
|
+
self.set_run_state_for_host(hostname, IteratingStates.COMPLETE)
|
|
653
|
+
self._play._removed_hosts.append(hostname)
|
|
@@ -195,10 +195,7 @@ class PlaybookExecutor:
|
|
|
195
195
|
result = self._tqm.RUN_FAILED_HOSTS
|
|
196
196
|
break_play = True
|
|
197
197
|
|
|
198
|
-
# check the number of failures here
|
|
199
|
-
# failure percentage allowed, or if any errors are fatal. If either of those
|
|
200
|
-
# conditions are met, we break out, otherwise we only break out if the entire
|
|
201
|
-
# batch failed
|
|
198
|
+
# check the number of failures here and break out if the entire batch failed
|
|
202
199
|
failed_hosts_count = len(self._tqm._failed_hosts) + len(self._tqm._unreachable_hosts) - \
|
|
203
200
|
(previously_failed + previously_unreachable)
|
|
204
201
|
|
|
@@ -116,12 +116,11 @@ Write-AnsibleLog "INFO - parsed become input, user: '$username', type: '$logon_t
|
|
|
116
116
|
# set to Stop and cannot be changed. Also need to split the payload from the wrapper to prevent potentially
|
|
117
117
|
# sensitive content from being logged by the scriptblock logger.
|
|
118
118
|
$bootstrap_wrapper = {
|
|
119
|
-
|
|
120
|
-
$
|
|
121
|
-
$split_parts = $
|
|
119
|
+
[Console]::InputEncoding = [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
|
|
120
|
+
$ew = [System.Console]::In.ReadToEnd()
|
|
121
|
+
$split_parts = $ew.Split(@("`0`0`0`0"), 2, [StringSplitOptions]::RemoveEmptyEntries)
|
|
122
122
|
Set-Variable -Name json_raw -Value $split_parts[1]
|
|
123
|
-
|
|
124
|
-
&$exec_wrapper
|
|
123
|
+
&([ScriptBlock]::Create($split_parts[0]))
|
|
125
124
|
}
|
|
126
125
|
$exec_command = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($bootstrap_wrapper.ToString()))
|
|
127
126
|
$lp_command_line = "powershell.exe -NonInteractive -NoProfile -ExecutionPolicy Bypass -EncodedCommand $exec_command"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
try { [Console]::InputEncoding = [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding } catch { $null = $_ }
|
|
2
2
|
|
|
3
3
|
if ($PSVersionTable.PSVersion -lt [Version]"3.0") {
|
|
4
4
|
'{"failed":true,"msg":"Ansible requires PowerShell v3.0 or newer"}'
|
|
@@ -9,5 +9,4 @@ $exec_wrapper_str = $input | Out-String
|
|
|
9
9
|
$split_parts = $exec_wrapper_str.Split(@("`0`0`0`0"), 2, [StringSplitOptions]::RemoveEmptyEntries)
|
|
10
10
|
If (-not $split_parts.Length -eq 2) { throw "invalid payload" }
|
|
11
11
|
Set-Variable -Name json_raw -Value $split_parts[1]
|
|
12
|
-
|
|
13
|
-
&$exec_wrapper
|
|
12
|
+
& ([ScriptBlock]::Create($split_parts[0]))
|
|
@@ -16,7 +16,7 @@ begin {
|
|
|
16
16
|
.SYNOPSIS
|
|
17
17
|
Converts a JSON string to a Hashtable/Array in the fastest way
|
|
18
18
|
possible. Unfortunately ConvertFrom-Json is still faster but outputs
|
|
19
|
-
a PSCustomObject which is
|
|
19
|
+
a PSCustomObject which is cumbersome for module consumption.
|
|
20
20
|
|
|
21
21
|
.PARAMETER InputObject
|
|
22
22
|
[String] The JSON string to deserialize.
|
|
@@ -8,7 +8,7 @@ import errno
|
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
10
|
import pkgutil
|
|
11
|
-
import
|
|
11
|
+
import secrets
|
|
12
12
|
import re
|
|
13
13
|
from importlib import import_module
|
|
14
14
|
|
|
@@ -318,7 +318,7 @@ def _create_powershell_wrapper(b_module_data, module_path, module_args,
|
|
|
318
318
|
|
|
319
319
|
exec_manifest["actions"].insert(0, 'async_watchdog')
|
|
320
320
|
exec_manifest["actions"].insert(0, 'async_wrapper')
|
|
321
|
-
exec_manifest["async_jid"] = f'j{
|
|
321
|
+
exec_manifest["async_jid"] = f'j{secrets.randbelow(999999999999)}'
|
|
322
322
|
exec_manifest["async_timeout_sec"] = async_timeout
|
|
323
323
|
exec_manifest["async_startup_timeout"] = C.config.get_config_value("WIN_ASYNC_STARTUP_TIMEOUT", variables=task_vars)
|
|
324
324
|
|
|
@@ -4,23 +4,24 @@
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
|
-
import pty
|
|
8
7
|
import time
|
|
9
8
|
import json
|
|
9
|
+
import pathlib
|
|
10
10
|
import signal
|
|
11
11
|
import subprocess
|
|
12
12
|
import sys
|
|
13
|
-
import termios
|
|
14
13
|
import traceback
|
|
15
14
|
|
|
16
15
|
from ansible import constants as C
|
|
16
|
+
from ansible.cli import scripts
|
|
17
17
|
from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleConnectionFailure, AnsibleActionFail, AnsibleActionSkip
|
|
18
18
|
from ansible.executor.task_result import TaskResult
|
|
19
19
|
from ansible.executor.module_common import get_action_args_with_defaults
|
|
20
20
|
from ansible.module_utils.parsing.convert_bool import boolean
|
|
21
21
|
from ansible.module_utils.six import binary_type
|
|
22
22
|
from ansible.module_utils.common.text.converters import to_text, to_native
|
|
23
|
-
from ansible.module_utils.connection import
|
|
23
|
+
from ansible.module_utils.connection import write_to_stream
|
|
24
|
+
from ansible.module_utils.six import string_types
|
|
24
25
|
from ansible.playbook.conditional import Conditional
|
|
25
26
|
from ansible.playbook.task import Task
|
|
26
27
|
from ansible.plugins import get_plugin_class
|
|
@@ -42,11 +43,21 @@ __all__ = ['TaskExecutor']
|
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
class TaskTimeoutError(BaseException):
|
|
45
|
-
|
|
46
|
+
def __init__(self, message="", frame=None):
|
|
47
|
+
|
|
48
|
+
if frame is not None:
|
|
49
|
+
orig = frame
|
|
50
|
+
root = pathlib.Path(__file__).parent
|
|
51
|
+
while not pathlib.Path(frame.f_code.co_filename).is_relative_to(root):
|
|
52
|
+
frame = frame.f_back
|
|
53
|
+
|
|
54
|
+
self.frame = 'Interrupted at %s called from %s' % (orig, frame)
|
|
55
|
+
|
|
56
|
+
super(TaskTimeoutError, self).__init__(message)
|
|
46
57
|
|
|
47
58
|
|
|
48
59
|
def task_timeout(signum, frame):
|
|
49
|
-
raise TaskTimeoutError
|
|
60
|
+
raise TaskTimeoutError(frame=frame)
|
|
50
61
|
|
|
51
62
|
|
|
52
63
|
def remove_omit(task_args, omit_token):
|
|
@@ -369,12 +380,17 @@ class TaskExecutor:
|
|
|
369
380
|
'msg': 'Failed to template loop_control.label: %s' % to_text(e)
|
|
370
381
|
})
|
|
371
382
|
|
|
383
|
+
# if plugin is loaded, get resolved name, otherwise leave original task connection
|
|
384
|
+
if self._connection and not isinstance(self._connection, string_types):
|
|
385
|
+
task_fields['connection'] = getattr(self._connection, 'ansible_name')
|
|
386
|
+
|
|
372
387
|
tr = TaskResult(
|
|
373
388
|
self._host.name,
|
|
374
389
|
self._task._uuid,
|
|
375
390
|
res,
|
|
376
391
|
task_fields=task_fields,
|
|
377
392
|
)
|
|
393
|
+
|
|
378
394
|
if tr.is_failed() or tr.is_unreachable():
|
|
379
395
|
self._final_q.send_callback('v2_runner_item_on_failed', tr)
|
|
380
396
|
elif tr.is_skipped():
|
|
@@ -386,6 +402,19 @@ class TaskExecutor:
|
|
|
386
402
|
self._final_q.send_callback('v2_runner_item_on_ok', tr)
|
|
387
403
|
|
|
388
404
|
results.append(res)
|
|
405
|
+
|
|
406
|
+
# break loop if break_when conditions are met
|
|
407
|
+
if self._task.loop_control and self._task.loop_control.break_when:
|
|
408
|
+
cond = Conditional(loader=self._loader)
|
|
409
|
+
cond.when = self._task.loop_control.get_validated_value(
|
|
410
|
+
'break_when', self._task.loop_control.fattributes.get('break_when'), self._task.loop_control.break_when, templar
|
|
411
|
+
)
|
|
412
|
+
if cond.evaluate_conditional(templar, task_vars):
|
|
413
|
+
# delete loop vars before exiting loop
|
|
414
|
+
del task_vars[loop_var]
|
|
415
|
+
break
|
|
416
|
+
|
|
417
|
+
# done with loop var, remove for next iteration
|
|
389
418
|
del task_vars[loop_var]
|
|
390
419
|
|
|
391
420
|
# clear 'connection related' plugin variables for next iteration
|
|
@@ -647,7 +676,7 @@ class TaskExecutor:
|
|
|
647
676
|
return dict(unreachable=True, msg=to_text(e))
|
|
648
677
|
except TaskTimeoutError as e:
|
|
649
678
|
msg = 'The %s action failed to execute in the expected time frame (%d) and was terminated' % (self._task.action, self._task.timeout)
|
|
650
|
-
return dict(failed=True, msg=msg)
|
|
679
|
+
return dict(failed=True, msg=msg, timedout={'frame': e.frame, 'period': self._task.timeout})
|
|
651
680
|
finally:
|
|
652
681
|
if self._task.timeout:
|
|
653
682
|
signal.alarm(0)
|
|
@@ -1183,26 +1212,19 @@ class TaskExecutor:
|
|
|
1183
1212
|
return handler, module
|
|
1184
1213
|
|
|
1185
1214
|
|
|
1215
|
+
CLI_STUB_NAME = 'ansible_connection_cli_stub.py'
|
|
1216
|
+
|
|
1217
|
+
|
|
1186
1218
|
def start_connection(play_context, options, task_uuid):
|
|
1187
1219
|
'''
|
|
1188
1220
|
Starts the persistent connection
|
|
1189
1221
|
'''
|
|
1190
|
-
candidate_paths = [C.ANSIBLE_CONNECTION_PATH or os.path.dirname(sys.argv[0])]
|
|
1191
|
-
candidate_paths.extend(os.environ.get('PATH', '').split(os.pathsep))
|
|
1192
|
-
for dirname in candidate_paths:
|
|
1193
|
-
ansible_connection = os.path.join(dirname, 'ansible-connection')
|
|
1194
|
-
if os.path.isfile(ansible_connection):
|
|
1195
|
-
display.vvvv("Found ansible-connection at path {0}".format(ansible_connection))
|
|
1196
|
-
break
|
|
1197
|
-
else:
|
|
1198
|
-
raise AnsibleError("Unable to find location of 'ansible-connection'. "
|
|
1199
|
-
"Please set or check the value of ANSIBLE_CONNECTION_PATH")
|
|
1200
1222
|
|
|
1201
1223
|
env = os.environ.copy()
|
|
1202
1224
|
env.update({
|
|
1203
1225
|
# HACK; most of these paths may change during the controller's lifetime
|
|
1204
1226
|
# (eg, due to late dynamic role includes, multi-playbook execution), without a way
|
|
1205
|
-
# to invalidate/update,
|
|
1227
|
+
# to invalidate/update, the persistent connection helper won't always see the same plugins the controller
|
|
1206
1228
|
# can.
|
|
1207
1229
|
'ANSIBLE_BECOME_PLUGINS': become_loader.print_paths(),
|
|
1208
1230
|
'ANSIBLE_CLICONF_PLUGINS': cliconf_loader.print_paths(),
|
|
@@ -1215,30 +1237,19 @@ def start_connection(play_context, options, task_uuid):
|
|
|
1215
1237
|
verbosity = []
|
|
1216
1238
|
if display.verbosity:
|
|
1217
1239
|
verbosity.append('-%s' % ('v' * display.verbosity))
|
|
1218
|
-
|
|
1219
|
-
|
|
1240
|
+
|
|
1241
|
+
if not (cli_stub_path := C.config.get_config_value('_ANSIBLE_CONNECTION_PATH')):
|
|
1242
|
+
cli_stub_path = str(pathlib.Path(scripts.__file__).parent / CLI_STUB_NAME)
|
|
1243
|
+
|
|
1220
1244
|
p = subprocess.Popen(
|
|
1221
|
-
[
|
|
1222
|
-
stdin=
|
|
1245
|
+
[sys.executable, cli_stub_path, *verbosity, to_text(os.getppid()), to_text(task_uuid)],
|
|
1246
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env,
|
|
1223
1247
|
)
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
old = termios.tcgetattr(master)
|
|
1230
|
-
new = termios.tcgetattr(master)
|
|
1231
|
-
new[3] = new[3] & ~termios.ICANON
|
|
1232
|
-
|
|
1233
|
-
try:
|
|
1234
|
-
termios.tcsetattr(master, termios.TCSANOW, new)
|
|
1235
|
-
write_to_file_descriptor(master, options)
|
|
1236
|
-
write_to_file_descriptor(master, play_context.serialize())
|
|
1237
|
-
|
|
1238
|
-
(stdout, stderr) = p.communicate()
|
|
1239
|
-
finally:
|
|
1240
|
-
termios.tcsetattr(master, termios.TCSANOW, old)
|
|
1241
|
-
os.close(master)
|
|
1248
|
+
|
|
1249
|
+
write_to_stream(p.stdin, options)
|
|
1250
|
+
write_to_stream(p.stdin, play_context.serialize())
|
|
1251
|
+
|
|
1252
|
+
(stdout, stderr) = p.communicate()
|
|
1242
1253
|
|
|
1243
1254
|
if p.returncode == 0:
|
|
1244
1255
|
result = json.loads(to_text(stdout, errors='surrogate_then_replace'))
|
|
@@ -223,7 +223,7 @@ class TaskQueueManager:
|
|
|
223
223
|
callback_type = getattr(callback_plugin, 'CALLBACK_TYPE', '')
|
|
224
224
|
callback_needs_enabled = getattr(callback_plugin, 'CALLBACK_NEEDS_ENABLED', getattr(callback_plugin, 'CALLBACK_NEEDS_WHITELIST', False))
|
|
225
225
|
|
|
226
|
-
# try to get
|
|
226
|
+
# try to get collection world name first
|
|
227
227
|
cnames = getattr(callback_plugin, '_redirected_names', [])
|
|
228
228
|
if cnames:
|
|
229
229
|
# store the name the plugin was loaded as, as that's what we'll need to compare to the configured callback list later
|
ansible/executor/task_result.py
CHANGED
ansible/galaxy/api.py
CHANGED
|
@@ -62,8 +62,7 @@ def should_retry_error(exception):
|
|
|
62
62
|
if isinstance(orig_exc, URLError):
|
|
63
63
|
orig_exc = orig_exc.reason
|
|
64
64
|
|
|
65
|
-
# Handle common URL related errors
|
|
66
|
-
# Note: socket.timeout is only required for Py3.9
|
|
65
|
+
# Handle common URL related errors
|
|
67
66
|
if isinstance(orig_exc, (TimeoutError, BadStatusLine, IncompleteRead)):
|
|
68
67
|
return True
|
|
69
68
|
|
|
@@ -720,7 +719,7 @@ class GalaxyAPI:
|
|
|
720
719
|
|
|
721
720
|
display.display("Waiting until Galaxy import task %s has completed" % full_url)
|
|
722
721
|
start = time.time()
|
|
723
|
-
wait =
|
|
722
|
+
wait = C.GALAXY_COLLECTION_IMPORT_POLL_INTERVAL
|
|
724
723
|
|
|
725
724
|
while timeout == 0 or (time.time() - start) < timeout:
|
|
726
725
|
try:
|
|
@@ -744,7 +743,7 @@ class GalaxyAPI:
|
|
|
744
743
|
time.sleep(wait)
|
|
745
744
|
|
|
746
745
|
# poor man's exponential backoff algo so we don't flood the Galaxy API, cap at 30 seconds.
|
|
747
|
-
wait = min(30, wait *
|
|
746
|
+
wait = min(30, wait * C.GALAXY_COLLECTION_IMPORT_POLL_FACTOR)
|
|
748
747
|
if state == 'waiting':
|
|
749
748
|
raise AnsibleError("Timeout while waiting for the Galaxy import process to finish, check progress at '%s'"
|
|
750
749
|
% to_native(full_url))
|
|
@@ -8,6 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
import errno
|
|
9
9
|
import fnmatch
|
|
10
10
|
import functools
|
|
11
|
+
import glob
|
|
11
12
|
import inspect
|
|
12
13
|
import json
|
|
13
14
|
import os
|
|
@@ -1525,6 +1526,7 @@ def install(collection, path, artifacts_manager): # FIXME: mv to dataclasses?
|
|
|
1525
1526
|
artifacts_manager.required_successful_signature_count,
|
|
1526
1527
|
artifacts_manager.ignore_signature_errors,
|
|
1527
1528
|
)
|
|
1529
|
+
remove_source_metadata(collection, b_collection_path)
|
|
1528
1530
|
if (collection.is_online_index_pointer and isinstance(collection.src, GalaxyAPI)):
|
|
1529
1531
|
write_source_metadata(
|
|
1530
1532
|
collection,
|
|
@@ -1561,6 +1563,22 @@ def write_source_metadata(collection, b_collection_path, artifacts_manager):
|
|
|
1561
1563
|
raise
|
|
1562
1564
|
|
|
1563
1565
|
|
|
1566
|
+
def remove_source_metadata(collection, b_collection_path):
|
|
1567
|
+
pattern = f"{collection.namespace}.{collection.name}-*.info"
|
|
1568
|
+
info_path = os.path.join(
|
|
1569
|
+
b_collection_path,
|
|
1570
|
+
b'../../',
|
|
1571
|
+
to_bytes(pattern, errors='surrogate_or_strict')
|
|
1572
|
+
)
|
|
1573
|
+
if (outdated_info := glob.glob(info_path)):
|
|
1574
|
+
display.vvvv(f"Removing {pattern} metadata from previous installations")
|
|
1575
|
+
for info_dir in outdated_info:
|
|
1576
|
+
try:
|
|
1577
|
+
shutil.rmtree(info_dir)
|
|
1578
|
+
except Exception:
|
|
1579
|
+
pass
|
|
1580
|
+
|
|
1581
|
+
|
|
1564
1582
|
def verify_artifact_manifest(manifest_file, signatures, keyring, required_signature_count, ignore_signature_errors):
|
|
1565
1583
|
# type: (str, list[str], str, str, list[str]) -> None
|
|
1566
1584
|
failed_verify = False
|
|
@@ -1584,13 +1602,6 @@ def install_artifact(b_coll_targz_path, b_collection_path, b_temp_path, signatur
|
|
|
1584
1602
|
"""
|
|
1585
1603
|
try:
|
|
1586
1604
|
with tarfile.open(b_coll_targz_path, mode='r') as collection_tar:
|
|
1587
|
-
# Remove this once py3.11 is our controller minimum
|
|
1588
|
-
# Workaround for https://bugs.python.org/issue47231
|
|
1589
|
-
# See _extract_tar_dir
|
|
1590
|
-
collection_tar._ansible_normalized_cache = {
|
|
1591
|
-
m.name.removesuffix(os.path.sep): m for m in collection_tar.getmembers()
|
|
1592
|
-
} # deprecated: description='TarFile member index' core_version='2.18' python_version='3.11'
|
|
1593
|
-
|
|
1594
1605
|
# Verify the signature on the MANIFEST.json before extracting anything else
|
|
1595
1606
|
_extract_tar_file(collection_tar, MANIFEST_FILENAME, b_collection_path, b_temp_path)
|
|
1596
1607
|
|
|
@@ -1671,10 +1682,10 @@ def install_src(collection, b_collection_path, b_collection_output_path, artifac
|
|
|
1671
1682
|
|
|
1672
1683
|
def _extract_tar_dir(tar, dirname, b_dest):
|
|
1673
1684
|
""" Extracts a directory from a collection tar. """
|
|
1674
|
-
dirname = to_native(dirname, errors='surrogate_or_strict')
|
|
1685
|
+
dirname = to_native(dirname, errors='surrogate_or_strict')
|
|
1675
1686
|
|
|
1676
1687
|
try:
|
|
1677
|
-
tar_member = tar.
|
|
1688
|
+
tar_member = tar.getmember(dirname)
|
|
1678
1689
|
except KeyError:
|
|
1679
1690
|
raise AnsibleError("Unable to extract '%s' from collection" % dirname)
|
|
1680
1691
|
|
|
@@ -1896,7 +1907,7 @@ def _resolve_depenency_map(
|
|
|
1896
1907
|
|
|
1897
1908
|
for req in dep_exc.criterion.iter_requirement():
|
|
1898
1909
|
error_msg_lines.append(
|
|
1899
|
-
'* {req.fqcn!s}:{req.ver!s}'
|
|
1910
|
+
f'* {req.fqcn!s}:{req.ver!s}'
|
|
1900
1911
|
)
|
|
1901
1912
|
error_msg_lines.append(pre_release_hint)
|
|
1902
1913
|
|
|
@@ -61,7 +61,7 @@ class ConcreteArtifactsManager:
|
|
|
61
61
|
"""
|
|
62
62
|
def __init__(self, b_working_directory, validate_certs=True, keyring=None, timeout=60, required_signature_count=None, ignore_signature_errors=None):
|
|
63
63
|
# type: (bytes, bool, str, int, str, list[str]) -> None
|
|
64
|
-
"""Initialize ConcreteArtifactsManager caches and
|
|
64
|
+
"""Initialize ConcreteArtifactsManager caches and constraints."""
|
|
65
65
|
self._validate_certs = validate_certs # type: bool
|
|
66
66
|
self._artifact_cache = {} # type: dict[bytes, bytes]
|
|
67
67
|
self._galaxy_artifact_cache = {} # type: dict[Candidate | Requirement, bytes]
|
|
@@ -413,7 +413,7 @@ def _extract_collection_from_git(repo_url, coll_ver, b_path):
|
|
|
413
413
|
b_checkout_path = mkdtemp(
|
|
414
414
|
dir=b_path,
|
|
415
415
|
prefix=to_bytes(name, errors='surrogate_or_strict'),
|
|
416
|
-
)
|
|
416
|
+
)
|
|
417
417
|
|
|
418
418
|
try:
|
|
419
419
|
git_executable = get_bin_path('git')
|
|
@@ -27,8 +27,7 @@ display = Display()
|
|
|
27
27
|
class MultiGalaxyAPIProxy:
|
|
28
28
|
"""A proxy that abstracts talking to multiple Galaxy instances."""
|
|
29
29
|
|
|
30
|
-
def __init__(self, apis, concrete_artifacts_manager, offline=False):
|
|
31
|
-
# type: (t.Iterable[GalaxyAPI], ConcreteArtifactsManager, bool) -> None
|
|
30
|
+
def __init__(self, apis: t.Iterable[GalaxyAPI], concrete_artifacts_manager: ConcreteArtifactsManager, offline: bool = False) -> None:
|
|
32
31
|
"""Initialize the target APIs list."""
|
|
33
32
|
self._apis = apis
|
|
34
33
|
self._concrete_art_mgr = concrete_artifacts_manager
|
|
@@ -38,22 +37,21 @@ class MultiGalaxyAPIProxy:
|
|
|
38
37
|
def is_offline_mode_requested(self):
|
|
39
38
|
return self._offline
|
|
40
39
|
|
|
41
|
-
def _assert_that_offline_mode_is_not_requested(self)
|
|
40
|
+
def _assert_that_offline_mode_is_not_requested(self) -> None:
|
|
42
41
|
if self.is_offline_mode_requested:
|
|
43
42
|
raise NotImplementedError("The calling code is not supposed to be invoked in 'offline' mode.")
|
|
44
43
|
|
|
45
|
-
def _get_collection_versions(self, requirement):
|
|
46
|
-
# type: (Requirement) -> t.Iterator[tuple[GalaxyAPI, str]]
|
|
44
|
+
def _get_collection_versions(self, requirement: Requirement) -> t.Iterator[tuple[GalaxyAPI, str]]:
|
|
47
45
|
"""Helper for get_collection_versions.
|
|
48
46
|
|
|
49
47
|
Yield api, version pairs for all APIs,
|
|
50
48
|
and reraise the last error if no valid API was found.
|
|
51
49
|
"""
|
|
52
50
|
if self._offline:
|
|
53
|
-
return
|
|
51
|
+
return
|
|
54
52
|
|
|
55
53
|
found_api = False
|
|
56
|
-
last_error
|
|
54
|
+
last_error: Exception | None = None
|
|
57
55
|
|
|
58
56
|
api_lookup_order = (
|
|
59
57
|
(requirement.src, )
|
|
@@ -86,8 +84,7 @@ class MultiGalaxyAPIProxy:
|
|
|
86
84
|
if not found_api and last_error is not None:
|
|
87
85
|
raise last_error
|
|
88
86
|
|
|
89
|
-
def get_collection_versions(self, requirement):
|
|
90
|
-
# type: (Requirement) -> t.Iterable[tuple[str, GalaxyAPI]]
|
|
87
|
+
def get_collection_versions(self, requirement: Requirement) -> t.Iterable[tuple[str, GalaxyAPI]]:
|
|
91
88
|
"""Get a set of unique versions for FQCN on Galaxy servers."""
|
|
92
89
|
if requirement.is_concrete_artifact:
|
|
93
90
|
return {
|
|
@@ -110,8 +107,7 @@ class MultiGalaxyAPIProxy:
|
|
|
110
107
|
)
|
|
111
108
|
)
|
|
112
109
|
|
|
113
|
-
def get_collection_version_metadata(self, collection_candidate):
|
|
114
|
-
# type: (Candidate) -> CollectionVersionMetadata
|
|
110
|
+
def get_collection_version_metadata(self, collection_candidate: Candidate) -> CollectionVersionMetadata:
|
|
115
111
|
"""Retrieve collection metadata of a given candidate."""
|
|
116
112
|
self._assert_that_offline_mode_is_not_requested()
|
|
117
113
|
|
|
@@ -160,8 +156,7 @@ class MultiGalaxyAPIProxy:
|
|
|
160
156
|
|
|
161
157
|
raise last_err
|
|
162
158
|
|
|
163
|
-
def get_collection_dependencies(self, collection_candidate):
|
|
164
|
-
# type: (Candidate) -> dict[str, str]
|
|
159
|
+
def get_collection_dependencies(self, collection_candidate: Candidate) -> dict[str, str]:
|
|
165
160
|
# FIXME: return Requirement instances instead?
|
|
166
161
|
"""Retrieve collection dependencies of a given candidate."""
|
|
167
162
|
if collection_candidate.is_concrete_artifact:
|
|
@@ -177,13 +172,12 @@ class MultiGalaxyAPIProxy:
|
|
|
177
172
|
dependencies
|
|
178
173
|
)
|
|
179
174
|
|
|
180
|
-
def get_signatures(self, collection_candidate):
|
|
181
|
-
# type: (Candidate) -> list[str]
|
|
175
|
+
def get_signatures(self, collection_candidate: Candidate) -> list[str]:
|
|
182
176
|
self._assert_that_offline_mode_is_not_requested()
|
|
183
177
|
namespace = collection_candidate.namespace
|
|
184
178
|
name = collection_candidate.name
|
|
185
179
|
version = collection_candidate.ver
|
|
186
|
-
last_err
|
|
180
|
+
last_err: Exception | None = None
|
|
187
181
|
|
|
188
182
|
api_lookup_order = (
|
|
189
183
|
(collection_candidate.src, )
|