ansible-core 2.18.5rc1__py3-none-any.whl → 2.19.0b2__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.
- ansible/_internal/__init__.py +53 -0
- ansible/_internal/_ansiballz.py +265 -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/_captured.py +128 -0
- ansible/_internal/_errors/_handler.py +91 -0
- ansible/_internal/_errors/_utils.py +310 -0
- ansible/_internal/_json/__init__.py +203 -0
- ansible/_internal/_json/_legacy_encoder.py +34 -0
- ansible/_internal/_json/_profiles/__init__.py +0 -0
- ansible/_internal/_json/_profiles/_cache_persistence.py +55 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
- ansible/_internal/_json/_profiles/_legacy.py +197 -0
- ansible/_internal/_locking.py +21 -0
- ansible/_internal/_plugins/__init__.py +0 -0
- ansible/_internal/_plugins/_cache.py +57 -0
- ansible/_internal/_task.py +78 -0
- ansible/_internal/_templating/__init__.py +10 -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 +588 -0
- ansible/_internal/_templating/_errors.py +28 -0
- ansible/_internal/_templating/_jinja_bits.py +1066 -0
- ansible/_internal/_templating/_jinja_common.py +332 -0
- ansible/_internal/_templating/_jinja_patches.py +44 -0
- ansible/_internal/_templating/_jinja_plugins.py +345 -0
- ansible/_internal/_templating/_lazy_containers.py +633 -0
- ansible/_internal/_templating/_marker_behaviors.py +103 -0
- ansible/_internal/_templating/_transform.py +63 -0
- ansible/_internal/_templating/_utils.py +107 -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 +62 -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 +18 -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 +159 -89
- ansible/cli/_ssh_askpass.py +47 -0
- ansible/cli/adhoc.py +14 -7
- ansible/cli/arguments/option_helpers.py +154 -7
- ansible/cli/config.py +43 -68
- ansible/cli/console.py +10 -8
- ansible/cli/doc.py +62 -53
- ansible/cli/galaxy.py +27 -20
- ansible/cli/inventory.py +28 -26
- ansible/cli/playbook.py +4 -12
- ansible/cli/pull.py +51 -11
- ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
- ansible/cli/vault.py +12 -11
- ansible/compat/__init__.py +2 -2
- ansible/config/base.yml +166 -112
- ansible/config/manager.py +52 -49
- ansible/constants.py +3 -4
- ansible/errors/__init__.py +277 -235
- ansible/executor/interpreter_discovery.py +28 -149
- ansible/executor/module_common.py +426 -493
- 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 +202 -151
- ansible/executor/powershell/become_wrapper.ps1 +89 -144
- ansible/executor/powershell/bootstrap_wrapper.ps1 +24 -9
- ansible/executor/powershell/coverage_wrapper.ps1 +82 -135
- ansible/executor/powershell/exec_wrapper.ps1 +462 -196
- ansible/executor/powershell/module_manifest.py +417 -265
- ansible/executor/powershell/module_wrapper.ps1 +169 -186
- 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 +161 -96
- ansible/executor/stats.py +5 -5
- ansible/executor/task_executor.py +268 -258
- ansible/executor/task_queue_manager.py +124 -90
- ansible/executor/task_result.py +183 -78
- ansible/galaxy/__init__.py +2 -2
- ansible/galaxy/api.py +22 -18
- ansible/galaxy/collection/__init__.py +1 -1
- ansible/galaxy/collection/concrete_artifact_manager.py +8 -11
- ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
- ansible/galaxy/dependency_resolution/providers.py +1 -1
- ansible/galaxy/dependency_resolution/reporters.py +81 -0
- ansible/galaxy/role.py +4 -8
- ansible/galaxy/token.py +28 -21
- ansible/inventory/data.py +47 -57
- ansible/inventory/group.py +44 -72
- ansible/inventory/helpers.py +9 -0
- ansible/inventory/host.py +32 -54
- ansible/inventory/manager.py +78 -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.py +133 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
- ansible/module_utils/_internal/_dataclass_annotation_patch.py +64 -0
- ansible/module_utils/_internal/_dataclass_validation.py +217 -0
- ansible/module_utils/_internal/_datatag/__init__.py +928 -0
- ansible/module_utils/_internal/_datatag/_tags.py +38 -0
- ansible/module_utils/_internal/_debugging.py +31 -0
- ansible/module_utils/_internal/_errors.py +30 -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 +410 -0
- ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +31 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +35 -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 +50 -0
- ansible/module_utils/_internal/_patches/__init__.py +66 -0
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +55 -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_exec_context.py +49 -0
- ansible/module_utils/_internal/_testing.py +0 -0
- ansible/module_utils/_internal/_traceback.py +89 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +1 -2
- ansible/module_utils/basic.py +152 -120
- ansible/module_utils/common/_utils.py +24 -28
- ansible/module_utils/common/collections.py +1 -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/messages.py +108 -0
- ansible/module_utils/common/parameters.py +27 -24
- ansible/module_utils/common/process.py +2 -2
- ansible/module_utils/common/respawn.py +41 -19
- 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 +86 -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 +2 -2
- ansible/module_utils/csharp/Ansible.Basic.cs +14 -11
- ansible/module_utils/csharp/Ansible.Become.cs +1 -0
- ansible/module_utils/csharp/Ansible._Async.cs +517 -0
- ansible/module_utils/datatag.py +46 -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 +1 -1
- 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 +4 -4
- 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 +1 -2
- ansible/module_utils/facts/other/ohai.py +2 -3
- ansible/module_utils/facts/system/apparmor.py +1 -2
- ansible/module_utils/facts/system/caps.py +1 -1
- 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 +9 -8
- 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 +1 -2
- 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/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 +1 -1
- ansible/module_utils/service.py +18 -21
- ansible/module_utils/splitter.py +7 -7
- ansible/module_utils/testing.py +31 -0
- ansible/module_utils/urls.py +60 -31
- ansible/modules/add_host.py +4 -4
- ansible/modules/apt.py +60 -46
- ansible/modules/apt_key.py +19 -12
- ansible/modules/apt_repository.py +19 -16
- ansible/modules/assemble.py +6 -6
- ansible/modules/assert.py +4 -4
- ansible/modules/async_status.py +10 -12
- ansible/modules/async_wrapper.py +8 -3
- ansible/modules/blockinfile.py +6 -7
- ansible/modules/command.py +10 -17
- ansible/modules/copy.py +57 -144
- ansible/modules/cron.py +20 -15
- 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 +8 -10
- ansible/modules/fail.py +4 -4
- ansible/modules/fetch.py +4 -4
- ansible/modules/file.py +174 -133
- ansible/modules/find.py +19 -17
- ansible/modules/gather_facts.py +3 -3
- ansible/modules/get_url.py +59 -53
- ansible/modules/getent.py +7 -9
- ansible/modules/git.py +28 -25
- ansible/modules/group.py +6 -6
- ansible/modules/group_by.py +4 -4
- ansible/modules/hostname.py +13 -29
- 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 +10 -10
- ansible/modules/lineinfile.py +5 -5
- ansible/modules/meta.py +4 -4
- ansible/modules/mount_facts.py +2 -2
- ansible/modules/package.py +4 -4
- ansible/modules/package_facts.py +22 -10
- ansible/modules/pause.py +6 -6
- ansible/modules/ping.py +6 -6
- ansible/modules/pip.py +10 -11
- ansible/modules/raw.py +4 -4
- ansible/modules/reboot.py +6 -6
- ansible/modules/replace.py +9 -13
- ansible/modules/rpm_key.py +7 -8
- ansible/modules/script.py +4 -4
- ansible/modules/service.py +7 -8
- 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 +6 -6
- ansible/modules/stat.py +9 -23
- ansible/modules/subversion.py +15 -15
- ansible/modules/systemd.py +6 -6
- ansible/modules/systemd_service.py +6 -6
- ansible/modules/sysvinit.py +6 -6
- ansible/modules/tempfile.py +5 -6
- ansible/modules/template.py +6 -6
- ansible/modules/unarchive.py +32 -11
- ansible/modules/uri.py +33 -26
- ansible/modules/user.py +53 -34
- ansible/modules/validate_argument_spec.py +10 -7
- ansible/modules/wait_for.py +32 -27
- ansible/modules/wait_for_connection.py +6 -6
- ansible/modules/yum_repository.py +6 -6
- ansible/parsing/ajson.py +14 -32
- ansible/parsing/dataloader.py +99 -54
- ansible/parsing/mod_args.py +28 -44
- 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 +319 -87
- 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 +182 -132
- 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 +31 -27
- ansible/playbook/loop_control.py +2 -2
- ansible/playbook/play.py +85 -44
- ansible/playbook/play_context.py +12 -17
- ansible/playbook/playbook_include.py +14 -15
- ansible/playbook/role/__init__.py +24 -26
- 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 +13 -8
- ansible/playbook/task.py +188 -118
- ansible/playbook/task_include.py +5 -5
- ansible/plugins/__init__.py +68 -21
- ansible/plugins/action/__init__.py +209 -176
- ansible/plugins/action/add_host.py +1 -1
- ansible/plugins/action/assemble.py +1 -1
- ansible/plugins/action/assert.py +54 -66
- ansible/plugins/action/copy.py +7 -11
- 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 +4 -5
- ansible/plugins/action/gather_facts.py +7 -6
- ansible/plugins/action/group_by.py +1 -1
- ansible/plugins/action/include_vars.py +10 -11
- ansible/plugins/action/package.py +3 -6
- ansible/plugins/action/pause.py +2 -2
- ansible/plugins/action/script.py +15 -8
- ansible/plugins/action/service.py +6 -11
- ansible/plugins/action/set_fact.py +3 -12
- ansible/plugins/action/set_stats.py +3 -8
- ansible/plugins/action/template.py +35 -59
- ansible/plugins/action/unarchive.py +1 -1
- 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 +35 -44
- 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 +284 -179
- ansible/plugins/callback/default.py +99 -92
- ansible/plugins/callback/junit.py +44 -39
- ansible/plugins/callback/minimal.py +28 -25
- ansible/plugins/callback/oneline.py +28 -21
- ansible/plugins/callback/tree.py +16 -11
- ansible/plugins/connection/__init__.py +47 -34
- ansible/plugins/connection/local.py +150 -54
- ansible/plugins/connection/paramiko_ssh.py +21 -18
- ansible/plugins/connection/psrp.py +76 -165
- ansible/plugins/connection/ssh.py +301 -78
- ansible/plugins/connection/winrm.py +58 -140
- 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 +6 -2
- 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 +225 -108
- ansible/plugins/filter/encryption.py +32 -32
- 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/regex_search.yml +1 -4
- ansible/plugins/filter/split.yml +1 -1
- ansible/plugins/filter/to_nice_yaml.yml +0 -4
- 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 +97 -77
- 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 +189 -119
- ansible/plugins/inventory/toml.py +16 -126
- ansible/plugins/inventory/yaml.py +10 -8
- ansible/plugins/list.py +3 -3
- ansible/plugins/loader.py +197 -82
- ansible/plugins/lookup/__init__.py +21 -4
- ansible/plugins/lookup/config.py +21 -35
- ansible/plugins/lookup/csvfile.py +3 -2
- 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 +86 -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 +42 -36
- ansible/plugins/lookup/together.py +0 -12
- ansible/plugins/lookup/unvault.py +1 -5
- ansible/plugins/lookup/url.py +2 -8
- ansible/plugins/lookup/vars.py +16 -24
- ansible/plugins/shell/__init__.py +2 -2
- ansible/plugins/shell/cmd.py +2 -2
- ansible/plugins/shell/powershell.py +39 -22
- ansible/plugins/shell/sh.py +3 -2
- ansible/plugins/strategy/__init__.py +159 -184
- 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 +55 -21
- ansible/plugins/test/files.py +1 -1
- ansible/plugins/test/mathstuff.py +3 -3
- ansible/plugins/test/uri.py +3 -3
- ansible/plugins/vars/host_group_vars.py +7 -14
- ansible/release.py +2 -2
- ansible/template/__init__.py +370 -944
- ansible/utils/__init__.py +0 -18
- ansible/utils/_ssh_agent.py +657 -0
- ansible/utils/collection_loader/__init__.py +52 -5
- ansible/utils/collection_loader/_collection_config.py +5 -6
- ansible/utils/collection_loader/_collection_finder.py +79 -93
- ansible/utils/collection_loader/_collection_meta.py +13 -8
- ansible/utils/display.py +433 -63
- ansible/utils/encrypt.py +27 -19
- ansible/utils/fqcn.py +2 -2
- ansible/utils/hashing.py +2 -2
- ansible/utils/helpers.py +2 -2
- ansible/utils/listify.py +8 -8
- ansible/utils/lock.py +2 -2
- ansible/utils/path.py +4 -4
- ansible/utils/plugin_docs.py +14 -13
- ansible/utils/sentinel.py +4 -62
- ansible/utils/singleton.py +2 -0
- ansible/utils/ssh_functions.py +1 -1
- ansible/utils/unsafe_proxy.py +23 -332
- ansible/utils/vars.py +51 -8
- ansible/utils/version.py +2 -2
- ansible/vars/clean.py +5 -5
- ansible/vars/hostvars.py +60 -90
- ansible/vars/manager.py +206 -282
- ansible/vars/reserved.py +8 -9
- ansible_core-2.19.0b2.dist-info/BSD-3-Clause.txt +28 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/METADATA +5 -4
- ansible_core-2.19.0b2.dist-info/RECORD +1072 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/WHEEL +1 -1
- 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 +1 -1
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- 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 +1 -0
- ansible_test/_internal/ansible_util.py +2 -0
- 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 -0
- 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 +1 -0
- 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 +2 -1
- 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 +4 -0
- 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 +2 -1
- 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 +1 -0
- 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 +16 -1
- 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 +1 -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 +24 -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 +1 -0
- ansible_test/_internal/commands/units/__init__.py +1 -0
- 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 +2 -0
- 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/delegation.py +1 -0
- 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 +1 -0
- ansible_test/_internal/http.py +1 -0
- ansible_test/_internal/init.py +1 -0
- ansible_test/_internal/inventory.py +35 -3
- ansible_test/_internal/io.py +1 -0
- ansible_test/_internal/metadata.py +1 -0
- ansible_test/_internal/payload.py +1 -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 +1 -0
- ansible_test/_internal/pypi_proxy.py +6 -5
- ansible_test/_internal/python_requirements.py +1 -0
- ansible_test/_internal/ssh.py +1 -0
- ansible_test/_internal/target.py +1 -0
- ansible_test/_internal/test.py +3 -2
- ansible_test/_internal/thread.py +1 -0
- ansible_test/_internal/timeout.py +1 -0
- ansible_test/_internal/util.py +1 -0
- ansible_test/_internal/util_common.py +5 -2
- 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 +7 -5
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +7 -5
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +7 -5
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +3 -5
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +7 -7
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -13
- 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/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 +35 -27
- 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.5rc1.dist-info/RECORD +0 -992
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/Apache-License.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/COPYING +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/MIT-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/PSF-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/simplified_bsd.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import datetime as _datetime
|
4
|
+
|
5
|
+
from ansible.module_utils._internal import _datatag
|
6
|
+
from ansible.module_utils._internal._json import _profiles
|
7
|
+
from ansible.parsing import vault as _vault
|
8
|
+
from ansible._internal._datatag import _tags
|
9
|
+
|
10
|
+
|
11
|
+
class _Profile(_profiles._JSONSerializationProfile):
|
12
|
+
"""Profile for external cache persistence of inventory/fact data that preserves most tags."""
|
13
|
+
|
14
|
+
serialize_map = {}
|
15
|
+
schema_id = 1
|
16
|
+
|
17
|
+
@classmethod
|
18
|
+
def post_init(cls, **kwargs):
|
19
|
+
cls.allowed_ansible_serializable_types = (
|
20
|
+
_profiles._common_module_types
|
21
|
+
| _profiles._common_module_response_types
|
22
|
+
| {
|
23
|
+
_datatag._AnsibleTaggedDate,
|
24
|
+
_datatag._AnsibleTaggedTime,
|
25
|
+
_datatag._AnsibleTaggedDateTime,
|
26
|
+
_datatag._AnsibleTaggedStr,
|
27
|
+
_datatag._AnsibleTaggedInt,
|
28
|
+
_datatag._AnsibleTaggedFloat,
|
29
|
+
_datatag._AnsibleTaggedList,
|
30
|
+
_datatag._AnsibleTaggedSet,
|
31
|
+
_datatag._AnsibleTaggedTuple,
|
32
|
+
_datatag._AnsibleTaggedDict,
|
33
|
+
_tags.SourceWasEncrypted,
|
34
|
+
_tags.Origin,
|
35
|
+
_tags.TrustedAsTemplate,
|
36
|
+
_vault.EncryptedString,
|
37
|
+
_vault.VaultedValue,
|
38
|
+
}
|
39
|
+
)
|
40
|
+
|
41
|
+
cls.serialize_map = {
|
42
|
+
set: cls.serialize_as_list,
|
43
|
+
tuple: cls.serialize_as_list,
|
44
|
+
_datetime.date: _datatag.AnsibleSerializableDate,
|
45
|
+
_datetime.time: _datatag.AnsibleSerializableTime,
|
46
|
+
_datetime.datetime: _datatag.AnsibleSerializableDateTime,
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
class Encoder(_profiles.AnsibleProfileJSONEncoder):
|
51
|
+
_profile = _Profile
|
52
|
+
|
53
|
+
|
54
|
+
class Decoder(_profiles.AnsibleProfileJSONDecoder):
|
55
|
+
_profile = _Profile
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""
|
2
|
+
Backwards compatibility profile for serialization for persisted ansible-inventory output.
|
3
|
+
Behavior is equivalent to pre 2.18 `AnsibleJSONEncoder` with vault_to_text=True.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from __future__ import annotations
|
7
|
+
|
8
|
+
from ... import _json
|
9
|
+
from . import _legacy
|
10
|
+
|
11
|
+
|
12
|
+
class _InventoryVariableVisitor(_legacy._LegacyVariableVisitor, _json.StateTrackingMixIn):
|
13
|
+
"""State-tracking visitor implementation that only applies trust to `_meta.hostvars` and `vars` inventory values."""
|
14
|
+
|
15
|
+
# DTFIX-RELEASE: does the variable visitor need to support conversion of sequence/mapping for inventory?
|
16
|
+
|
17
|
+
@property
|
18
|
+
def _allow_trust(self) -> bool:
|
19
|
+
stack = self._get_stack()
|
20
|
+
|
21
|
+
if len(stack) >= 4 and stack[:2] == ['_meta', 'hostvars']:
|
22
|
+
return True
|
23
|
+
|
24
|
+
if len(stack) >= 3 and stack[1] == 'vars':
|
25
|
+
return True
|
26
|
+
|
27
|
+
return False
|
28
|
+
|
29
|
+
|
30
|
+
class _Profile(_legacy._Profile):
|
31
|
+
visitor_type = _InventoryVariableVisitor
|
32
|
+
encode_strings_as_utf8 = True
|
33
|
+
|
34
|
+
|
35
|
+
class Encoder(_legacy.Encoder):
|
36
|
+
_profile = _Profile
|
37
|
+
|
38
|
+
|
39
|
+
class Decoder(_legacy.Decoder):
|
40
|
+
_profile = _Profile
|
@@ -0,0 +1,197 @@
|
|
1
|
+
"""
|
2
|
+
Backwards compatibility profile for serialization other than inventory (which should use inventory_legacy for backward-compatible trust behavior).
|
3
|
+
Behavior is equivalent to pre 2.18 `AnsibleJSONEncoder` with vault_to_text=True.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from __future__ import annotations as _annotations
|
7
|
+
|
8
|
+
import datetime as _datetime
|
9
|
+
import typing as _t
|
10
|
+
|
11
|
+
from ansible._internal import _json
|
12
|
+
from ansible._internal._datatag import _tags
|
13
|
+
from ansible.module_utils._internal import _datatag
|
14
|
+
from ansible.module_utils._internal._json import _profiles
|
15
|
+
from ansible.parsing import vault as _vault
|
16
|
+
|
17
|
+
|
18
|
+
class _Untrusted:
|
19
|
+
"""
|
20
|
+
Temporarily wraps strings which are not trusted for templating.
|
21
|
+
Used before serialization of strings not tagged TrustedAsTemplate when trust inversion is enabled and trust is allowed in the string's context.
|
22
|
+
Used during deserialization of `__ansible_unsafe` strings to indicate they should not be tagged TrustedAsTemplate.
|
23
|
+
"""
|
24
|
+
|
25
|
+
__slots__ = ('value',)
|
26
|
+
|
27
|
+
def __init__(self, value: str) -> None:
|
28
|
+
self.value = value
|
29
|
+
|
30
|
+
|
31
|
+
class _LegacyVariableVisitor(_json.AnsibleVariableVisitor):
|
32
|
+
"""Variable visitor that supports optional trust inversion for legacy serialization."""
|
33
|
+
|
34
|
+
def __init__(
|
35
|
+
self,
|
36
|
+
*,
|
37
|
+
trusted_as_template: bool = False,
|
38
|
+
invert_trust: bool = False,
|
39
|
+
origin: _tags.Origin | None = None,
|
40
|
+
convert_mapping_to_dict: bool = False,
|
41
|
+
convert_sequence_to_list: bool = False,
|
42
|
+
convert_custom_scalars: bool = False,
|
43
|
+
):
|
44
|
+
super().__init__(
|
45
|
+
trusted_as_template=trusted_as_template,
|
46
|
+
origin=origin,
|
47
|
+
convert_mapping_to_dict=convert_mapping_to_dict,
|
48
|
+
convert_sequence_to_list=convert_sequence_to_list,
|
49
|
+
convert_custom_scalars=convert_custom_scalars,
|
50
|
+
encrypted_string_behavior=_json.EncryptedStringBehavior.PRESERVE,
|
51
|
+
)
|
52
|
+
|
53
|
+
self.invert_trust = invert_trust
|
54
|
+
|
55
|
+
if trusted_as_template and invert_trust:
|
56
|
+
raise ValueError('trusted_as_template is mutually exclusive with invert_trust')
|
57
|
+
|
58
|
+
@property
|
59
|
+
def _allow_trust(self) -> bool:
|
60
|
+
"""
|
61
|
+
This profile supports trust application in all contexts.
|
62
|
+
Derived implementations can override this behavior for application-dependent/schema-aware trust.
|
63
|
+
"""
|
64
|
+
return True
|
65
|
+
|
66
|
+
def _early_visit(self, value, value_type) -> _t.Any:
|
67
|
+
"""Similar to base implementation, but supports an intermediate wrapper for trust inversion."""
|
68
|
+
if value_type in (str, _datatag._AnsibleTaggedStr):
|
69
|
+
# apply compatibility behavior
|
70
|
+
if self.trusted_as_template and self._allow_trust:
|
71
|
+
result = _tags.TrustedAsTemplate().tag(value)
|
72
|
+
elif self.invert_trust and not _tags.TrustedAsTemplate.is_tagged_on(value) and self._allow_trust:
|
73
|
+
result = _Untrusted(value)
|
74
|
+
else:
|
75
|
+
result = value
|
76
|
+
elif value_type is _Untrusted:
|
77
|
+
result = value.value
|
78
|
+
else:
|
79
|
+
result = _json._sentinel
|
80
|
+
|
81
|
+
return result
|
82
|
+
|
83
|
+
|
84
|
+
class _Profile(_profiles._JSONSerializationProfile["Encoder", "Decoder"]):
|
85
|
+
visitor_type = _LegacyVariableVisitor
|
86
|
+
|
87
|
+
@classmethod
|
88
|
+
def serialize_untrusted(cls, value: _Untrusted) -> dict[str, str] | str:
|
89
|
+
return dict(
|
90
|
+
__ansible_unsafe=_datatag.AnsibleTagHelper.untag(value.value),
|
91
|
+
)
|
92
|
+
|
93
|
+
@classmethod
|
94
|
+
def serialize_tagged_str(cls, value: _datatag.AnsibleTaggedObject) -> _t.Any:
|
95
|
+
if ciphertext := _vault.VaultHelper.get_ciphertext(value, with_tags=False):
|
96
|
+
return dict(
|
97
|
+
__ansible_vault=ciphertext,
|
98
|
+
)
|
99
|
+
|
100
|
+
return _datatag.AnsibleTagHelper.untag(value)
|
101
|
+
|
102
|
+
@classmethod
|
103
|
+
def deserialize_unsafe(cls, value: dict[str, _t.Any]) -> _Untrusted:
|
104
|
+
ansible_unsafe = value['__ansible_unsafe']
|
105
|
+
|
106
|
+
if type(ansible_unsafe) is not str: # pylint: disable=unidiomatic-typecheck
|
107
|
+
raise TypeError(f"__ansible_unsafe is {type(ansible_unsafe)} not {str}")
|
108
|
+
|
109
|
+
return _Untrusted(ansible_unsafe)
|
110
|
+
|
111
|
+
@classmethod
|
112
|
+
def deserialize_vault(cls, value: dict[str, _t.Any]) -> _vault.EncryptedString:
|
113
|
+
ansible_vault = value['__ansible_vault']
|
114
|
+
|
115
|
+
if type(ansible_vault) is not str: # pylint: disable=unidiomatic-typecheck
|
116
|
+
raise TypeError(f"__ansible_vault is {type(ansible_vault)} not {str}")
|
117
|
+
|
118
|
+
encrypted_string = _vault.EncryptedString(ciphertext=ansible_vault)
|
119
|
+
|
120
|
+
return encrypted_string
|
121
|
+
|
122
|
+
@classmethod
|
123
|
+
def serialize_encrypted_string(cls, value: _vault.EncryptedString) -> dict[str, str]:
|
124
|
+
return dict(
|
125
|
+
__ansible_vault=_vault.VaultHelper.get_ciphertext(value, with_tags=False),
|
126
|
+
)
|
127
|
+
|
128
|
+
@classmethod
|
129
|
+
def post_init(cls) -> None:
|
130
|
+
cls.serialize_map = {
|
131
|
+
set: cls.serialize_as_list,
|
132
|
+
tuple: cls.serialize_as_list,
|
133
|
+
_datetime.date: cls.serialize_as_isoformat, # existing devel behavior
|
134
|
+
_datetime.time: cls.serialize_as_isoformat, # always failed pre-2.18, so okay to include for consistency
|
135
|
+
_datetime.datetime: cls.serialize_as_isoformat, # existing devel behavior
|
136
|
+
_datatag._AnsibleTaggedDate: cls.discard_tags,
|
137
|
+
_datatag._AnsibleTaggedTime: cls.discard_tags,
|
138
|
+
_datatag._AnsibleTaggedDateTime: cls.discard_tags,
|
139
|
+
_vault.EncryptedString: cls.serialize_encrypted_string,
|
140
|
+
_datatag._AnsibleTaggedStr: cls.serialize_tagged_str, # for VaultedValue tagged str
|
141
|
+
_datatag._AnsibleTaggedInt: cls.discard_tags,
|
142
|
+
_datatag._AnsibleTaggedFloat: cls.discard_tags,
|
143
|
+
_datatag._AnsibleTaggedList: cls.discard_tags,
|
144
|
+
_datatag._AnsibleTaggedSet: cls.discard_tags,
|
145
|
+
_datatag._AnsibleTaggedTuple: cls.discard_tags,
|
146
|
+
_datatag._AnsibleTaggedDict: cls.discard_tags,
|
147
|
+
_Untrusted: cls.serialize_untrusted, # equivalent to AnsibleJSONEncoder(preprocess_unsafe=True) in devel
|
148
|
+
}
|
149
|
+
|
150
|
+
cls.deserialize_map = {
|
151
|
+
'__ansible_unsafe': cls.deserialize_unsafe,
|
152
|
+
'__ansible_vault': cls.deserialize_vault,
|
153
|
+
}
|
154
|
+
|
155
|
+
@classmethod
|
156
|
+
def pre_serialize(cls, encoder: Encoder, o: _t.Any) -> _t.Any:
|
157
|
+
# DTFIX-RELEASE: these conversion args probably aren't needed
|
158
|
+
avv = cls.visitor_type(invert_trust=True, convert_mapping_to_dict=True, convert_sequence_to_list=True, convert_custom_scalars=True)
|
159
|
+
|
160
|
+
return avv.visit(o)
|
161
|
+
|
162
|
+
@classmethod
|
163
|
+
def post_deserialize(cls, decoder: Decoder, o: _t.Any) -> _t.Any:
|
164
|
+
avv = cls.visitor_type(trusted_as_template=decoder._trusted_as_template, origin=decoder._origin)
|
165
|
+
|
166
|
+
return avv.visit(o)
|
167
|
+
|
168
|
+
@classmethod
|
169
|
+
def handle_key(cls, k: _t.Any) -> _t.Any:
|
170
|
+
if isinstance(k, str):
|
171
|
+
return k
|
172
|
+
|
173
|
+
# DTFIX-RELEASE: decide if this is a deprecation warning, error, or what?
|
174
|
+
# Non-string variable names have been disallowed by set_fact and other things since at least 2021.
|
175
|
+
# DTFIX-RELEASE: document why this behavior is here, also verify the legacy tagless use case doesn't need this same behavior
|
176
|
+
return str(k)
|
177
|
+
|
178
|
+
|
179
|
+
class Encoder(_profiles.AnsibleProfileJSONEncoder):
|
180
|
+
_profile = _Profile
|
181
|
+
|
182
|
+
|
183
|
+
class Decoder(_profiles.AnsibleProfileJSONDecoder):
|
184
|
+
_profile = _Profile
|
185
|
+
|
186
|
+
def __init__(self, **kwargs) -> None:
|
187
|
+
super().__init__(**kwargs)
|
188
|
+
|
189
|
+
# NB: these can only be sampled properly when loading strings, eg, `json.loads`; the global `json.load` function does not expose the file-like to us
|
190
|
+
self._origin: _tags.Origin | None = None
|
191
|
+
self._trusted_as_template: bool = False
|
192
|
+
|
193
|
+
def raw_decode(self, s: str, idx: int = 0) -> tuple[_t.Any, int]:
|
194
|
+
self._origin = _tags.Origin.get_tag(s)
|
195
|
+
self._trusted_as_template = _tags.TrustedAsTemplate.is_tagged_on(s)
|
196
|
+
|
197
|
+
return super().raw_decode(s, idx)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import contextlib
|
4
|
+
import fcntl
|
5
|
+
import typing as t
|
6
|
+
|
7
|
+
|
8
|
+
@contextlib.contextmanager
|
9
|
+
def named_mutex(path: str) -> t.Iterator[None]:
|
10
|
+
"""
|
11
|
+
Lightweight context manager wrapper over `fcntl.flock` to provide IPC locking via a shared filename.
|
12
|
+
Entering the context manager blocks until the lock is acquired.
|
13
|
+
The lock file will be created automatically, but creation of the parent directory and deletion of the lockfile are the caller's responsibility.
|
14
|
+
"""
|
15
|
+
with open(path, 'a') as file:
|
16
|
+
fcntl.flock(file, fcntl.LOCK_EX)
|
17
|
+
|
18
|
+
try:
|
19
|
+
yield
|
20
|
+
finally:
|
21
|
+
fcntl.flock(file, fcntl.LOCK_UN)
|
File without changes
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import functools
|
4
|
+
import json
|
5
|
+
import json.encoder
|
6
|
+
import json.decoder
|
7
|
+
import typing as t
|
8
|
+
|
9
|
+
from .._wrapt import ObjectProxy
|
10
|
+
from .._json._profiles import _cache_persistence
|
11
|
+
|
12
|
+
|
13
|
+
class PluginInterposer(ObjectProxy):
|
14
|
+
"""Proxies a Cache plugin instance to implement transparent encapsulation of serialized Ansible internal data types."""
|
15
|
+
|
16
|
+
_PAYLOAD_KEY = '__payload__'
|
17
|
+
"""The key used to store the serialized payload."""
|
18
|
+
|
19
|
+
def get(self, key: str) -> dict[str, object]:
|
20
|
+
return self._decode(self.__wrapped__.get(self._get_key(key)))
|
21
|
+
|
22
|
+
def set(self, key: str, value: dict[str, object]) -> None:
|
23
|
+
self.__wrapped__.set(self._get_key(key), self._encode(value))
|
24
|
+
|
25
|
+
def keys(self) -> t.Sequence[str]:
|
26
|
+
return [k for k in (self._restore_key(k) for k in self.__wrapped__.keys()) if k is not None]
|
27
|
+
|
28
|
+
def contains(self, key: t.Any) -> bool:
|
29
|
+
return self.__wrapped__.contains(self._get_key(key))
|
30
|
+
|
31
|
+
def delete(self, key: str) -> None:
|
32
|
+
self.__wrapped__.delete(self._get_key(key))
|
33
|
+
|
34
|
+
@classmethod
|
35
|
+
def _restore_key(cls, wrapped_key: str) -> str | None:
|
36
|
+
prefix = cls._get_wrapped_key_prefix()
|
37
|
+
|
38
|
+
if not wrapped_key.startswith(prefix):
|
39
|
+
return None
|
40
|
+
|
41
|
+
return wrapped_key[len(prefix) :]
|
42
|
+
|
43
|
+
@classmethod
|
44
|
+
@functools.cache
|
45
|
+
def _get_wrapped_key_prefix(cls) -> str:
|
46
|
+
return f's{_cache_persistence._Profile.schema_id}_'
|
47
|
+
|
48
|
+
@classmethod
|
49
|
+
def _get_key(cls, key: str) -> str:
|
50
|
+
"""Augment the supplied key with a schema identifier to allow for side-by-side caching across incompatible schemas."""
|
51
|
+
return f'{cls._get_wrapped_key_prefix()}{key}'
|
52
|
+
|
53
|
+
def _encode(self, value: dict[str, object]) -> dict[str, object]:
|
54
|
+
return {self._PAYLOAD_KEY: json.dumps(value, cls=_cache_persistence.Encoder)}
|
55
|
+
|
56
|
+
def _decode(self, value: dict[str, t.Any]) -> dict[str, object]:
|
57
|
+
return json.loads(value[self._PAYLOAD_KEY], cls=_cache_persistence.Decoder)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import dataclasses
|
4
|
+
import typing as t
|
5
|
+
|
6
|
+
from collections import abc as c
|
7
|
+
|
8
|
+
from ansible import constants
|
9
|
+
from ansible._internal._templating import _engine
|
10
|
+
from ansible._internal._templating._chain_templar import ChainTemplar
|
11
|
+
from ansible.errors import AnsibleError
|
12
|
+
from ansible.module_utils._internal._ambient_context import AmbientContextBase
|
13
|
+
from ansible.module_utils.datatag import native_type_name
|
14
|
+
from ansible.parsing import vault as _vault
|
15
|
+
from ansible.utils.display import Display
|
16
|
+
|
17
|
+
if t.TYPE_CHECKING:
|
18
|
+
from ansible.playbook.task import Task
|
19
|
+
|
20
|
+
|
21
|
+
@dataclasses.dataclass
|
22
|
+
class TaskContext(AmbientContextBase):
|
23
|
+
"""Ambient context that wraps task execution on workers. It provides access to the currently executing task."""
|
24
|
+
|
25
|
+
task: Task
|
26
|
+
|
27
|
+
|
28
|
+
TaskArgsFinalizerCallback = t.Callable[[str, t.Any, _engine.TemplateEngine, t.Any], t.Any]
|
29
|
+
"""Type alias for the shape of the `ActionBase.finalize_task_arg` method."""
|
30
|
+
|
31
|
+
|
32
|
+
class TaskArgsChainTemplar(ChainTemplar):
|
33
|
+
"""
|
34
|
+
A ChainTemplar that carries a user-provided context object, optionally provided by `ActionBase.get_finalize_task_args_context`.
|
35
|
+
TaskArgsFinalizer provides the context to each `ActionBase.finalize_task_arg` call to allow for more complex/stateful customization.
|
36
|
+
"""
|
37
|
+
|
38
|
+
def __init__(self, *sources: c.Mapping, templar: _engine.TemplateEngine, callback: TaskArgsFinalizerCallback, context: t.Any) -> None:
|
39
|
+
super().__init__(*sources, templar=templar)
|
40
|
+
|
41
|
+
self.callback = callback
|
42
|
+
self.context = context
|
43
|
+
|
44
|
+
def template(self, key: t.Any, value: t.Any) -> t.Any:
|
45
|
+
return self.callback(key, value, self.templar, self.context)
|
46
|
+
|
47
|
+
|
48
|
+
class TaskArgsFinalizer:
|
49
|
+
"""Invoked during task args finalization; allows actions to override default arg processing (e.g., templating)."""
|
50
|
+
|
51
|
+
def __init__(self, *args: c.Mapping[str, t.Any] | str | None, templar: _engine.TemplateEngine) -> None:
|
52
|
+
self._args_layers = [arg for arg in args if arg is not None]
|
53
|
+
self._templar = templar
|
54
|
+
|
55
|
+
def finalize(self, callback: TaskArgsFinalizerCallback, context: t.Any) -> dict[str, t.Any]:
|
56
|
+
resolved_layers: list[c.Mapping[str, t.Any]] = []
|
57
|
+
|
58
|
+
for layer in self._args_layers:
|
59
|
+
if isinstance(layer, (str, _vault.EncryptedString)): # EncryptedString can hide a template
|
60
|
+
if constants.config.get_config_value('INJECT_FACTS_AS_VARS'):
|
61
|
+
Display().warning(
|
62
|
+
"Using a template for task args is unsafe in some situations "
|
63
|
+
"(see https://docs.ansible.com/ansible/devel/reference_appendices/faq.html#argsplat-unsafe).",
|
64
|
+
obj=layer,
|
65
|
+
)
|
66
|
+
|
67
|
+
resolved_layer = self._templar.resolve_to_container(layer, options=_engine.TemplateOptions(value_for_omit={}))
|
68
|
+
else:
|
69
|
+
resolved_layer = layer
|
70
|
+
|
71
|
+
if not isinstance(resolved_layer, dict):
|
72
|
+
raise AnsibleError(f'Task args must resolve to a {native_type_name(dict)!r} not {native_type_name(resolved_layer)!r}.', obj=layer)
|
73
|
+
|
74
|
+
resolved_layers.append(resolved_layer)
|
75
|
+
|
76
|
+
ct = TaskArgsChainTemplar(*reversed(resolved_layers), templar=self._templar, callback=callback, context=context)
|
77
|
+
|
78
|
+
return ct.as_dict()
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from jinja2 import __version__ as _jinja2_version
|
4
|
+
|
5
|
+
# DTFIX-FUTURE: sanity test to ensure this doesn't drift from requirements
|
6
|
+
_MINIMUM_JINJA_VERSION = (3, 1)
|
7
|
+
_CURRENT_JINJA_VERSION = tuple(map(int, _jinja2_version.split('.', maxsplit=2)[:2]))
|
8
|
+
|
9
|
+
if _CURRENT_JINJA_VERSION < _MINIMUM_JINJA_VERSION:
|
10
|
+
raise RuntimeError(f'Jinja version {".".join(map(str, _MINIMUM_JINJA_VERSION))} or higher is required (current version {_jinja2_version}).')
|
@@ -0,0 +1,86 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import abc
|
4
|
+
import typing as t
|
5
|
+
|
6
|
+
from contextvars import ContextVar
|
7
|
+
|
8
|
+
from ansible.module_utils._internal._datatag import AnsibleTagHelper
|
9
|
+
|
10
|
+
|
11
|
+
class NotifiableAccessContextBase(metaclass=abc.ABCMeta):
|
12
|
+
"""Base class for a context manager that, when active, receives notification of managed access for types/tags in which it has registered an interest."""
|
13
|
+
|
14
|
+
_type_interest: t.FrozenSet[type] = frozenset()
|
15
|
+
"""Set of types (including tag types) for which this context will be notified upon access."""
|
16
|
+
|
17
|
+
_mask: t.ClassVar[bool] = False
|
18
|
+
"""When true, only the innermost (most recently created) context of this type will be notified."""
|
19
|
+
|
20
|
+
def __enter__(self):
|
21
|
+
# noinspection PyProtectedMember
|
22
|
+
AnsibleAccessContext.current()._register_interest(self)
|
23
|
+
return self
|
24
|
+
|
25
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
26
|
+
# noinspection PyProtectedMember
|
27
|
+
AnsibleAccessContext.current()._unregister_interest(self)
|
28
|
+
return None
|
29
|
+
|
30
|
+
@abc.abstractmethod
|
31
|
+
def _notify(self, o: t.Any) -> t.Any:
|
32
|
+
"""Derived classes implement custom notification behavior when a registered type or tag is accessed."""
|
33
|
+
|
34
|
+
|
35
|
+
class AnsibleAccessContext:
|
36
|
+
"""
|
37
|
+
Broker object for managed access registration and notification.
|
38
|
+
Each thread or other logical callstack has a dedicated `AnsibleAccessContext` object with which `NotifiableAccessContext` objects can register interest.
|
39
|
+
When a managed access occurs on an object, each active `NotifiableAccessContext` within the current callstack that has registered interest in that
|
40
|
+
object's type or a tag present on it will be notified.
|
41
|
+
"""
|
42
|
+
|
43
|
+
_contextvar: t.ClassVar[ContextVar[AnsibleAccessContext]] = ContextVar('AnsibleAccessContext')
|
44
|
+
|
45
|
+
@staticmethod
|
46
|
+
def current() -> AnsibleAccessContext:
|
47
|
+
"""Creates or retrieves an `AnsibleAccessContext` for the current logical callstack."""
|
48
|
+
try:
|
49
|
+
ctx: AnsibleAccessContext = AnsibleAccessContext._contextvar.get()
|
50
|
+
except LookupError:
|
51
|
+
# didn't exist; create it
|
52
|
+
ctx = AnsibleAccessContext()
|
53
|
+
AnsibleAccessContext._contextvar.set(ctx) # we ignore the token, since this should live for the life of the thread/async ctx
|
54
|
+
|
55
|
+
return ctx
|
56
|
+
|
57
|
+
def __init__(self) -> None:
|
58
|
+
self._notify_contexts: list[NotifiableAccessContextBase] = []
|
59
|
+
|
60
|
+
def _register_interest(self, context: NotifiableAccessContextBase) -> None:
|
61
|
+
self._notify_contexts.append(context)
|
62
|
+
|
63
|
+
def _unregister_interest(self, context: NotifiableAccessContextBase) -> None:
|
64
|
+
ctx = self._notify_contexts.pop()
|
65
|
+
|
66
|
+
if ctx is not context:
|
67
|
+
raise RuntimeError(f'Out-of-order context deactivation detected. Found {ctx} instead of {context}.')
|
68
|
+
|
69
|
+
def access(self, value: t.Any) -> None:
|
70
|
+
"""Notify all contexts which have registered interest in the given value that it is being accessed."""
|
71
|
+
if not self._notify_contexts:
|
72
|
+
return
|
73
|
+
|
74
|
+
value_types = AnsibleTagHelper.tag_types(value) | frozenset((type(value),))
|
75
|
+
masked: set[type] = set()
|
76
|
+
|
77
|
+
for ctx in reversed(self._notify_contexts):
|
78
|
+
if ctx._mask:
|
79
|
+
if (ctx_type := type(ctx)) in masked:
|
80
|
+
continue
|
81
|
+
|
82
|
+
masked.add(ctx_type)
|
83
|
+
|
84
|
+
# noinspection PyProtectedMember
|
85
|
+
if ctx._type_interest.intersection(value_types):
|
86
|
+
ctx._notify(value)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import collections.abc as c
|
4
|
+
import itertools
|
5
|
+
import typing as t
|
6
|
+
|
7
|
+
from ansible.errors import AnsibleValueOmittedError, AnsibleError
|
8
|
+
|
9
|
+
from ._engine import TemplateEngine
|
10
|
+
|
11
|
+
|
12
|
+
class ChainTemplar:
|
13
|
+
"""A basic variable layering mechanism that supports templating and obliteration of `omit` values."""
|
14
|
+
|
15
|
+
def __init__(self, *sources: c.Mapping, templar: TemplateEngine) -> None:
|
16
|
+
self.sources = sources
|
17
|
+
self.templar = templar
|
18
|
+
|
19
|
+
def template(self, key: t.Any, value: t.Any) -> t.Any:
|
20
|
+
"""
|
21
|
+
Render the given value using the templar.
|
22
|
+
Intended to be overridden by subclasses.
|
23
|
+
"""
|
24
|
+
return self.templar.template(value)
|
25
|
+
|
26
|
+
def get(self, key: t.Any) -> t.Any:
|
27
|
+
"""Get the value for the given key, templating the result before returning it."""
|
28
|
+
for source in self.sources:
|
29
|
+
if key not in source:
|
30
|
+
continue
|
31
|
+
|
32
|
+
value = source[key]
|
33
|
+
|
34
|
+
try:
|
35
|
+
return self.template(key, value)
|
36
|
+
except AnsibleValueOmittedError:
|
37
|
+
break # omit == obliterate - matches historical behavior where dict layers were squashed before templating was applied
|
38
|
+
except Exception as ex:
|
39
|
+
raise AnsibleError(f'Error while resolving value for {key!r}.', obj=value) from ex
|
40
|
+
|
41
|
+
raise KeyError(key)
|
42
|
+
|
43
|
+
def keys(self) -> t.Iterable[t.Any]:
|
44
|
+
"""
|
45
|
+
Returns a sorted iterable of all keys present in all source layers, without templating associated values.
|
46
|
+
Values that resolve to `omit` are thus included.
|
47
|
+
"""
|
48
|
+
return sorted(set(itertools.chain.from_iterable(self.sources)))
|
49
|
+
|
50
|
+
def items(self) -> t.Iterable[t.Tuple[t.Any, t.Any]]:
|
51
|
+
"""
|
52
|
+
Returns a sorted iterable of (key, templated value) tuples.
|
53
|
+
Any tuple where the templated value resolves to `omit` will not be included in the result.
|
54
|
+
"""
|
55
|
+
for key in self.keys():
|
56
|
+
try:
|
57
|
+
yield key, self.get(key)
|
58
|
+
except KeyError:
|
59
|
+
pass
|
60
|
+
|
61
|
+
def as_dict(self) -> dict[t.Any, t.Any]:
|
62
|
+
"""Returns a dict representing all layers, squashed and templated, with `omit` values dropped."""
|
63
|
+
return dict(self.items())
|