ansible-core 2.18.7rc1__py3-none-any.whl → 2.19.0__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/_internal/__init__.py +53 -0
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/_ansiballz/_wrapper.py +262 -0
- ansible/_internal/_collection_proxy.py +47 -0
- ansible/_internal/_datatag/__init__.py +0 -0
- ansible/_internal/_datatag/_tags.py +130 -0
- ansible/_internal/_datatag/_utils.py +19 -0
- ansible/_internal/_datatag/_wrappers.py +33 -0
- ansible/_internal/_errors/__init__.py +0 -0
- ansible/_internal/_errors/_alarm_timeout.py +66 -0
- ansible/_internal/_errors/_captured.py +123 -0
- ansible/_internal/_errors/_error_factory.py +89 -0
- ansible/_internal/_errors/_error_utils.py +240 -0
- ansible/_internal/_errors/_handler.py +91 -0
- ansible/_internal/_errors/_task_timeout.py +28 -0
- ansible/_internal/_event_formatting.py +127 -0
- ansible/_internal/_json/__init__.py +214 -0
- ansible/_internal/_json/_legacy_encoder.py +34 -0
- ansible/_internal/_json/_profiles/__init__.py +0 -0
- ansible/_internal/_json/_profiles/_cache_persistence.py +57 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
- ansible/_internal/_json/_profiles/_legacy.py +189 -0
- ansible/_internal/_locking.py +21 -0
- ansible/_internal/_plugins/__init__.py +0 -0
- ansible/_internal/_plugins/_cache.py +57 -0
- ansible/_internal/_ssh/__init__.py +0 -0
- ansible/_internal/_ssh/_agent_launch.py +91 -0
- ansible/_internal/_ssh/_ssh_agent.py +619 -0
- ansible/_internal/_task.py +78 -0
- ansible/_internal/_templating/__init__.py +12 -0
- ansible/_internal/_templating/_access.py +86 -0
- ansible/_internal/_templating/_chain_templar.py +63 -0
- ansible/_internal/_templating/_datatag.py +95 -0
- ansible/_internal/_templating/_engine.py +592 -0
- ansible/_internal/_templating/_errors.py +28 -0
- ansible/_internal/_templating/_jinja_bits.py +1106 -0
- ansible/_internal/_templating/_jinja_common.py +323 -0
- ansible/_internal/_templating/_jinja_patches.py +44 -0
- ansible/_internal/_templating/_jinja_plugins.py +375 -0
- ansible/_internal/_templating/_lazy_containers.py +633 -0
- ansible/_internal/_templating/_marker_behaviors.py +103 -0
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +70 -0
- ansible/_internal/_templating/_utils.py +108 -0
- ansible/_internal/_testing.py +26 -0
- ansible/_internal/_wrapt.py +1052 -0
- ansible/_internal/_yaml/__init__.py +0 -0
- ansible/_internal/_yaml/_constructor.py +240 -0
- ansible/_internal/_yaml/_dumper.py +70 -0
- ansible/_internal/_yaml/_errors.py +166 -0
- ansible/_internal/_yaml/_loader.py +66 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +27 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
- ansible/cli/__init__.py +93 -104
- ansible/cli/_ssh_askpass.py +54 -0
- ansible/cli/adhoc.py +20 -10
- ansible/cli/arguments/option_helpers.py +163 -10
- ansible/cli/config.py +43 -68
- ansible/cli/console.py +13 -11
- ansible/cli/doc.py +134 -77
- ansible/cli/galaxy.py +27 -20
- ansible/cli/inventory.py +28 -28
- ansible/cli/playbook.py +4 -12
- ansible/cli/pull.py +6 -3
- ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
- ansible/cli/vault.py +12 -11
- ansible/compat/__init__.py +2 -2
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +218 -133
- ansible/config/manager.py +220 -159
- ansible/constants.py +2 -65
- ansible/errors/__init__.py +350 -256
- ansible/executor/interpreter_discovery.py +28 -149
- ansible/executor/module_common.py +480 -514
- ansible/executor/play_iterator.py +22 -27
- ansible/executor/playbook_executor.py +11 -11
- ansible/executor/powershell/async_watchdog.ps1 +97 -102
- ansible/executor/powershell/async_wrapper.ps1 +204 -153
- ansible/executor/powershell/become_wrapper.ps1 +107 -144
- ansible/executor/powershell/bootstrap_wrapper.ps1 +46 -9
- ansible/executor/powershell/coverage_wrapper.ps1 +91 -135
- ansible/executor/powershell/exec_wrapper.ps1 +675 -196
- ansible/executor/powershell/module_manifest.py +469 -265
- ansible/executor/powershell/module_wrapper.ps1 +195 -186
- ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
- ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
- ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
- ansible/executor/powershell/psrp_put_file.ps1 +122 -0
- ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
- ansible/executor/powershell/winrm_put_file.ps1 +36 -0
- ansible/executor/process/worker.py +139 -149
- ansible/executor/stats.py +5 -5
- ansible/executor/task_executor.py +270 -297
- ansible/executor/task_queue_manager.py +135 -137
- ansible/executor/task_result.py +182 -79
- ansible/galaxy/__init__.py +2 -2
- ansible/galaxy/api.py +26 -25
- ansible/galaxy/collection/__init__.py +6 -14
- ansible/galaxy/collection/concrete_artifact_manager.py +12 -21
- ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
- ansible/galaxy/dependency_resolution/providers.py +4 -4
- ansible/galaxy/dependency_resolution/reporters.py +81 -0
- ansible/galaxy/role.py +6 -10
- ansible/galaxy/token.py +28 -21
- ansible/inventory/data.py +47 -57
- ansible/inventory/group.py +50 -73
- ansible/inventory/helpers.py +9 -0
- ansible/inventory/host.py +37 -54
- ansible/inventory/manager.py +79 -34
- ansible/keyword_desc.yml +1 -1
- ansible/module_utils/_internal/__init__.py +55 -0
- ansible/module_utils/_internal/_ambient_context.py +58 -0
- ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
- ansible/module_utils/_internal/_ansiballz/_loader.py +81 -0
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
- ansible/module_utils/_internal/_dataclass_validation.py +217 -0
- ansible/module_utils/_internal/_datatag/__init__.py +961 -0
- ansible/module_utils/_internal/_datatag/_tags.py +16 -0
- ansible/module_utils/_internal/_debugging.py +31 -0
- ansible/module_utils/_internal/_deprecator.py +157 -0
- ansible/module_utils/_internal/_errors.py +101 -0
- ansible/module_utils/_internal/_event_utils.py +61 -0
- ansible/module_utils/_internal/_json/__init__.py +63 -0
- ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
- ansible/module_utils/_internal/_json/_profiles/__init__.py +428 -0
- ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +33 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +37 -0
- ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
- ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
- ansible/module_utils/_internal/_json/_profiles/_tagless.py +52 -0
- ansible/module_utils/_internal/_messages.py +130 -0
- ansible/module_utils/_internal/_patches/__init__.py +66 -0
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +53 -0
- ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
- ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
- ansible/module_utils/_internal/_plugin_info.py +38 -0
- ansible/module_utils/_internal/_stack.py +22 -0
- ansible/module_utils/_internal/_testing.py +0 -0
- ansible/module_utils/_internal/_text_utils.py +6 -0
- ansible/module_utils/_internal/_traceback.py +92 -0
- ansible/module_utils/_internal/_validation.py +14 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +1 -2
- ansible/module_utils/basic.py +303 -202
- ansible/module_utils/common/_utils.py +24 -28
- ansible/module_utils/common/arg_spec.py +8 -3
- ansible/module_utils/common/collections.py +7 -2
- ansible/module_utils/common/dict_transformations.py +2 -2
- ansible/module_utils/common/file.py +2 -2
- ansible/module_utils/common/json.py +90 -84
- ansible/module_utils/common/locale.py +2 -2
- ansible/module_utils/common/parameters.py +27 -24
- ansible/module_utils/common/process.py +2 -3
- ansible/module_utils/common/respawn.py +11 -33
- ansible/module_utils/common/sentinel.py +66 -0
- ansible/module_utils/common/sys_info.py +8 -8
- ansible/module_utils/common/text/converters.py +16 -37
- ansible/module_utils/common/validation.py +35 -24
- ansible/module_utils/common/warnings.py +143 -25
- ansible/module_utils/common/yaml.py +29 -3
- ansible/module_utils/compat/datetime.py +33 -21
- ansible/module_utils/compat/paramiko.py +21 -10
- ansible/module_utils/compat/typing.py +6 -5
- ansible/module_utils/connection.py +10 -13
- ansible/module_utils/csharp/Ansible.Basic.cs +15 -12
- ansible/module_utils/csharp/Ansible.Become.cs +1 -0
- ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
- ansible/module_utils/csharp/Ansible._Async.cs +517 -0
- ansible/module_utils/datatag.py +49 -0
- ansible/module_utils/distro/__init__.py +2 -2
- ansible/module_utils/facts/ansible_collector.py +4 -5
- ansible/module_utils/facts/collector.py +13 -14
- ansible/module_utils/facts/compat.py +4 -4
- ansible/module_utils/facts/default_collectors.py +1 -1
- ansible/module_utils/facts/hardware/aix.py +34 -0
- ansible/module_utils/facts/hardware/base.py +2 -2
- ansible/module_utils/facts/hardware/darwin.py +1 -3
- ansible/module_utils/facts/hardware/freebsd.py +2 -2
- ansible/module_utils/facts/hardware/linux.py +5 -5
- ansible/module_utils/facts/namespace.py +1 -1
- ansible/module_utils/facts/network/base.py +1 -1
- ansible/module_utils/facts/network/fc_wwn.py +1 -2
- ansible/module_utils/facts/network/iscsi.py +1 -2
- ansible/module_utils/facts/network/nvme.py +1 -2
- ansible/module_utils/facts/other/facter.py +2 -3
- ansible/module_utils/facts/other/ohai.py +2 -3
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/apparmor.py +1 -2
- ansible/module_utils/facts/system/caps.py +3 -3
- ansible/module_utils/facts/system/chroot.py +1 -2
- ansible/module_utils/facts/system/cmdline.py +1 -2
- ansible/module_utils/facts/system/date_time.py +5 -3
- ansible/module_utils/facts/system/distribution.py +27 -13
- ansible/module_utils/facts/system/dns.py +1 -1
- ansible/module_utils/facts/system/env.py +1 -2
- ansible/module_utils/facts/system/fips.py +7 -20
- ansible/module_utils/facts/system/loadavg.py +1 -2
- ansible/module_utils/facts/system/local.py +2 -3
- ansible/module_utils/facts/system/lsb.py +1 -2
- ansible/module_utils/facts/system/pkg_mgr.py +1 -2
- ansible/module_utils/facts/system/platform.py +1 -2
- ansible/module_utils/facts/system/python.py +1 -2
- ansible/module_utils/facts/system/selinux.py +1 -1
- ansible/module_utils/facts/system/service_mgr.py +1 -2
- ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
- ansible/module_utils/facts/system/systemd.py +1 -1
- ansible/module_utils/facts/system/user.py +1 -2
- ansible/module_utils/facts/utils.py +3 -3
- ansible/module_utils/facts/virtual/base.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +3 -3
- ansible/module_utils/facts/virtual/sunos.py +3 -15
- ansible/module_utils/facts/virtual/sysctl.py +3 -16
- ansible/module_utils/json_utils.py +2 -2
- ansible/module_utils/parsing/convert_bool.py +7 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
- ansible/module_utils/service.py +21 -31
- ansible/module_utils/splitter.py +7 -7
- ansible/module_utils/testing.py +31 -0
- ansible/module_utils/urls.py +64 -35
- ansible/modules/add_host.py +4 -4
- ansible/modules/apt.py +69 -49
- ansible/modules/apt_key.py +19 -12
- ansible/modules/apt_repository.py +32 -51
- ansible/modules/assemble.py +16 -14
- ansible/modules/assert.py +4 -4
- ansible/modules/async_status.py +24 -24
- ansible/modules/async_wrapper.py +20 -25
- ansible/modules/blockinfile.py +6 -7
- ansible/modules/command.py +13 -20
- ansible/modules/copy.py +60 -147
- ansible/modules/cron.py +24 -21
- ansible/modules/deb822_repository.py +8 -9
- ansible/modules/debconf.py +5 -5
- ansible/modules/debug.py +4 -4
- ansible/modules/dnf.py +8 -8
- ansible/modules/dnf5.py +39 -13
- ansible/modules/dpkg_selections.py +4 -4
- ansible/modules/expect.py +13 -15
- ansible/modules/fail.py +4 -4
- ansible/modules/fetch.py +4 -4
- ansible/modules/file.py +184 -144
- ansible/modules/find.py +22 -20
- ansible/modules/gather_facts.py +3 -3
- ansible/modules/get_url.py +77 -54
- ansible/modules/getent.py +7 -9
- ansible/modules/git.py +38 -38
- ansible/modules/group.py +6 -6
- ansible/modules/group_by.py +4 -4
- ansible/modules/hostname.py +15 -32
- ansible/modules/import_playbook.py +6 -6
- ansible/modules/import_role.py +6 -6
- ansible/modules/import_tasks.py +6 -6
- ansible/modules/include_role.py +6 -6
- ansible/modules/include_tasks.py +6 -6
- ansible/modules/include_vars.py +6 -6
- ansible/modules/iptables.py +86 -73
- ansible/modules/known_hosts.py +22 -24
- ansible/modules/lineinfile.py +5 -5
- ansible/modules/meta.py +4 -4
- ansible/modules/mount_facts.py +2 -2
- ansible/modules/package.py +10 -4
- ansible/modules/package_facts.py +22 -10
- ansible/modules/pause.py +6 -6
- ansible/modules/ping.py +6 -6
- ansible/modules/pip.py +21 -26
- ansible/modules/raw.py +6 -6
- ansible/modules/reboot.py +6 -6
- ansible/modules/replace.py +10 -14
- ansible/modules/rpm_key.py +7 -8
- ansible/modules/script.py +4 -4
- ansible/modules/service.py +10 -17
- ansible/modules/service_facts.py +87 -10
- ansible/modules/set_fact.py +5 -5
- ansible/modules/set_stats.py +4 -4
- ansible/modules/setup.py +2 -2
- ansible/modules/shell.py +6 -6
- ansible/modules/slurp.py +16 -19
- ansible/modules/stat.py +15 -31
- ansible/modules/subversion.py +15 -15
- ansible/modules/systemd.py +7 -7
- ansible/modules/systemd_service.py +7 -7
- ansible/modules/sysvinit.py +9 -9
- ansible/modules/tempfile.py +5 -6
- ansible/modules/template.py +6 -6
- ansible/modules/unarchive.py +38 -17
- ansible/modules/uri.py +33 -26
- ansible/modules/user.py +45 -32
- ansible/modules/validate_argument_spec.py +10 -7
- ansible/modules/wait_for.py +70 -60
- ansible/modules/wait_for_connection.py +6 -6
- ansible/modules/yum_repository.py +10 -9
- ansible/parsing/ajson.py +17 -37
- ansible/parsing/dataloader.py +99 -54
- ansible/parsing/mod_args.py +62 -60
- ansible/parsing/plugin_docs.py +21 -86
- ansible/parsing/quoting.py +1 -1
- ansible/parsing/splitter.py +27 -12
- ansible/parsing/utils/addresses.py +24 -24
- ansible/parsing/utils/jsonify.py +5 -1
- ansible/parsing/utils/yaml.py +32 -61
- ansible/parsing/vault/__init__.py +327 -99
- ansible/parsing/yaml/__init__.py +0 -18
- ansible/parsing/yaml/dumper.py +6 -120
- ansible/parsing/yaml/loader.py +6 -39
- ansible/parsing/yaml/objects.py +43 -335
- ansible/playbook/__init__.py +1 -1
- ansible/playbook/attribute.py +8 -3
- ansible/playbook/base.py +187 -134
- ansible/playbook/block.py +26 -24
- ansible/playbook/collectionsearch.py +1 -15
- ansible/playbook/conditional.py +3 -77
- ansible/playbook/handler.py +8 -2
- ansible/playbook/helpers.py +41 -53
- ansible/playbook/included_file.py +32 -26
- ansible/playbook/loop_control.py +2 -2
- ansible/playbook/play.py +85 -44
- ansible/playbook/play_context.py +14 -17
- ansible/playbook/playbook_include.py +27 -62
- ansible/playbook/role/__init__.py +64 -49
- ansible/playbook/role/definition.py +15 -17
- ansible/playbook/role/include.py +2 -4
- ansible/playbook/role/metadata.py +10 -11
- ansible/playbook/role_include.py +3 -3
- ansible/playbook/taggable.py +28 -12
- ansible/playbook/task.py +192 -121
- ansible/playbook/task_include.py +5 -5
- ansible/plugins/__init__.py +58 -26
- ansible/plugins/action/__init__.py +188 -186
- ansible/plugins/action/add_host.py +2 -2
- ansible/plugins/action/assemble.py +11 -18
- ansible/plugins/action/assert.py +55 -67
- ansible/plugins/action/async_status.py +7 -2
- ansible/plugins/action/copy.py +14 -17
- ansible/plugins/action/debug.py +37 -31
- ansible/plugins/action/dnf.py +3 -4
- ansible/plugins/action/fail.py +1 -1
- ansible/plugins/action/fetch.py +7 -8
- ansible/plugins/action/gather_facts.py +13 -14
- ansible/plugins/action/group_by.py +1 -1
- ansible/plugins/action/include_vars.py +10 -11
- ansible/plugins/action/package.py +8 -14
- ansible/plugins/action/pause.py +2 -2
- ansible/plugins/action/script.py +27 -38
- ansible/plugins/action/service.py +9 -18
- ansible/plugins/action/set_fact.py +3 -12
- ansible/plugins/action/set_stats.py +3 -8
- ansible/plugins/action/template.py +47 -67
- ansible/plugins/action/unarchive.py +6 -16
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/action/validate_argument_spec.py +5 -5
- ansible/plugins/action/wait_for_connection.py +1 -1
- ansible/plugins/become/__init__.py +31 -8
- ansible/plugins/become/runas.py +71 -0
- ansible/plugins/become/su.py +13 -8
- ansible/plugins/become/sudo.py +19 -0
- ansible/plugins/cache/__init__.py +52 -63
- ansible/plugins/cache/base.py +8 -0
- ansible/plugins/cache/jsonfile.py +10 -16
- ansible/plugins/cache/memory.py +6 -12
- ansible/plugins/callback/__init__.py +294 -201
- ansible/plugins/callback/default.py +99 -95
- ansible/plugins/callback/junit.py +44 -43
- ansible/plugins/callback/minimal.py +28 -25
- ansible/plugins/callback/oneline.py +34 -21
- ansible/plugins/callback/tree.py +27 -16
- ansible/plugins/connection/__init__.py +47 -34
- ansible/plugins/connection/local.py +156 -60
- ansible/plugins/connection/paramiko_ssh.py +34 -24
- ansible/plugins/connection/psrp.py +76 -165
- ansible/plugins/connection/ssh.py +326 -86
- ansible/plugins/connection/winrm.py +62 -141
- ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
- ansible/plugins/doc_fragments/action_core.py +6 -6
- ansible/plugins/doc_fragments/backup.py +2 -2
- ansible/plugins/doc_fragments/checksum_common.py +27 -0
- ansible/plugins/doc_fragments/constructed.py +8 -4
- ansible/plugins/doc_fragments/decrypt.py +2 -2
- ansible/plugins/doc_fragments/default_callback.py +2 -2
- ansible/plugins/doc_fragments/files.py +2 -2
- ansible/plugins/doc_fragments/inventory_cache.py +2 -2
- ansible/plugins/doc_fragments/result_format_callback.py +2 -2
- ansible/plugins/doc_fragments/return_common.py +2 -2
- ansible/plugins/doc_fragments/template_common.py +4 -4
- ansible/plugins/doc_fragments/url.py +17 -1
- ansible/plugins/doc_fragments/url_windows.py +2 -2
- ansible/plugins/doc_fragments/validate.py +2 -2
- ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
- ansible/plugins/filter/__init__.py +6 -2
- ansible/plugins/filter/b64decode.yml +22 -0
- ansible/plugins/filter/b64encode.yml +22 -0
- ansible/plugins/filter/bool.yml +11 -4
- ansible/plugins/filter/core.py +245 -120
- ansible/plugins/filter/encryption.py +42 -34
- ansible/plugins/filter/flatten.yml +3 -2
- ansible/plugins/filter/human_to_bytes.yml +1 -1
- ansible/plugins/filter/mathstuff.py +30 -37
- ansible/plugins/filter/password_hash.yml +8 -0
- ansible/plugins/filter/pow.yml +1 -1
- ansible/plugins/filter/regex_search.yml +1 -4
- ansible/plugins/filter/root.yml +1 -1
- ansible/plugins/filter/split.yml +1 -1
- ansible/plugins/filter/strftime.yml +3 -3
- ansible/plugins/filter/to_nice_yaml.yml +0 -4
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/filter/to_yaml.yml +0 -4
- ansible/plugins/filter/unvault.yml +1 -1
- ansible/plugins/filter/urls.py +1 -1
- ansible/plugins/filter/urlsplit.py +8 -9
- ansible/plugins/filter/vault.yml +14 -9
- ansible/plugins/filter/win_basename.yml +6 -1
- ansible/plugins/filter/win_dirname.yml +5 -0
- ansible/plugins/inventory/__init__.py +107 -86
- ansible/plugins/inventory/advanced_host_list.py +7 -5
- ansible/plugins/inventory/auto.py +11 -4
- ansible/plugins/inventory/constructed.py +21 -24
- ansible/plugins/inventory/generator.py +16 -11
- ansible/plugins/inventory/host_list.py +7 -5
- ansible/plugins/inventory/ini.py +78 -44
- ansible/plugins/inventory/script.py +190 -120
- ansible/plugins/inventory/toml.py +16 -126
- ansible/plugins/inventory/yaml.py +10 -8
- ansible/plugins/list.py +72 -19
- ansible/plugins/loader.py +383 -198
- ansible/plugins/lookup/__init__.py +21 -4
- ansible/plugins/lookup/config.py +21 -35
- ansible/plugins/lookup/csvfile.py +19 -73
- ansible/plugins/lookup/dict.py +1 -6
- ansible/plugins/lookup/env.py +12 -9
- ansible/plugins/lookup/file.py +5 -8
- ansible/plugins/lookup/first_found.py +87 -55
- ansible/plugins/lookup/indexed_items.py +1 -10
- ansible/plugins/lookup/ini.py +14 -13
- ansible/plugins/lookup/items.py +1 -1
- ansible/plugins/lookup/lines.py +8 -1
- ansible/plugins/lookup/list.py +1 -1
- ansible/plugins/lookup/nested.py +2 -18
- ansible/plugins/lookup/password.py +5 -5
- ansible/plugins/lookup/pipe.py +5 -7
- ansible/plugins/lookup/sequence.py +18 -8
- ansible/plugins/lookup/subelements.py +1 -4
- ansible/plugins/lookup/template.py +47 -36
- ansible/plugins/lookup/together.py +0 -12
- ansible/plugins/lookup/unvault.py +1 -5
- ansible/plugins/lookup/url.py +4 -10
- ansible/plugins/lookup/vars.py +16 -24
- ansible/plugins/shell/__init__.py +58 -4
- ansible/plugins/shell/cmd.py +2 -2
- ansible/plugins/shell/powershell.py +106 -31
- ansible/plugins/shell/sh.py +13 -7
- ansible/plugins/strategy/__init__.py +168 -193
- ansible/plugins/strategy/debug.py +2 -2
- ansible/plugins/strategy/free.py +16 -31
- ansible/plugins/strategy/host_pinned.py +2 -2
- ansible/plugins/strategy/linear.py +41 -41
- ansible/plugins/terminal/__init__.py +4 -4
- ansible/plugins/test/__init__.py +7 -2
- ansible/plugins/test/core.py +75 -35
- ansible/plugins/test/files.py +1 -1
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/mathstuff.py +3 -3
- ansible/plugins/test/uri.py +5 -8
- ansible/plugins/vars/host_group_vars.py +7 -14
- ansible/release.py +2 -2
- ansible/template/__init__.py +353 -943
- ansible/utils/__init__.py +0 -18
- ansible/utils/collection_loader/__init__.py +54 -5
- ansible/utils/collection_loader/_collection_config.py +5 -6
- ansible/utils/collection_loader/_collection_finder.py +82 -96
- ansible/utils/collection_loader/_collection_meta.py +15 -8
- ansible/utils/display.py +485 -73
- ansible/utils/encrypt.py +27 -19
- ansible/utils/fqcn.py +2 -2
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +8 -10
- ansible/utils/helpers.py +2 -2
- ansible/utils/listify.py +10 -8
- ansible/utils/lock.py +2 -2
- ansible/utils/path.py +10 -12
- ansible/utils/plugin_docs.py +16 -14
- ansible/utils/py3compat.py +2 -7
- ansible/utils/sentinel.py +4 -62
- ansible/utils/singleton.py +2 -0
- ansible/utils/ssh_functions.py +6 -2
- ansible/utils/unsafe_proxy.py +23 -332
- ansible/utils/vars.py +55 -8
- ansible/utils/version.py +2 -2
- ansible/vars/clean.py +5 -5
- ansible/vars/hostvars.py +60 -90
- ansible/vars/manager.py +220 -285
- ansible/vars/plugins.py +4 -4
- ansible/vars/reserved.py +13 -12
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/METADATA +4 -3
- ansible_core-2.19.0.dist-info/RECORD +1097 -0
- ansible_core-2.19.0.dist-info/licenses/licenses/BSD-3-Clause.txt +28 -0
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/remote.txt +6 -6
- ansible_test/_data/completion/windows.txt +1 -0
- ansible_test/_data/requirements/ansible.txt +2 -2
- ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
- ansible_test/_data/requirements/sanity.changelog.txt +2 -2
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +5 -5
- ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_data/requirements/units.txt +1 -0
- ansible_test/_internal/__init__.py +6 -0
- ansible_test/_internal/ansible_util.py +3 -1
- ansible_test/_internal/become.py +1 -0
- ansible_test/_internal/bootstrap.py +1 -0
- ansible_test/_internal/cache.py +1 -0
- ansible_test/_internal/cgroup.py +1 -0
- ansible_test/_internal/ci/__init__.py +1 -0
- ansible_test/_internal/ci/azp.py +1 -0
- ansible_test/_internal/ci/local.py +1 -0
- ansible_test/_internal/classification/__init__.py +1 -0
- ansible_test/_internal/classification/common.py +1 -0
- ansible_test/_internal/classification/csharp.py +1 -0
- ansible_test/_internal/classification/powershell.py +1 -0
- ansible_test/_internal/classification/python.py +1 -0
- ansible_test/_internal/cli/__init__.py +1 -0
- ansible_test/_internal/cli/actions.py +1 -0
- ansible_test/_internal/cli/argparsing/__init__.py +1 -0
- ansible_test/_internal/cli/argparsing/actions.py +1 -0
- ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
- ansible_test/_internal/cli/argparsing/parsers.py +1 -0
- ansible_test/_internal/cli/commands/__init__.py +11 -5
- ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
- ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
- ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
- ansible_test/_internal/cli/commands/coverage/html.py +1 -0
- ansible_test/_internal/cli/commands/coverage/report.py +1 -0
- ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
- ansible_test/_internal/cli/commands/env.py +1 -0
- ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
- ansible_test/_internal/cli/commands/integration/network.py +1 -0
- ansible_test/_internal/cli/commands/integration/posix.py +1 -0
- ansible_test/_internal/cli/commands/integration/windows.py +1 -0
- ansible_test/_internal/cli/commands/sanity.py +9 -0
- ansible_test/_internal/cli/commands/shell.py +1 -0
- ansible_test/_internal/cli/commands/units.py +1 -0
- ansible_test/_internal/cli/compat.py +1 -0
- ansible_test/_internal/cli/completers.py +1 -0
- ansible_test/_internal/cli/converters.py +1 -0
- ansible_test/_internal/cli/environments.py +52 -5
- ansible_test/_internal/cli/epilog.py +1 -0
- ansible_test/_internal/cli/parsers/__init__.py +1 -0
- ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/helpers.py +1 -0
- ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
- ansible_test/_internal/commands/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/__init__.py +3 -2
- ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
- ansible_test/_internal/commands/coverage/combine.py +2 -1
- ansible_test/_internal/commands/coverage/erase.py +1 -0
- ansible_test/_internal/commands/coverage/html.py +1 -0
- ansible_test/_internal/commands/coverage/report.py +1 -0
- ansible_test/_internal/commands/coverage/xml.py +1 -0
- ansible_test/_internal/commands/env/__init__.py +2 -0
- ansible_test/_internal/commands/integration/__init__.py +22 -5
- ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
- ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
- ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
- ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
- ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
- ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
- ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
- ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
- ansible_test/_internal/commands/integration/cloud/httptester.py +3 -2
- ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
- ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
- ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
- ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
- ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
- ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
- ansible_test/_internal/commands/integration/coverage.py +8 -2
- ansible_test/_internal/commands/integration/filters.py +1 -0
- ansible_test/_internal/commands/integration/network.py +1 -0
- ansible_test/_internal/commands/integration/posix.py +1 -0
- ansible_test/_internal/commands/integration/windows.py +1 -0
- ansible_test/_internal/commands/sanity/__init__.py +19 -2
- ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
- ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
- ansible_test/_internal/commands/sanity/compile.py +1 -0
- ansible_test/_internal/commands/sanity/ignores.py +1 -0
- ansible_test/_internal/commands/sanity/import.py +1 -0
- ansible_test/_internal/commands/sanity/integration_aliases.py +12 -0
- ansible_test/_internal/commands/sanity/pep8.py +1 -0
- ansible_test/_internal/commands/sanity/pslint.py +1 -0
- ansible_test/_internal/commands/sanity/pylint.py +25 -26
- ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
- ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
- ansible_test/_internal/commands/sanity/yamllint.py +1 -0
- ansible_test/_internal/commands/shell/__init__.py +44 -4
- ansible_test/_internal/commands/units/__init__.py +5 -1
- ansible_test/_internal/compat/__init__.py +1 -0
- ansible_test/_internal/compat/packaging.py +1 -0
- ansible_test/_internal/compat/yaml.py +1 -0
- ansible_test/_internal/completion.py +1 -0
- ansible_test/_internal/config.py +23 -13
- ansible_test/_internal/connections.py +1 -0
- ansible_test/_internal/constants.py +1 -0
- ansible_test/_internal/containers.py +1 -0
- ansible_test/_internal/content_config.py +1 -0
- ansible_test/_internal/core_ci.py +1 -0
- ansible_test/_internal/coverage_util.py +11 -10
- ansible_test/_internal/data.py +1 -0
- ansible_test/_internal/debugging.py +166 -0
- ansible_test/_internal/delegation.py +22 -13
- ansible_test/_internal/dev/__init__.py +1 -0
- ansible_test/_internal/dev/container_probe.py +1 -0
- ansible_test/_internal/diff.py +3 -2
- ansible_test/_internal/docker_util.py +2 -1
- ansible_test/_internal/encoding.py +1 -0
- ansible_test/_internal/executor.py +1 -0
- ansible_test/_internal/git.py +1 -0
- ansible_test/_internal/host_configs.py +1 -0
- ansible_test/_internal/host_profiles.py +260 -16
- ansible_test/_internal/http.py +1 -0
- ansible_test/_internal/init.py +1 -0
- ansible_test/_internal/inventory.py +39 -3
- ansible_test/_internal/io.py +1 -0
- ansible_test/_internal/metadata.py +95 -4
- ansible_test/_internal/payload.py +1 -0
- ansible_test/_internal/processes.py +80 -0
- ansible_test/_internal/provider/__init__.py +1 -0
- ansible_test/_internal/provider/layout/__init__.py +1 -0
- ansible_test/_internal/provider/layout/ansible.py +1 -0
- ansible_test/_internal/provider/layout/collection.py +1 -0
- ansible_test/_internal/provider/layout/unsupported.py +1 -0
- ansible_test/_internal/provider/source/__init__.py +1 -0
- ansible_test/_internal/provider/source/git.py +1 -0
- ansible_test/_internal/provider/source/installed.py +1 -0
- ansible_test/_internal/provider/source/unsupported.py +1 -0
- ansible_test/_internal/provider/source/unversioned.py +1 -0
- ansible_test/_internal/provisioning.py +11 -4
- ansible_test/_internal/pypi_proxy.py +6 -5
- ansible_test/_internal/python_requirements.py +28 -0
- ansible_test/_internal/ssh.py +2 -5
- ansible_test/_internal/target.py +9 -0
- ansible_test/_internal/test.py +3 -2
- ansible_test/_internal/thread.py +3 -1
- ansible_test/_internal/timeout.py +2 -1
- ansible_test/_internal/util.py +41 -12
- ansible_test/_internal/util_common.py +18 -5
- ansible_test/_internal/venv.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
- ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
- ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
- ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +8 -5
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +8 -5
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +8 -5
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +4 -5
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +8 -7
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +541 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_comment.py +137 -0
- ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
- ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
- ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
- ansible_test/_util/controller/tools/collection_detail.py +1 -0
- ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
- ansible_test/_util/target/injector/python.py +8 -0
- ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
- ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
- ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
- ansible_test/_util/target/sanity/compile/compile.py +1 -0
- ansible_test/_util/target/sanity/import/importer.py +15 -16
- ansible_test/_util/target/setup/bootstrap.sh +9 -20
- ansible_test/_util/target/setup/probe_cgroups.py +1 -0
- ansible_test/_util/target/setup/quiet_pip.py +1 -0
- ansible_test/_util/target/setup/requirements.py +38 -36
- ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
- ansible_test/_util/target/tools/yamlcheck.py +2 -1
- ansible/compat/selectors.py +0 -32
- ansible/errors/yaml_strings.py +0 -138
- ansible/executor/action_write_locks.py +0 -44
- ansible/executor/discovery/python_target.py +0 -47
- ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
- ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
- ansible/module_utils/compat/importlib.py +0 -26
- ansible/module_utils/compat/selectors.py +0 -32
- ansible/module_utils/pycompat24.py +0 -73
- ansible/parsing/yaml/constructor.py +0 -178
- ansible/template/native_helpers.py +0 -251
- ansible/template/template.py +0 -43
- ansible/template/vars.py +0 -77
- ansible/utils/native_jinja.py +0 -11
- ansible/vars/fact_cache.py +0 -71
- ansible_core-2.18.7rc1.dist-info/RECORD +0 -992
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +0 -411
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/WHEEL +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/top_level.txt +0 -0
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
import errno
|
|
21
20
|
import fcntl
|
|
21
|
+
import functools
|
|
22
22
|
import os
|
|
23
23
|
import random
|
|
24
24
|
import shlex
|
|
@@ -27,11 +27,18 @@ import subprocess
|
|
|
27
27
|
import sys
|
|
28
28
|
import tempfile
|
|
29
29
|
import warnings
|
|
30
|
+
import typing as t
|
|
30
31
|
|
|
31
32
|
from binascii import hexlify
|
|
32
33
|
from binascii import unhexlify
|
|
33
34
|
from binascii import Error as BinasciiError
|
|
34
35
|
|
|
36
|
+
from ansible.module_utils._internal._datatag import (
|
|
37
|
+
AnsibleTagHelper, AnsibleTaggedObject, _AnsibleTagsMapping, _EmptyROInternalTagsMapping, _EMPTY_INTERNAL_TAGS_MAPPING,
|
|
38
|
+
)
|
|
39
|
+
from ansible._internal._templating import _jinja_common
|
|
40
|
+
from ansible._internal._datatag._tags import Origin, VaultedValue, TrustedAsTemplate
|
|
41
|
+
|
|
35
42
|
HAS_CRYPTOGRAPHY = False
|
|
36
43
|
CRYPTOGRAPHY_BACKEND = None
|
|
37
44
|
try:
|
|
@@ -141,11 +148,13 @@ def _parse_vaulttext_envelope(b_vaulttext_envelope, default_vault_id=None):
|
|
|
141
148
|
vault_id = to_text(b_tmpheader[3].strip())
|
|
142
149
|
|
|
143
150
|
b_ciphertext = b''.join(b_tmpdata[1:])
|
|
151
|
+
# DTFIX7: possible candidate for propagate_origin
|
|
152
|
+
b_ciphertext = AnsibleTagHelper.tag_copy(b_vaulttext_envelope, b_ciphertext)
|
|
144
153
|
|
|
145
154
|
return b_ciphertext, b_version, cipher_name, vault_id
|
|
146
155
|
|
|
147
156
|
|
|
148
|
-
def parse_vaulttext_envelope(b_vaulttext_envelope, default_vault_id=None
|
|
157
|
+
def parse_vaulttext_envelope(b_vaulttext_envelope, default_vault_id=None):
|
|
149
158
|
"""Parse the vaulttext envelope
|
|
150
159
|
|
|
151
160
|
When data is saved, it has a header prepended and is formatted into 80
|
|
@@ -153,11 +162,8 @@ def parse_vaulttext_envelope(b_vaulttext_envelope, default_vault_id=None, filena
|
|
|
153
162
|
and then removes the header and the inserted newlines. The string returned
|
|
154
163
|
is suitable for processing by the Cipher classes.
|
|
155
164
|
|
|
156
|
-
:arg
|
|
157
|
-
:
|
|
158
|
-
:kwarg filename: The filename that the data came from. This is only
|
|
159
|
-
used to make better error messages in case the data cannot be
|
|
160
|
-
decrypted. This is optional.
|
|
165
|
+
:arg b_vaulttext_envelope: byte str containing the data from a save file
|
|
166
|
+
:arg default_vault_id: The vault_id name to use if the vaulttext does not provide one.
|
|
161
167
|
:returns: A tuple of byte str of the vaulttext suitable to pass to parse_vaultext,
|
|
162
168
|
a byte str of the vault format version,
|
|
163
169
|
the name of the cipher used, and the vault_id.
|
|
@@ -168,12 +174,8 @@ def parse_vaulttext_envelope(b_vaulttext_envelope, default_vault_id=None, filena
|
|
|
168
174
|
|
|
169
175
|
try:
|
|
170
176
|
return _parse_vaulttext_envelope(b_vaulttext_envelope, default_vault_id)
|
|
171
|
-
except Exception as
|
|
172
|
-
|
|
173
|
-
if filename:
|
|
174
|
-
msg += ' in %s' % (filename)
|
|
175
|
-
msg += ': %s' % exc
|
|
176
|
-
raise AnsibleVaultFormatError(msg)
|
|
177
|
+
except Exception as ex:
|
|
178
|
+
raise AnsibleVaultFormatError("Vault envelope format error.", obj=b_vaulttext_envelope) from ex
|
|
177
179
|
|
|
178
180
|
|
|
179
181
|
def format_vaulttext_envelope(b_ciphertext, cipher_name, version=None, vault_id=None):
|
|
@@ -219,9 +221,10 @@ def format_vaulttext_envelope(b_ciphertext, cipher_name, version=None, vault_id=
|
|
|
219
221
|
|
|
220
222
|
def _unhexlify(b_data):
|
|
221
223
|
try:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
# DTFIX7: possible candidate for propagate_origin
|
|
225
|
+
return AnsibleTagHelper.tag_copy(b_data, unhexlify(b_data))
|
|
226
|
+
except (BinasciiError, TypeError) as ex:
|
|
227
|
+
raise AnsibleVaultFormatError('Vault format unhexlify error.', obj=b_data) from ex
|
|
225
228
|
|
|
226
229
|
|
|
227
230
|
def _parse_vaulttext(b_vaulttext):
|
|
@@ -247,25 +250,24 @@ def parse_vaulttext(b_vaulttext):
|
|
|
247
250
|
return _parse_vaulttext(b_vaulttext)
|
|
248
251
|
except AnsibleVaultFormatError:
|
|
249
252
|
raise
|
|
250
|
-
except Exception as
|
|
251
|
-
|
|
252
|
-
raise AnsibleVaultFormatError(msg)
|
|
253
|
+
except Exception as ex:
|
|
254
|
+
raise AnsibleVaultFormatError("Vault vaulttext format error.", obj=b_vaulttext) from ex
|
|
253
255
|
|
|
254
256
|
|
|
255
257
|
def verify_secret_is_not_empty(secret, msg=None):
|
|
256
|
-
|
|
258
|
+
"""Check the secret against minimal requirements.
|
|
257
259
|
|
|
258
260
|
Raises: AnsibleVaultPasswordError if the password does not meet requirements.
|
|
259
261
|
|
|
260
262
|
Currently, only requirement is that the password is not None or an empty string.
|
|
261
|
-
|
|
263
|
+
"""
|
|
262
264
|
msg = msg or 'Invalid vault password was provided'
|
|
263
265
|
if not secret:
|
|
264
266
|
raise AnsibleVaultPasswordError(msg)
|
|
265
267
|
|
|
266
268
|
|
|
267
269
|
class VaultSecret:
|
|
268
|
-
|
|
270
|
+
"""Opaque/abstract objects for a single vault secret. ie, a password or a key."""
|
|
269
271
|
|
|
270
272
|
def __init__(self, _bytes=None):
|
|
271
273
|
# FIXME: ? that seems wrong... Unset etc?
|
|
@@ -273,10 +275,10 @@ class VaultSecret:
|
|
|
273
275
|
|
|
274
276
|
@property
|
|
275
277
|
def bytes(self):
|
|
276
|
-
|
|
278
|
+
"""The secret as a bytestring.
|
|
277
279
|
|
|
278
280
|
Sub classes that store text types will need to override to encode the text to bytes.
|
|
279
|
-
|
|
281
|
+
"""
|
|
280
282
|
return self._bytes
|
|
281
283
|
|
|
282
284
|
def load(self):
|
|
@@ -335,7 +337,7 @@ class PromptVaultSecret(VaultSecret):
|
|
|
335
337
|
|
|
336
338
|
|
|
337
339
|
def script_is_client(filename):
|
|
338
|
-
|
|
340
|
+
"""Determine if a vault secret script is a client script that can be given --vault-id args"""
|
|
339
341
|
|
|
340
342
|
# if password script is 'something-client' or 'something-client.[sh|py|rb|etc]'
|
|
341
343
|
# script_name can still have '.' or could be entire filename if there is no ext
|
|
@@ -349,7 +351,7 @@ def script_is_client(filename):
|
|
|
349
351
|
|
|
350
352
|
|
|
351
353
|
def get_file_vault_secret(filename=None, vault_id=None, encoding=None, loader=None):
|
|
352
|
-
|
|
354
|
+
""" Get secret from file content or execute file and get secret from stdout """
|
|
353
355
|
|
|
354
356
|
# we unfrack but not follow the full path/context to possible vault script
|
|
355
357
|
# so when the script uses 'adjacent' file for configuration or similar
|
|
@@ -411,10 +413,10 @@ class FileVaultSecret(VaultSecret):
|
|
|
411
413
|
try:
|
|
412
414
|
with open(filename, "rb") as f:
|
|
413
415
|
vault_pass = f.read().strip()
|
|
414
|
-
except
|
|
415
|
-
raise AnsibleError("Could not read vault password file
|
|
416
|
+
except OSError as ex:
|
|
417
|
+
raise AnsibleError(f"Could not read vault password file {filename!r}.") from ex
|
|
416
418
|
|
|
417
|
-
b_vault_data, dummy = self.loader._decrypt_if_vault_data(vault_pass
|
|
419
|
+
b_vault_data, dummy = self.loader._decrypt_if_vault_data(vault_pass)
|
|
418
420
|
|
|
419
421
|
vault_pass = b_vault_data.strip(b'\r\n')
|
|
420
422
|
|
|
@@ -519,7 +521,7 @@ class ClientScriptVaultSecret(ScriptVaultSecret):
|
|
|
519
521
|
|
|
520
522
|
|
|
521
523
|
def match_secrets(secrets, target_vault_ids):
|
|
522
|
-
|
|
524
|
+
"""Find all VaultSecret objects that are mapped to any of the target_vault_ids in secrets"""
|
|
523
525
|
if not secrets:
|
|
524
526
|
return []
|
|
525
527
|
|
|
@@ -528,10 +530,10 @@ def match_secrets(secrets, target_vault_ids):
|
|
|
528
530
|
|
|
529
531
|
|
|
530
532
|
def match_best_secret(secrets, target_vault_ids):
|
|
531
|
-
|
|
533
|
+
"""Find the best secret from secrets that matches target_vault_ids
|
|
532
534
|
|
|
533
535
|
Since secrets should be ordered so the early secrets are 'better' than later ones, this
|
|
534
|
-
just finds all the matches, then returns the first secret
|
|
536
|
+
just finds all the matches, then returns the first secret"""
|
|
535
537
|
matches = match_secrets(secrets, target_vault_ids)
|
|
536
538
|
if matches:
|
|
537
539
|
return matches[0]
|
|
@@ -560,7 +562,7 @@ def match_encrypt_vault_id_secret(secrets, encrypt_vault_id=None):
|
|
|
560
562
|
|
|
561
563
|
|
|
562
564
|
def match_encrypt_secret(secrets, encrypt_vault_id=None):
|
|
563
|
-
|
|
565
|
+
"""Find the best/first/only secret in secrets to use for encrypting"""
|
|
564
566
|
|
|
565
567
|
display.vvvv(u'encrypt_vault_id=%s' % to_text(encrypt_vault_id))
|
|
566
568
|
# See if the --encrypt-vault-id matches a vault-id
|
|
@@ -568,8 +570,8 @@ def match_encrypt_secret(secrets, encrypt_vault_id=None):
|
|
|
568
570
|
return match_encrypt_vault_id_secret(secrets,
|
|
569
571
|
encrypt_vault_id=encrypt_vault_id)
|
|
570
572
|
|
|
571
|
-
# Find the best/first secret from secrets since we
|
|
572
|
-
# ie, consider all
|
|
573
|
+
# Find the best/first secret from secrets since we didn't specify otherwise
|
|
574
|
+
# ie, consider all the available secrets as matches
|
|
573
575
|
_vault_id_matchers = [_vault_id for _vault_id, dummy in secrets]
|
|
574
576
|
best_secret = match_best_secret(secrets, _vault_id_matchers)
|
|
575
577
|
|
|
@@ -633,58 +635,44 @@ class VaultLib:
|
|
|
633
635
|
vault_id=vault_id)
|
|
634
636
|
return b_vaulttext
|
|
635
637
|
|
|
636
|
-
def decrypt(self, vaulttext
|
|
637
|
-
|
|
638
|
+
def decrypt(self, vaulttext):
|
|
639
|
+
"""Decrypt a piece of vault encrypted data.
|
|
638
640
|
|
|
639
641
|
:arg vaulttext: a string to decrypt. Since vault encrypted data is an
|
|
640
642
|
ascii text format this can be either a byte str or unicode string.
|
|
641
|
-
:
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
:returns: a byte string containing the decrypted data and the vault-id that was used
|
|
645
|
-
|
|
646
|
-
'''
|
|
647
|
-
plaintext, vault_id, vault_secret = self.decrypt_and_get_vault_id(vaulttext, filename=filename, obj=obj)
|
|
643
|
+
:returns: a byte string containing the decrypted data
|
|
644
|
+
"""
|
|
645
|
+
plaintext, vault_id, vault_secret = self.decrypt_and_get_vault_id(vaulttext)
|
|
648
646
|
return plaintext
|
|
649
647
|
|
|
650
|
-
def decrypt_and_get_vault_id(self, vaulttext
|
|
648
|
+
def decrypt_and_get_vault_id(self, vaulttext):
|
|
651
649
|
"""Decrypt a piece of vault encrypted data.
|
|
652
650
|
|
|
653
651
|
:arg vaulttext: a string to decrypt. Since vault encrypted data is an
|
|
654
652
|
ascii text format this can be either a byte str or unicode string.
|
|
655
|
-
:kwarg filename: a filename that the data came from. This is only
|
|
656
|
-
used to make better error messages in case the data cannot be
|
|
657
|
-
decrypted.
|
|
658
653
|
:returns: a byte string containing the decrypted data and the vault-id vault-secret that was used
|
|
659
|
-
|
|
660
654
|
"""
|
|
661
|
-
|
|
655
|
+
origin = Origin.get_tag(vaulttext)
|
|
656
|
+
|
|
657
|
+
b_vaulttext = to_bytes(vaulttext, nonstring='error') # enforce vaulttext is str/bytes, keep type check if removing type conversion
|
|
662
658
|
|
|
663
659
|
if self.secrets is None:
|
|
664
|
-
|
|
665
|
-
if filename:
|
|
666
|
-
msg += " in file %s" % to_native(filename)
|
|
667
|
-
raise AnsibleVaultError(msg)
|
|
660
|
+
raise AnsibleVaultError("A vault password must be specified to decrypt data.", obj=vaulttext)
|
|
668
661
|
|
|
669
662
|
if not is_encrypted(b_vaulttext):
|
|
670
|
-
|
|
671
|
-
if filename:
|
|
672
|
-
msg += "%s is not a vault encrypted file" % to_native(filename)
|
|
673
|
-
raise AnsibleError(msg)
|
|
663
|
+
raise AnsibleVaultError("Input is not vault encrypted data.", obj=vaulttext)
|
|
674
664
|
|
|
675
|
-
b_vaulttext, dummy, cipher_name, vault_id = parse_vaulttext_envelope(b_vaulttext
|
|
665
|
+
b_vaulttext, dummy, cipher_name, vault_id = parse_vaulttext_envelope(b_vaulttext)
|
|
676
666
|
|
|
677
667
|
# create the cipher object, note that the cipher used for decrypt can
|
|
678
668
|
# be different than the cipher used for encrypt
|
|
679
669
|
if cipher_name in CIPHER_ALLOWLIST:
|
|
680
670
|
this_cipher = CIPHER_MAPPING[cipher_name]()
|
|
681
671
|
else:
|
|
682
|
-
raise
|
|
683
|
-
|
|
684
|
-
b_plaintext = None
|
|
672
|
+
raise AnsibleVaultError(f"Cipher {cipher_name!r} could not be found.", obj=vaulttext)
|
|
685
673
|
|
|
686
674
|
if not self.secrets:
|
|
687
|
-
raise AnsibleVaultError('Attempting to decrypt but no vault secrets found')
|
|
675
|
+
raise AnsibleVaultError('Attempting to decrypt but no vault secrets found.', obj=vaulttext)
|
|
688
676
|
|
|
689
677
|
# WARNING: Currently, the vault id is not required to match the vault id in the vault blob to
|
|
690
678
|
# decrypt a vault properly. The vault id in the vault blob is not part of the encrypted
|
|
@@ -697,15 +685,13 @@ class VaultLib:
|
|
|
697
685
|
# we check it first.
|
|
698
686
|
|
|
699
687
|
vault_id_matchers = []
|
|
700
|
-
vault_id_used = None
|
|
701
|
-
vault_secret_used = None
|
|
702
688
|
|
|
703
689
|
if vault_id:
|
|
704
690
|
display.vvvvv(u'Found a vault_id (%s) in the vaulttext' % to_text(vault_id))
|
|
705
691
|
vault_id_matchers.append(vault_id)
|
|
706
692
|
_matches = match_secrets(self.secrets, vault_id_matchers)
|
|
707
693
|
if _matches:
|
|
708
|
-
display.vvvvv(u'We have a secret associated with vault id (%s), will try to use to decrypt %s' % (to_text(vault_id), to_text(
|
|
694
|
+
display.vvvvv(u'We have a secret associated with vault id (%s), will try to use to decrypt %s' % (to_text(vault_id), to_text(origin)))
|
|
709
695
|
else:
|
|
710
696
|
display.vvvvv(u'Found a vault_id (%s) in the vault text, but we do not have a associated secret (--vault-id)' % to_text(vault_id))
|
|
711
697
|
|
|
@@ -719,45 +705,32 @@ class VaultLib:
|
|
|
719
705
|
|
|
720
706
|
# for vault_secret_id in vault_secret_ids:
|
|
721
707
|
for vault_secret_id, vault_secret in matched_secrets:
|
|
722
|
-
display.vvvvv(u'Trying to use vault secret=(%s) id=%s to decrypt %s' % (to_text(vault_secret), to_text(vault_secret_id), to_text(
|
|
708
|
+
display.vvvvv(u'Trying to use vault secret=(%s) id=%s to decrypt %s' % (to_text(vault_secret), to_text(vault_secret_id), to_text(origin)))
|
|
723
709
|
|
|
724
710
|
try:
|
|
725
711
|
# secret = self.secrets[vault_secret_id]
|
|
726
712
|
display.vvvv(u'Trying secret %s for vault_id=%s' % (to_text(vault_secret), to_text(vault_secret_id)))
|
|
727
713
|
b_plaintext = this_cipher.decrypt(b_vaulttext, vault_secret)
|
|
714
|
+
# DTFIX7: possible candidate for propagate_origin
|
|
715
|
+
b_plaintext = AnsibleTagHelper.tag_copy(vaulttext, b_plaintext)
|
|
728
716
|
if b_plaintext is not None:
|
|
729
717
|
vault_id_used = vault_secret_id
|
|
730
718
|
vault_secret_used = vault_secret
|
|
731
719
|
file_slug = ''
|
|
732
|
-
if
|
|
733
|
-
file_slug = ' of "%s"' %
|
|
720
|
+
if origin:
|
|
721
|
+
file_slug = ' of "%s"' % origin
|
|
734
722
|
display.vvvvv(
|
|
735
723
|
u'Decrypt%s successful with secret=%s and vault_id=%s' % (to_text(file_slug), to_text(vault_secret), to_text(vault_secret_id))
|
|
736
724
|
)
|
|
737
725
|
break
|
|
738
|
-
except AnsibleVaultFormatError
|
|
739
|
-
exc.obj = obj
|
|
740
|
-
msg = u"There was a vault format error"
|
|
741
|
-
if filename:
|
|
742
|
-
msg += u' in %s' % (to_text(filename))
|
|
743
|
-
msg += u': %s' % to_text(exc)
|
|
744
|
-
display.warning(msg, formatted=True)
|
|
726
|
+
except AnsibleVaultFormatError:
|
|
745
727
|
raise
|
|
746
728
|
except AnsibleError as e:
|
|
747
729
|
display.vvvv(u'Tried to use the vault secret (%s) to decrypt (%s) but it failed. Error: %s' %
|
|
748
|
-
(to_text(vault_secret_id), to_text(
|
|
730
|
+
(to_text(vault_secret_id), to_text(origin), e))
|
|
749
731
|
continue
|
|
750
732
|
else:
|
|
751
|
-
|
|
752
|
-
if filename:
|
|
753
|
-
msg += " on %s" % to_native(filename)
|
|
754
|
-
raise AnsibleVaultError(msg)
|
|
755
|
-
|
|
756
|
-
if b_plaintext is None:
|
|
757
|
-
msg = "Decryption failed"
|
|
758
|
-
if filename:
|
|
759
|
-
msg += " on %s" % to_native(filename)
|
|
760
|
-
raise AnsibleError(msg)
|
|
733
|
+
raise AnsibleVaultError("Decryption failed (no vault secrets were found that could decrypt).", obj=vaulttext)
|
|
761
734
|
|
|
762
735
|
return b_plaintext, vault_id_used, vault_secret_used
|
|
763
736
|
|
|
@@ -916,7 +889,7 @@ class VaultEditor:
|
|
|
916
889
|
ciphertext = self.read_data(filename)
|
|
917
890
|
|
|
918
891
|
try:
|
|
919
|
-
plaintext = self.vault.decrypt(ciphertext
|
|
892
|
+
plaintext = self.vault.decrypt(ciphertext)
|
|
920
893
|
except AnsibleError as e:
|
|
921
894
|
raise AnsibleError("%s for %s" % (to_native(e), to_native(filename)))
|
|
922
895
|
self.write_data(plaintext, output_file or filename, shred=False)
|
|
@@ -956,7 +929,7 @@ class VaultEditor:
|
|
|
956
929
|
|
|
957
930
|
# Figure out the vault id from the file, to select the right secret to re-encrypt it
|
|
958
931
|
# (duplicates parts of decrypt, but alas...)
|
|
959
|
-
dummy, dummy, cipher_name, vault_id = parse_vaulttext_envelope(b_vaulttext
|
|
932
|
+
dummy, dummy, cipher_name, vault_id = parse_vaulttext_envelope(b_vaulttext)
|
|
960
933
|
|
|
961
934
|
# vault id here may not be the vault id actually used for decrypting
|
|
962
935
|
# as when the edited file has no vault-id but is decrypted by non-default id in secrets
|
|
@@ -974,7 +947,7 @@ class VaultEditor:
|
|
|
974
947
|
vaulttext = to_text(b_vaulttext)
|
|
975
948
|
|
|
976
949
|
try:
|
|
977
|
-
plaintext = self.vault.decrypt(vaulttext
|
|
950
|
+
plaintext = self.vault.decrypt(vaulttext)
|
|
978
951
|
return plaintext
|
|
979
952
|
except AnsibleError as e:
|
|
980
953
|
raise AnsibleVaultError("%s for %s" % (to_native(e), to_native(filename)))
|
|
@@ -1024,10 +997,12 @@ class VaultEditor:
|
|
|
1024
997
|
|
|
1025
998
|
try:
|
|
1026
999
|
if filename == '-':
|
|
1027
|
-
data = sys.stdin.buffer.read()
|
|
1000
|
+
data = Origin(description='<stdin>').tag(sys.stdin.buffer.read())
|
|
1028
1001
|
else:
|
|
1002
|
+
filename = os.path.abspath(filename)
|
|
1003
|
+
|
|
1029
1004
|
with open(filename, "rb") as fh:
|
|
1030
|
-
data = fh.read()
|
|
1005
|
+
data = Origin(path=filename).tag(fh.read())
|
|
1031
1006
|
except Exception as e:
|
|
1032
1007
|
msg = to_native(e)
|
|
1033
1008
|
if not msg:
|
|
@@ -1095,13 +1070,10 @@ class VaultEditor:
|
|
|
1095
1070
|
try:
|
|
1096
1071
|
# create file with secure permissions
|
|
1097
1072
|
fd = os.open(thefile, os.O_CREAT | os.O_EXCL | os.O_RDWR | os.O_TRUNC, mode)
|
|
1098
|
-
except
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
raise AnsibleError('Vault file got recreated while we were operating on it: %s' % to_native(ose))
|
|
1103
|
-
|
|
1104
|
-
raise AnsibleError('Problem creating temporary vault file: %s' % to_native(ose))
|
|
1073
|
+
except FileExistsError as ex:
|
|
1074
|
+
raise AnsibleError('Vault file got recreated while we were operating on it.') from ex
|
|
1075
|
+
except OSError as ex:
|
|
1076
|
+
raise AnsibleError('Problem creating temporary vault file.') from ex
|
|
1105
1077
|
|
|
1106
1078
|
try:
|
|
1107
1079
|
# now write to the file and ensure ours is only data in it
|
|
@@ -1170,6 +1142,7 @@ class VaultAES256:
|
|
|
1170
1142
|
return b_derivedkey
|
|
1171
1143
|
|
|
1172
1144
|
@classmethod
|
|
1145
|
+
@functools.cache # Concurrent first-use by multiple threads will all execute the method body.
|
|
1173
1146
|
def _gen_key_initctr(cls, b_password, b_salt):
|
|
1174
1147
|
# 16 for AES 128, 32 for AES256
|
|
1175
1148
|
key_length = 32
|
|
@@ -1302,3 +1275,258 @@ class VaultAES256:
|
|
|
1302
1275
|
CIPHER_MAPPING = {
|
|
1303
1276
|
u'AES256': VaultAES256,
|
|
1304
1277
|
}
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
class VaultSecretsContext:
|
|
1281
|
+
"""Provides context-style access to vault secrets."""
|
|
1282
|
+
_current: t.ClassVar[t.Self | None] = None
|
|
1283
|
+
|
|
1284
|
+
def __init__(self, secrets: list[tuple[str, VaultSecret]]) -> None:
|
|
1285
|
+
self.secrets = secrets
|
|
1286
|
+
|
|
1287
|
+
@classmethod
|
|
1288
|
+
def initialize(cls, value: t.Self) -> None:
|
|
1289
|
+
"""
|
|
1290
|
+
Initialize VaultSecretsContext with the specified instance and secrets (since it's not a lazy or per-thread context).
|
|
1291
|
+
This method will fail if called more than once.
|
|
1292
|
+
"""
|
|
1293
|
+
if cls._current:
|
|
1294
|
+
raise RuntimeError(f"The {cls.__name__} context is already initialized.")
|
|
1295
|
+
|
|
1296
|
+
cls._current = value
|
|
1297
|
+
|
|
1298
|
+
@classmethod
|
|
1299
|
+
def current(cls, optional: bool = False) -> t.Self:
|
|
1300
|
+
"""Access vault secrets, if initialized, ala `AmbientContextBase.current()`."""
|
|
1301
|
+
if not cls._current and not optional:
|
|
1302
|
+
raise ReferenceError(f"A required {cls.__name__} context is not active.")
|
|
1303
|
+
|
|
1304
|
+
return cls._current
|
|
1305
|
+
|
|
1306
|
+
|
|
1307
|
+
@t.final
|
|
1308
|
+
class EncryptedString(AnsibleTaggedObject):
|
|
1309
|
+
"""
|
|
1310
|
+
An encrypted string which supports tagging and on-demand decryption.
|
|
1311
|
+
All methods provided by Python's built-in `str` are supported, all of which operate on the decrypted value.
|
|
1312
|
+
Any attempt to use this value when it cannot be decrypted will raise an exception.
|
|
1313
|
+
Despite supporting `str` methods, access to an instance of this type through templating is recommended over direct access.
|
|
1314
|
+
"""
|
|
1315
|
+
|
|
1316
|
+
__slots__ = ('_ciphertext', '_plaintext', '_ansible_tags_mapping')
|
|
1317
|
+
|
|
1318
|
+
_subclasses_native_type: t.ClassVar[bool] = False
|
|
1319
|
+
_empty_tags_as_native: t.ClassVar[bool] = False
|
|
1320
|
+
|
|
1321
|
+
_ciphertext: str
|
|
1322
|
+
_plaintext: str | None
|
|
1323
|
+
_ansible_tags_mapping: _AnsibleTagsMapping | _EmptyROInternalTagsMapping
|
|
1324
|
+
|
|
1325
|
+
def __init__(self, *, ciphertext: str) -> None:
|
|
1326
|
+
if type(ciphertext) is not str: # pylint: disable=unidiomatic-typecheck
|
|
1327
|
+
raise TypeError(f'ciphertext must be {str} instead of {type(ciphertext)}')
|
|
1328
|
+
|
|
1329
|
+
object.__setattr__(self, '_ciphertext', ciphertext)
|
|
1330
|
+
object.__setattr__(self, '_plaintext', None)
|
|
1331
|
+
object.__setattr__(self, '_ansible_tags_mapping', _EMPTY_INTERNAL_TAGS_MAPPING)
|
|
1332
|
+
|
|
1333
|
+
@classmethod
|
|
1334
|
+
def _instance_factory(cls, value: t.Any, tags_mapping: _AnsibleTagsMapping) -> EncryptedString:
|
|
1335
|
+
instance = EncryptedString.__new__(EncryptedString)
|
|
1336
|
+
|
|
1337
|
+
# In 2.18 and earlier, vaulted values were not trusted.
|
|
1338
|
+
# This maintains backwards compatibility with that.
|
|
1339
|
+
# Additionally, supporting templating on vaulted values could be problematic for a few cases:
|
|
1340
|
+
# 1) There's no way to compose YAML tags, so you can't use `!unsafe` and `!vault` together.
|
|
1341
|
+
# 2) It would make composing `EncryptedString` with a possible future `TemplateString` more difficult.
|
|
1342
|
+
tags_mapping.pop(TrustedAsTemplate, None)
|
|
1343
|
+
|
|
1344
|
+
object.__setattr__(instance, '_ciphertext', value._ciphertext)
|
|
1345
|
+
object.__setattr__(instance, '_plaintext', value._plaintext)
|
|
1346
|
+
object.__setattr__(instance, '_ansible_tags_mapping', tags_mapping)
|
|
1347
|
+
|
|
1348
|
+
return instance
|
|
1349
|
+
|
|
1350
|
+
def __setstate__(self, state: tuple[None, dict[str, t.Any]]) -> None:
|
|
1351
|
+
for key, value in state[1].items():
|
|
1352
|
+
object.__setattr__(self, key, value)
|
|
1353
|
+
|
|
1354
|
+
def __delattr__(self, item: str) -> t.NoReturn:
|
|
1355
|
+
raise AttributeError(f'{self.__class__.__name__!r} object is read-only')
|
|
1356
|
+
|
|
1357
|
+
def __setattr__(self, key: str, value: object) -> t.NoReturn:
|
|
1358
|
+
raise AttributeError(f'{self.__class__.__name__!r} object is read-only')
|
|
1359
|
+
|
|
1360
|
+
@classmethod
|
|
1361
|
+
def _init_class(cls) -> None:
|
|
1362
|
+
"""
|
|
1363
|
+
Add proxies for the specified `str` methods.
|
|
1364
|
+
These proxies operate on the plaintext, which is decrypted on-demand.
|
|
1365
|
+
"""
|
|
1366
|
+
cls._native_type = cls
|
|
1367
|
+
|
|
1368
|
+
operator_method_names = (
|
|
1369
|
+
'__eq__',
|
|
1370
|
+
'__ge__',
|
|
1371
|
+
'__gt__',
|
|
1372
|
+
'__le__',
|
|
1373
|
+
'__lt__',
|
|
1374
|
+
'__ne__',
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
method_names = (
|
|
1378
|
+
'__add__',
|
|
1379
|
+
'__contains__',
|
|
1380
|
+
'__format__',
|
|
1381
|
+
'__getitem__',
|
|
1382
|
+
'__hash__',
|
|
1383
|
+
'__iter__',
|
|
1384
|
+
'__len__',
|
|
1385
|
+
'__mod__',
|
|
1386
|
+
'__mul__',
|
|
1387
|
+
'__rmod__',
|
|
1388
|
+
'__rmul__',
|
|
1389
|
+
'capitalize',
|
|
1390
|
+
'casefold',
|
|
1391
|
+
'center',
|
|
1392
|
+
'count',
|
|
1393
|
+
'encode',
|
|
1394
|
+
'endswith',
|
|
1395
|
+
'expandtabs',
|
|
1396
|
+
'find',
|
|
1397
|
+
'format',
|
|
1398
|
+
'format_map',
|
|
1399
|
+
'index',
|
|
1400
|
+
'isalnum',
|
|
1401
|
+
'isalpha',
|
|
1402
|
+
'isascii',
|
|
1403
|
+
'isdecimal',
|
|
1404
|
+
'isdigit',
|
|
1405
|
+
'isidentifier',
|
|
1406
|
+
'islower',
|
|
1407
|
+
'isnumeric',
|
|
1408
|
+
'isprintable',
|
|
1409
|
+
'isspace',
|
|
1410
|
+
'istitle',
|
|
1411
|
+
'isupper',
|
|
1412
|
+
'join',
|
|
1413
|
+
'ljust',
|
|
1414
|
+
'lower',
|
|
1415
|
+
'lstrip',
|
|
1416
|
+
'maketrans', # static, but implemented for simplicity/consistency
|
|
1417
|
+
'partition',
|
|
1418
|
+
'removeprefix',
|
|
1419
|
+
'removesuffix',
|
|
1420
|
+
'replace',
|
|
1421
|
+
'rfind',
|
|
1422
|
+
'rindex',
|
|
1423
|
+
'rjust',
|
|
1424
|
+
'rpartition',
|
|
1425
|
+
'rsplit',
|
|
1426
|
+
'rstrip',
|
|
1427
|
+
'split',
|
|
1428
|
+
'splitlines',
|
|
1429
|
+
'startswith',
|
|
1430
|
+
'strip',
|
|
1431
|
+
'swapcase',
|
|
1432
|
+
'title',
|
|
1433
|
+
'translate',
|
|
1434
|
+
'upper',
|
|
1435
|
+
'zfill',
|
|
1436
|
+
)
|
|
1437
|
+
|
|
1438
|
+
for method_name in operator_method_names:
|
|
1439
|
+
setattr(cls, method_name, functools.partialmethod(cls._proxy_str_operator_method, getattr(str, method_name)))
|
|
1440
|
+
|
|
1441
|
+
for method_name in method_names:
|
|
1442
|
+
setattr(cls, method_name, functools.partialmethod(cls._proxy_str_method, getattr(str, method_name)))
|
|
1443
|
+
|
|
1444
|
+
def _decrypt(self) -> str:
|
|
1445
|
+
"""
|
|
1446
|
+
Attempt to decrypt the ciphertext and return the plaintext, which will be cached.
|
|
1447
|
+
If decryption fails an exception will be raised and no result will be cached.
|
|
1448
|
+
"""
|
|
1449
|
+
if self._plaintext is None:
|
|
1450
|
+
vault = VaultLib(secrets=VaultSecretsContext.current().secrets)
|
|
1451
|
+
# use the utility method to ensure that origin tags are available
|
|
1452
|
+
plaintext = to_text(vault.decrypt(VaultHelper.get_ciphertext(self, with_tags=True))) # raises if the ciphertext cannot be decrypted
|
|
1453
|
+
|
|
1454
|
+
# propagate source value tags plus VaultedValue for round-tripping ciphertext
|
|
1455
|
+
plaintext = AnsibleTagHelper.tag(plaintext, AnsibleTagHelper.tags(self) | {VaultedValue(ciphertext=self._ciphertext)})
|
|
1456
|
+
|
|
1457
|
+
object.__setattr__(self, '_plaintext', plaintext)
|
|
1458
|
+
|
|
1459
|
+
return self._plaintext
|
|
1460
|
+
|
|
1461
|
+
def _as_dict(self) -> t.Dict[str, t.Any]:
|
|
1462
|
+
return dict(
|
|
1463
|
+
value=self._ciphertext,
|
|
1464
|
+
tags=list(self._ansible_tags_mapping.values()),
|
|
1465
|
+
)
|
|
1466
|
+
|
|
1467
|
+
def _native_copy(self) -> str:
|
|
1468
|
+
return AnsibleTagHelper.untag(self._decrypt())
|
|
1469
|
+
|
|
1470
|
+
def _proxy_str_operator_method(self, method: t.Callable, other) -> t.Any:
|
|
1471
|
+
obj = self._decrypt()
|
|
1472
|
+
|
|
1473
|
+
if type(other) is EncryptedString: # pylint: disable=unidiomatic-typecheck
|
|
1474
|
+
other = other._decrypt()
|
|
1475
|
+
|
|
1476
|
+
return method(obj, other)
|
|
1477
|
+
|
|
1478
|
+
def _proxy_str_method(self, method: t.Callable, *args, **kwargs) -> t.Any:
|
|
1479
|
+
obj = self._decrypt()
|
|
1480
|
+
return method(obj, *args, **kwargs)
|
|
1481
|
+
|
|
1482
|
+
def __repr__(self) -> str:
|
|
1483
|
+
return f'{self.__class__.__name__}(ciphertext={self._ciphertext!r})'
|
|
1484
|
+
|
|
1485
|
+
def __str__(self) -> str:
|
|
1486
|
+
return self._decrypt()
|
|
1487
|
+
|
|
1488
|
+
def __float__(self) -> float:
|
|
1489
|
+
return float(self._decrypt())
|
|
1490
|
+
|
|
1491
|
+
def __int__(self) -> int:
|
|
1492
|
+
return int(self._decrypt())
|
|
1493
|
+
|
|
1494
|
+
def __radd__(self, other: t.Any) -> str:
|
|
1495
|
+
return other + self._decrypt()
|
|
1496
|
+
|
|
1497
|
+
def __fspath__(self) -> str:
|
|
1498
|
+
return self._decrypt()
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
class VaultHelper:
|
|
1502
|
+
"""Vault specific utility methods."""
|
|
1503
|
+
|
|
1504
|
+
@staticmethod
|
|
1505
|
+
def get_ciphertext(value: t.Any, *, with_tags: bool) -> str | None:
|
|
1506
|
+
"""
|
|
1507
|
+
If the given value is an `EncryptedString`, `VaultExceptionMarker` or tagged with `VaultedValue`, return the ciphertext, otherwise return `None`.
|
|
1508
|
+
Tags on the value other than `VaultedValue` will be included on the ciphertext if `with_tags` is `True`, otherwise it will be tagless.
|
|
1509
|
+
"""
|
|
1510
|
+
value_type = type(value)
|
|
1511
|
+
ciphertext: str | None
|
|
1512
|
+
tags = AnsibleTagHelper.tags(value)
|
|
1513
|
+
|
|
1514
|
+
if value_type is _jinja_common.VaultExceptionMarker:
|
|
1515
|
+
ciphertext = value._marker_undecryptable_ciphertext
|
|
1516
|
+
tags = AnsibleTagHelper.tags(ciphertext) # ciphertext has tags but value does not
|
|
1517
|
+
elif value_type is EncryptedString:
|
|
1518
|
+
ciphertext = value._ciphertext
|
|
1519
|
+
elif value_type in _jinja_common.Marker._concrete_subclasses: # avoid wasteful raise/except of Marker when calling get_tag below
|
|
1520
|
+
ciphertext = None
|
|
1521
|
+
elif vaulted_value := VaultedValue.get_tag(value):
|
|
1522
|
+
ciphertext = vaulted_value.ciphertext
|
|
1523
|
+
else:
|
|
1524
|
+
ciphertext = None
|
|
1525
|
+
|
|
1526
|
+
if ciphertext:
|
|
1527
|
+
if with_tags:
|
|
1528
|
+
ciphertext = VaultedValue.untag(AnsibleTagHelper.tag(ciphertext, tags))
|
|
1529
|
+
else:
|
|
1530
|
+
ciphertext = AnsibleTagHelper.untag(ciphertext)
|
|
1531
|
+
|
|
1532
|
+
return ciphertext
|