ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b6__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 +1 -1
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
- ansible/_internal/_collection_proxy.py +1 -1
- ansible/_internal/_errors/_alarm_timeout.py +66 -0
- ansible/_internal/_errors/_captured.py +25 -30
- ansible/_internal/_errors/_error_factory.py +89 -0
- ansible/_internal/_errors/_error_utils.py +240 -0
- ansible/_internal/_errors/_task_timeout.py +28 -0
- ansible/_internal/_event_formatting.py +127 -0
- ansible/_internal/_json/__init__.py +5 -5
- ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
- ansible/_internal/_json/_profiles/_legacy.py +3 -11
- ansible/_internal/_ssh/__init__.py +0 -0
- ansible/_internal/_ssh/_agent_launch.py +91 -0
- ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
- ansible/_internal/_templating/__init__.py +5 -3
- ansible/_internal/_templating/_datatag.py +2 -1
- ansible/_internal/_templating/_engine.py +3 -4
- ansible/_internal/_templating/_jinja_bits.py +28 -20
- ansible/_internal/_templating/_jinja_common.py +18 -27
- ansible/_internal/_templating/_jinja_plugins.py +36 -5
- ansible/_internal/_templating/_lazy_containers.py +5 -5
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +26 -19
- ansible/_internal/_templating/_utils.py +1 -1
- ansible/_internal/_yaml/_constructor.py +4 -4
- ansible/_internal/_yaml/_dumper.py +26 -18
- ansible/_internal/_yaml/_errors.py +7 -7
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
- ansible/cli/__init__.py +11 -93
- ansible/cli/arguments/option_helpers.py +3 -4
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +86 -30
- ansible/cli/inventory.py +5 -7
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +46 -0
- ansible/errors/__init__.py +98 -50
- ansible/executor/module_common.py +75 -49
- ansible/executor/powershell/async_watchdog.ps1 +2 -2
- ansible/executor/powershell/async_wrapper.ps1 +3 -3
- ansible/executor/powershell/become_wrapper.ps1 +20 -2
- ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
- ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
- ansible/executor/powershell/exec_wrapper.ps1 +219 -6
- ansible/executor/powershell/module_manifest.py +52 -0
- ansible/executor/powershell/module_wrapper.ps1 +47 -21
- ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
- ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
- ansible/executor/process/worker.py +40 -115
- ansible/executor/task_executor.py +26 -61
- ansible/executor/task_result.py +2 -4
- ansible/galaxy/api.py +1 -4
- ansible/galaxy/collection/__init__.py +2 -10
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
- ansible/galaxy/role.py +2 -2
- ansible/inventory/manager.py +1 -1
- ansible/module_utils/_internal/__init__.py +7 -7
- ansible/module_utils/_internal/_ambient_context.py +3 -3
- 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.py → _ansiballz/_loader.py} +13 -39
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_datatag/__init__.py +43 -15
- ansible/module_utils/_internal/_datatag/_tags.py +2 -2
- ansible/module_utils/_internal/_deprecator.py +67 -55
- ansible/module_utils/_internal/_errors.py +88 -17
- ansible/module_utils/_internal/_event_utils.py +61 -0
- ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
- ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
- ansible/module_utils/_internal/_plugin_info.py +15 -2
- ansible/module_utils/_internal/_stack.py +22 -0
- ansible/module_utils/_internal/_text_utils.py +6 -0
- ansible/module_utils/_internal/_traceback.py +11 -8
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +95 -71
- ansible/module_utils/common/arg_spec.py +2 -2
- ansible/module_utils/common/collections.py +6 -0
- ansible/module_utils/common/json.py +2 -2
- ansible/module_utils/common/respawn.py +4 -41
- ansible/module_utils/common/text/converters.py +3 -3
- ansible/module_utils/common/validation.py +1 -1
- ansible/module_utils/common/warnings.py +80 -23
- ansible/module_utils/common/yaml.py +1 -1
- ansible/module_utils/connection.py +8 -11
- ansible/module_utils/datatag.py +5 -2
- ansible/module_utils/facts/hardware/linux.py +1 -1
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/caps.py +2 -2
- ansible/module_utils/facts/system/distribution.py +16 -3
- ansible/module_utils/facts/system/local.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +2 -2
- ansible/module_utils/service.py +3 -10
- ansible/module_utils/urls.py +4 -4
- ansible/modules/apt_repository.py +17 -39
- ansible/modules/assemble.py +2 -2
- ansible/modules/async_status.py +13 -11
- ansible/modules/async_wrapper.py +12 -22
- ansible/modules/command.py +3 -3
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +1 -1
- ansible/modules/dnf5.py +14 -22
- ansible/modules/file.py +16 -17
- ansible/modules/find.py +3 -3
- ansible/modules/get_url.py +17 -0
- ansible/modules/git.py +9 -7
- ansible/modules/hostname.py +0 -1
- ansible/modules/known_hosts.py +12 -14
- ansible/modules/package.py +6 -0
- ansible/modules/replace.py +2 -2
- ansible/modules/service.py +3 -9
- ansible/modules/slurp.py +10 -13
- ansible/modules/stat.py +5 -7
- ansible/modules/unarchive.py +6 -6
- ansible/modules/user.py +1 -1
- ansible/modules/wait_for.py +28 -30
- ansible/modules/yum_repository.py +4 -3
- ansible/parsing/ajson.py +3 -5
- ansible/parsing/dataloader.py +6 -6
- ansible/parsing/mod_args.py +1 -1
- ansible/parsing/plugin_docs.py +2 -2
- ansible/parsing/utils/yaml.py +3 -3
- ansible/parsing/vault/__init__.py +10 -14
- ansible/playbook/base.py +7 -2
- ansible/playbook/included_file.py +3 -1
- ansible/playbook/play_context.py +2 -0
- ansible/playbook/playbook_include.py +1 -1
- ansible/playbook/taggable.py +19 -8
- ansible/playbook/task.py +2 -0
- ansible/plugins/__init__.py +0 -25
- ansible/plugins/action/__init__.py +8 -31
- ansible/plugins/action/add_host.py +1 -1
- ansible/plugins/action/assemble.py +8 -16
- ansible/plugins/action/async_status.py +7 -2
- ansible/plugins/action/copy.py +8 -7
- ansible/plugins/action/fetch.py +3 -3
- ansible/plugins/action/gather_facts.py +8 -8
- ansible/plugins/action/package.py +5 -8
- ansible/plugins/action/script.py +8 -15
- ansible/plugins/action/service.py +3 -7
- ansible/plugins/action/template.py +11 -10
- ansible/plugins/action/unarchive.py +5 -15
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/cache/__init__.py +17 -19
- ansible/plugins/callback/__init__.py +4 -6
- ansible/plugins/callback/junit.py +4 -2
- ansible/plugins/callback/tree.py +5 -5
- ansible/plugins/connection/local.py +6 -6
- ansible/plugins/connection/paramiko_ssh.py +5 -5
- ansible/plugins/connection/ssh.py +25 -15
- ansible/plugins/connection/winrm.py +6 -3
- ansible/plugins/doc_fragments/constructed.py +2 -2
- ansible/plugins/filter/core.py +32 -27
- ansible/plugins/filter/encryption.py +14 -6
- ansible/plugins/inventory/__init__.py +11 -10
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +73 -19
- ansible/plugins/loader.py +7 -7
- ansible/plugins/lookup/csvfile.py +16 -71
- ansible/plugins/lookup/first_found.py +2 -1
- ansible/plugins/lookup/template.py +9 -4
- ansible/plugins/shell/__init__.py +56 -2
- ansible/plugins/shell/powershell.py +67 -9
- ansible/plugins/shell/sh.py +10 -5
- ansible/plugins/strategy/__init__.py +3 -3
- ansible/plugins/test/core.py +22 -16
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/uri.py +2 -5
- ansible/release.py +1 -1
- ansible/template/__init__.py +38 -54
- ansible/utils/collection_loader/_collection_finder.py +3 -3
- ansible/utils/display.py +124 -138
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +6 -8
- ansible/utils/listify.py +6 -4
- ansible/utils/path.py +5 -7
- ansible/utils/py3compat.py +2 -1
- ansible/utils/ssh_functions.py +3 -2
- ansible/utils/unsafe_proxy.py +1 -1
- ansible/vars/hostvars.py +1 -1
- ansible/vars/plugins.py +3 -3
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
- ansible_test/_data/completion/docker.txt +3 -3
- ansible_test/_data/completion/remote.txt +1 -0
- ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +2 -2
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/commands/integration/coverage.py +7 -2
- ansible_test/_internal/host_profiles.py +62 -10
- ansible_test/_internal/provisioning.py +10 -4
- ansible_test/_internal/ssh.py +1 -5
- ansible_test/_internal/thread.py +2 -1
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +40 -12
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
- ansible_test/_util/target/setup/bootstrap.sh +31 -0
- ansible_test/_util/target/setup/requirements.py +3 -9
- ansible/_internal/_errors/_utils.py +0 -310
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
@@ -37,9 +37,12 @@ from ast import AST, Import, ImportFrom
|
|
37
37
|
from io import BytesIO
|
38
38
|
|
39
39
|
from ansible._internal import _locking
|
40
|
+
from ansible._internal._ansiballz import _builder
|
41
|
+
from ansible._internal import _ansiballz
|
40
42
|
from ansible._internal._datatag import _utils
|
41
43
|
from ansible.module_utils._internal import _dataclass_validation
|
42
44
|
from ansible.module_utils.common.yaml import yaml_load
|
45
|
+
from ansible.module_utils.datatag import deprecator_from_collection_name
|
43
46
|
from ansible._internal._datatag._tags import Origin
|
44
47
|
from ansible.module_utils.common.json import Direction, get_module_encoder
|
45
48
|
from ansible.release import __version__, __author__
|
@@ -53,9 +56,9 @@ from ansible.plugins.loader import module_utils_loader
|
|
53
56
|
from ansible._internal._templating._engine import TemplateOptions, TemplateEngine
|
54
57
|
from ansible.template import Templar
|
55
58
|
from ansible.utils.collection_loader._collection_finder import _get_collection_metadata, _nested_dict_get
|
56
|
-
from ansible.module_utils._internal import _json
|
59
|
+
from ansible.module_utils._internal import _json
|
60
|
+
from ansible.module_utils._internal._ansiballz import _loader
|
57
61
|
from ansible.module_utils import basic as _basic
|
58
|
-
from ansible.module_utils.common import messages as _messages
|
59
62
|
|
60
63
|
if t.TYPE_CHECKING:
|
61
64
|
from ansible import template as _template
|
@@ -117,7 +120,7 @@ def _strip_comments(source: str) -> str:
|
|
117
120
|
|
118
121
|
|
119
122
|
def _read_ansiballz_code() -> str:
|
120
|
-
code = (pathlib.Path(__file__).parent
|
123
|
+
code = (pathlib.Path(_ansiballz.__file__).parent / '_wrapper.py').read_text()
|
121
124
|
|
122
125
|
if not C.DEFAULT_KEEP_REMOTE_FILES:
|
123
126
|
# Keep comments when KEEP_REMOTE_FILES is set. That way users will see
|
@@ -166,7 +169,7 @@ NEW_STYLE_PYTHON_MODULE_RE = re.compile(
|
|
166
169
|
|
167
170
|
|
168
171
|
class ModuleDepFinder(ast.NodeVisitor):
|
169
|
-
# DTFIX-
|
172
|
+
# DTFIX-FUTURE: add support for ignoring imports with a "controller only" comment, this will allow replacing import_controller_module with standard imports
|
170
173
|
def __init__(self, module_fqn, tree, is_pkg_init=False, *args, **kwargs):
|
171
174
|
"""
|
172
175
|
Walk the ast tree for the python module.
|
@@ -439,7 +442,7 @@ class ModuleUtilLocatorBase:
|
|
439
442
|
version=removal_version,
|
440
443
|
removed=removed,
|
441
444
|
date=removal_date,
|
442
|
-
deprecator=
|
445
|
+
deprecator=deprecator_from_collection_name(self._collection_name),
|
443
446
|
)
|
444
447
|
if 'redirect' in routing_entry:
|
445
448
|
self.redirected = True
|
@@ -618,7 +621,7 @@ class CollectionModuleUtilLocator(ModuleUtilLocatorBase):
|
|
618
621
|
if pkg_path:
|
619
622
|
origin = Origin(path=os.path.join(pkg_path, src_path))
|
620
623
|
else:
|
621
|
-
# DTFIX-
|
624
|
+
# DTFIX-FUTURE: not sure if this case is even reachable
|
622
625
|
origin = Origin(description=f'<synthetic collection package for {collection_pkg_name}!r>')
|
623
626
|
|
624
627
|
self.source_code = origin.tag(src)
|
@@ -658,7 +661,7 @@ metadata_versions: dict[t.Any, type[ModuleMetadata]] = {
|
|
658
661
|
|
659
662
|
|
660
663
|
def _get_module_metadata(module: ast.Module) -> ModuleMetadata:
|
661
|
-
#
|
664
|
+
# DTFIX2: while module metadata works, this feature isn't fully baked and should be turned off before release
|
662
665
|
metadata_nodes: list[ast.Assign] = []
|
663
666
|
|
664
667
|
for node in module.body:
|
@@ -709,7 +712,14 @@ def _get_module_metadata(module: ast.Module) -> ModuleMetadata:
|
|
709
712
|
return metadata
|
710
713
|
|
711
714
|
|
712
|
-
def recursive_finder(
|
715
|
+
def recursive_finder(
|
716
|
+
name: str,
|
717
|
+
module_fqn: str,
|
718
|
+
module_data: str | bytes,
|
719
|
+
zf: zipfile.ZipFile,
|
720
|
+
date_time: datetime.datetime,
|
721
|
+
extension_manager: _builder.ExtensionManager,
|
722
|
+
) -> ModuleMetadata:
|
713
723
|
"""
|
714
724
|
Using ModuleDepFinder, make sure we have all of the module_utils files that
|
715
725
|
the module and its module_utils files needs. (no longer actually recursive)
|
@@ -755,12 +765,14 @@ def recursive_finder(name: str, module_fqn: str, module_data: str | bytes, zf: z
|
|
755
765
|
|
756
766
|
# include module_utils that are always required
|
757
767
|
modules_to_process.extend((
|
758
|
-
_ModuleUtilsProcessEntry.from_module(
|
768
|
+
_ModuleUtilsProcessEntry.from_module(_loader),
|
759
769
|
_ModuleUtilsProcessEntry.from_module(_basic),
|
760
770
|
_ModuleUtilsProcessEntry.from_module_name(_json.get_module_serialization_profile_module_name(profile, True)),
|
761
771
|
_ModuleUtilsProcessEntry.from_module_name(_json.get_module_serialization_profile_module_name(profile, False)),
|
762
772
|
))
|
763
773
|
|
774
|
+
modules_to_process.extend(_ModuleUtilsProcessEntry.from_module_name(name) for name in extension_manager.module_names)
|
775
|
+
|
764
776
|
module_info: ModuleUtilLocatorBase
|
765
777
|
|
766
778
|
# we'll be adding new modules inline as we discover them, so just keep going til we've processed them all
|
@@ -815,12 +827,13 @@ def recursive_finder(name: str, module_fqn: str, module_data: str | bytes, zf: z
|
|
815
827
|
modules_to_process.append(_ModuleUtilsProcessEntry(normalized_name, False, module_info.redirected, is_optional=entry.is_optional))
|
816
828
|
|
817
829
|
for py_module_name in py_module_cache:
|
818
|
-
py_module_file_name = py_module_cache[py_module_name]
|
830
|
+
source_code, py_module_file_name = py_module_cache[py_module_name]
|
831
|
+
|
832
|
+
zf.writestr(_make_zinfo(py_module_file_name, date_time, zf=zf), source_code)
|
833
|
+
|
834
|
+
if extension_manager.debugger_enabled and (origin := Origin.get_tag(source_code)) and origin.path:
|
835
|
+
extension_manager.source_mapping[origin.path] = py_module_file_name
|
819
836
|
|
820
|
-
zf.writestr(
|
821
|
-
_make_zinfo(py_module_file_name, date_time, zf=zf),
|
822
|
-
py_module_cache[py_module_name][0]
|
823
|
-
)
|
824
837
|
mu_file = to_text(py_module_file_name, errors='surrogate_or_strict')
|
825
838
|
display.vvvvv("Including module_utils file %s" % mu_file)
|
826
839
|
|
@@ -879,17 +892,27 @@ def _get_ansible_module_fqn(module_path):
|
|
879
892
|
return remote_module_fqn
|
880
893
|
|
881
894
|
|
882
|
-
def _add_module_to_zip(
|
895
|
+
def _add_module_to_zip(
|
896
|
+
zf: zipfile.ZipFile,
|
897
|
+
date_time: datetime.datetime,
|
898
|
+
remote_module_fqn: str,
|
899
|
+
b_module_data: bytes,
|
900
|
+
module_path: str,
|
901
|
+
extension_manager: _builder.ExtensionManager,
|
902
|
+
) -> None:
|
883
903
|
"""Add a module from ansible or from an ansible collection into the module zip"""
|
884
904
|
module_path_parts = remote_module_fqn.split('.')
|
885
905
|
|
886
906
|
# Write the module
|
887
|
-
|
907
|
+
zip_module_path = '/'.join(module_path_parts) + '.py'
|
888
908
|
zf.writestr(
|
889
|
-
_make_zinfo(
|
909
|
+
_make_zinfo(zip_module_path, date_time, zf=zf),
|
890
910
|
b_module_data
|
891
911
|
)
|
892
912
|
|
913
|
+
if extension_manager.debugger_enabled:
|
914
|
+
extension_manager.source_mapping[module_path] = zip_module_path
|
915
|
+
|
893
916
|
existing_paths: frozenset[str]
|
894
917
|
|
895
918
|
# Write the __init__.py's necessary to get there
|
@@ -928,10 +951,12 @@ class _BuiltModule:
|
|
928
951
|
class _CachedModule:
|
929
952
|
"""Cached Python module created by AnsiballZ."""
|
930
953
|
|
931
|
-
#
|
954
|
+
# DTFIX5: secure this (locked down pickle, don't use pickle, etc.)
|
932
955
|
|
933
956
|
zip_data: bytes
|
934
957
|
metadata: ModuleMetadata
|
958
|
+
source_mapping: dict[str, str]
|
959
|
+
"""A mapping of controller absolute source locations to target relative source locations within the AnsiballZ payload."""
|
935
960
|
|
936
961
|
def dump(self, path: str) -> None:
|
937
962
|
temp_path = pathlib.Path(path + '-part')
|
@@ -991,10 +1016,8 @@ def _find_module_utils(
|
|
991
1016
|
module_substyle = 'powershell'
|
992
1017
|
b_module_data = b_module_data.replace(REPLACER_WINDOWS, b'#AnsibleRequires -PowerShell Ansible.ModuleUtils.Legacy')
|
993
1018
|
elif re.search(b'#Requires -Module', b_module_data, re.IGNORECASE) \
|
994
|
-
or re.search(b'#Requires -Version', b_module_data, re.IGNORECASE)\
|
995
|
-
or re.search(b'#AnsibleRequires -OSVersion', b_module_data, re.IGNORECASE)
|
996
|
-
or re.search(b'#AnsibleRequires -Powershell', b_module_data, re.IGNORECASE) \
|
997
|
-
or re.search(b'#AnsibleRequires -CSharpUtil', b_module_data, re.IGNORECASE):
|
1019
|
+
or re.search(b'#Requires -Version', b_module_data, re.IGNORECASE) \
|
1020
|
+
or re.search(b'#AnsibleRequires -(OSVersion|PowerShell|CSharpUtil|Wrapper)', b_module_data, re.IGNORECASE):
|
998
1021
|
module_style = 'new'
|
999
1022
|
module_substyle = 'powershell'
|
1000
1023
|
elif REPLACER_JSONARGS in b_module_data:
|
@@ -1031,6 +1054,7 @@ def _find_module_utils(
|
|
1031
1054
|
|
1032
1055
|
if module_substyle == 'python':
|
1033
1056
|
date_time = datetime.datetime.now(datetime.timezone.utc)
|
1057
|
+
|
1034
1058
|
if date_time.year < 1980:
|
1035
1059
|
raise AnsibleError(f'Cannot create zipfile due to pre-1980 configured date: {date_time}')
|
1036
1060
|
|
@@ -1040,19 +1064,19 @@ def _find_module_utils(
|
|
1040
1064
|
display.warning(u'Bad module compression string specified: %s. Using ZIP_STORED (no compression)' % module_compression)
|
1041
1065
|
compression_method = zipfile.ZIP_STORED
|
1042
1066
|
|
1067
|
+
extension_manager = _builder.ExtensionManager.create(task_vars=task_vars)
|
1068
|
+
extension_key = '~'.join(extension_manager.extension_names) if extension_manager.extension_names else 'none'
|
1043
1069
|
lookup_path = os.path.join(C.DEFAULT_LOCAL_TMP, 'ansiballz_cache') # type: ignore[attr-defined]
|
1044
|
-
cached_module_filename = os.path.join(lookup_path,
|
1070
|
+
cached_module_filename = os.path.join(lookup_path, '-'.join((remote_module_fqn, module_compression, extension_key)))
|
1045
1071
|
|
1046
1072
|
os.makedirs(os.path.dirname(cached_module_filename), exist_ok=True)
|
1047
1073
|
|
1048
|
-
|
1049
|
-
module_metadata: ModuleMetadata | None = None
|
1074
|
+
cached_module: _CachedModule | None = None
|
1050
1075
|
|
1051
1076
|
# Optimization -- don't lock if the module has already been cached
|
1052
1077
|
if os.path.exists(cached_module_filename):
|
1053
1078
|
display.debug('ANSIBALLZ: using cached module: %s' % cached_module_filename)
|
1054
1079
|
cached_module = _CachedModule.load(cached_module_filename)
|
1055
|
-
zipdata, module_metadata = cached_module.zip_data, cached_module.metadata
|
1056
1080
|
else:
|
1057
1081
|
display.debug('ANSIBALLZ: Acquiring lock')
|
1058
1082
|
lock_path = f'{cached_module_filename}.lock'
|
@@ -1067,35 +1091,40 @@ def _find_module_utils(
|
|
1067
1091
|
zf = zipfile.ZipFile(zipoutput, mode='w', compression=compression_method)
|
1068
1092
|
|
1069
1093
|
# walk the module imports, looking for module_utils to send- they'll be added to the zipfile
|
1070
|
-
module_metadata = recursive_finder(
|
1094
|
+
module_metadata = recursive_finder(
|
1095
|
+
module_name,
|
1096
|
+
remote_module_fqn,
|
1097
|
+
Origin(path=module_path).tag(b_module_data),
|
1098
|
+
zf,
|
1099
|
+
date_time,
|
1100
|
+
extension_manager,
|
1101
|
+
)
|
1071
1102
|
|
1072
1103
|
display.debug('ANSIBALLZ: Writing module into payload')
|
1073
|
-
_add_module_to_zip(zf, date_time, remote_module_fqn, b_module_data)
|
1104
|
+
_add_module_to_zip(zf, date_time, remote_module_fqn, b_module_data, module_path, extension_manager)
|
1074
1105
|
|
1075
1106
|
zf.close()
|
1076
|
-
|
1107
|
+
zip_data = base64.b64encode(zipoutput.getvalue())
|
1077
1108
|
|
1078
1109
|
# Write the assembled module to a temp file (write to temp
|
1079
1110
|
# so that no one looking for the file reads a partially
|
1080
1111
|
# written file)
|
1081
1112
|
os.makedirs(lookup_path, exist_ok=True)
|
1082
1113
|
display.debug('ANSIBALLZ: Writing module')
|
1083
|
-
cached_module = _CachedModule(zip_data=
|
1114
|
+
cached_module = _CachedModule(zip_data=zip_data, metadata=module_metadata, source_mapping=extension_manager.source_mapping)
|
1084
1115
|
cached_module.dump(cached_module_filename)
|
1085
1116
|
display.debug('ANSIBALLZ: Done creating module')
|
1086
1117
|
|
1087
|
-
if not
|
1118
|
+
if not cached_module:
|
1088
1119
|
display.debug('ANSIBALLZ: Reading module after lock')
|
1089
1120
|
# Another process wrote the file while we were waiting for
|
1090
1121
|
# the write lock. Go ahead and read the data from disk
|
1091
1122
|
# instead of re-creating it.
|
1092
1123
|
try:
|
1093
1124
|
cached_module = _CachedModule.load(cached_module_filename)
|
1094
|
-
except
|
1125
|
+
except OSError as ex:
|
1095
1126
|
raise AnsibleError('A different worker process failed to create module file. '
|
1096
|
-
'Look at traceback for that process for debugging information.')
|
1097
|
-
|
1098
|
-
zipdata, module_metadata = cached_module.zip_data, cached_module.metadata
|
1127
|
+
'Look at traceback for that process for debugging information.') from ex
|
1099
1128
|
|
1100
1129
|
o_interpreter, o_args = _extract_interpreter(b_module_data)
|
1101
1130
|
if o_interpreter is None:
|
@@ -1109,40 +1138,36 @@ def _find_module_utils(
|
|
1109
1138
|
if not isinstance(rlimit_nofile, int):
|
1110
1139
|
rlimit_nofile = int(templar._engine.template(rlimit_nofile, options=TemplateOptions(value_for_omit=0)))
|
1111
1140
|
|
1112
|
-
|
1113
|
-
|
1114
|
-
if coverage_config:
|
1115
|
-
coverage_output = os.environ['_ANSIBLE_COVERAGE_OUTPUT']
|
1116
|
-
else:
|
1117
|
-
coverage_output = None
|
1118
|
-
|
1119
|
-
if not isinstance(module_metadata, ModuleMetadataV1):
|
1141
|
+
if not isinstance(cached_module.metadata, ModuleMetadataV1):
|
1120
1142
|
raise NotImplementedError()
|
1121
1143
|
|
1122
1144
|
params = dict(ANSIBLE_MODULE_ARGS=module_args,)
|
1123
|
-
encoder = get_module_encoder(
|
1145
|
+
encoder = get_module_encoder(cached_module.metadata.serialization_profile, Direction.CONTROLLER_TO_MODULE)
|
1146
|
+
|
1124
1147
|
try:
|
1125
1148
|
encoded_params = json.dumps(params, cls=encoder)
|
1126
1149
|
except TypeError as ex:
|
1127
1150
|
raise AnsibleError(f'Failed to serialize arguments for the {module_name!r} module.') from ex
|
1128
1151
|
|
1152
|
+
extension_manager.source_mapping = cached_module.source_mapping
|
1153
|
+
|
1129
1154
|
code = _get_ansiballz_code(shebang)
|
1130
1155
|
args = dict(
|
1131
|
-
zipdata=to_text(zipdata),
|
1132
1156
|
ansible_module=module_name,
|
1133
1157
|
module_fqn=remote_module_fqn,
|
1134
|
-
|
1135
|
-
profile=module_metadata.serialization_profile,
|
1158
|
+
profile=cached_module.metadata.serialization_profile,
|
1136
1159
|
date_time=date_time,
|
1137
|
-
coverage_config=coverage_config,
|
1138
|
-
coverage_output=coverage_output,
|
1139
1160
|
rlimit_nofile=rlimit_nofile,
|
1161
|
+
params=encoded_params,
|
1162
|
+
extensions=extension_manager.get_extensions(),
|
1163
|
+
zip_data=to_text(cached_module.zip_data),
|
1140
1164
|
)
|
1141
1165
|
|
1142
1166
|
args_string = '\n'.join(f'{key}={value!r},' for key, value in args.items())
|
1143
1167
|
|
1144
1168
|
wrapper = f"""{code}
|
1145
1169
|
|
1170
|
+
|
1146
1171
|
if __name__ == "__main__":
|
1147
1172
|
_ansiballz_main(
|
1148
1173
|
{args_string}
|
@@ -1151,6 +1176,7 @@ if __name__ == "__main__":
|
|
1151
1176
|
|
1152
1177
|
output.write(to_bytes(wrapper))
|
1153
1178
|
|
1179
|
+
module_metadata = cached_module.metadata
|
1154
1180
|
b_module_data = output.getvalue()
|
1155
1181
|
|
1156
1182
|
elif module_substyle == 'powershell':
|
@@ -40,7 +40,7 @@ param([ScriptBlock]$ScriptBlock, $Param)
|
|
40
40
|
& $ScriptBlock.Ast.GetScriptBlock() @Param
|
41
41
|
'@).AddParameters(
|
42
42
|
@{
|
43
|
-
ScriptBlock = $execInfo.ScriptBlock
|
43
|
+
ScriptBlock = $execInfo.ScriptInfo.ScriptBlock
|
44
44
|
Param = $execInfo.Parameters
|
45
45
|
})
|
46
46
|
|
@@ -64,7 +64,7 @@ $jobError = $null
|
|
64
64
|
try {
|
65
65
|
$jobAsyncResult = $ps.BeginInvoke($pipelineInput, $invocationSettings, $null, $null)
|
66
66
|
$jobAsyncResult.AsyncWaitHandle.WaitOne($Timeout * 1000) > $null
|
67
|
-
$result.finished =
|
67
|
+
$result.finished = $true
|
68
68
|
|
69
69
|
if ($jobAsyncResult.IsCompleted) {
|
70
70
|
$jobOutput = $ps.EndInvoke($jobAsyncResult)
|
@@ -113,7 +113,7 @@ try {
|
|
113
113
|
}
|
114
114
|
$execWrapper = @{
|
115
115
|
name = 'exec_wrapper-async.ps1'
|
116
|
-
script = $execAction.Script
|
116
|
+
script = $execAction.ScriptInfo.Script
|
117
117
|
params = $execAction.Parameters
|
118
118
|
} | ConvertTo-Json -Compress -Depth 99
|
119
119
|
$asyncInput = "$execWrapper`n`0`0`0`0`n$($execAction.InputData)"
|
@@ -135,8 +135,8 @@ try {
|
|
135
135
|
# We need to write the result file before the process is started to ensure
|
136
136
|
# it can read the file.
|
137
137
|
$result = @{
|
138
|
-
started =
|
139
|
-
finished =
|
138
|
+
started = $true
|
139
|
+
finished = $false
|
140
140
|
results_file = $resultsPath
|
141
141
|
ansible_job_id = $localJid
|
142
142
|
_ansible_suppress_tmpdir_delete = $true
|
@@ -7,6 +7,7 @@ using namespace System.Collections
|
|
7
7
|
using namespace System.Diagnostics
|
8
8
|
using namespace System.IO
|
9
9
|
using namespace System.Management.Automation
|
10
|
+
using namespace System.Management.Automation.Security
|
10
11
|
using namespace System.Net
|
11
12
|
using namespace System.Text
|
12
13
|
|
@@ -53,7 +54,7 @@ $executablePath = Join-Path -Path $PSHome -ChildPath $executable
|
|
53
54
|
$actionInfo = Get-AnsibleExecWrapper -EncodeInputOutput
|
54
55
|
$bootstrapManifest = ConvertTo-Json -InputObject @{
|
55
56
|
n = "exec_wrapper-become-$([Guid]::NewGuid()).ps1"
|
56
|
-
s = $actionInfo.Script
|
57
|
+
s = $actionInfo.ScriptInfo.Script
|
57
58
|
p = $actionInfo.Parameters
|
58
59
|
} -Depth 99 -Compress
|
59
60
|
|
@@ -68,9 +69,26 @@ $m=foreach($i in $input){
|
|
68
69
|
$m=$m|ConvertFrom-Json
|
69
70
|
$p=@{}
|
70
71
|
foreach($o in $m.p.PSObject.Properties){$p[$o.Name]=$o.Value}
|
72
|
+
'@
|
73
|
+
|
74
|
+
if ([SystemPolicy]::GetSystemLockdownPolicy() -eq 'Enforce') {
|
75
|
+
# If we started in CLM we need to execute the script from a file so that
|
76
|
+
# PowerShell validates our exec_wrapper is trusted and will run in FLM.
|
77
|
+
$command += @'
|
78
|
+
$n=Join-Path $env:TEMP $m.n
|
79
|
+
$null=New-Item $n -Value $m.s -Type File -Force
|
80
|
+
try{$input|&$n @p}
|
81
|
+
finally{if(Test-Path -LiteralPath $n){Remove-Item -LiteralPath $n -Force}}
|
82
|
+
'@
|
83
|
+
}
|
84
|
+
else {
|
85
|
+
# If we started in FLM we pass the script through stdin and execute in
|
86
|
+
# memory.
|
87
|
+
$command += @'
|
71
88
|
$c=[System.Management.Automation.Language.Parser]::ParseInput($m.s,$m.n,[ref]$null,[ref]$null).GetScriptBlock()
|
72
|
-
$input
|
89
|
+
$input|&$c @p
|
73
90
|
'@
|
91
|
+
}
|
74
92
|
|
75
93
|
# Strip out any leading or trailing whitespace and remove empty lines.
|
76
94
|
$command = @(
|
@@ -18,10 +18,32 @@ foreach ($obj in $code.params.PSObject.Properties) {
|
|
18
18
|
$splat[$obj.Name] = $obj.Value
|
19
19
|
}
|
20
20
|
|
21
|
-
$
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
$filePath = $null
|
22
|
+
try {
|
23
|
+
$cmd = if ($ExecutionContext.SessionState.LanguageMode -eq 'FullLanguage') {
|
24
|
+
# In FLM we can just invoke the code as a scriptblock without touching the
|
25
|
+
# disk.
|
26
|
+
[System.Management.Automation.Language.Parser]::ParseInput(
|
27
|
+
$code.script,
|
28
|
+
"$($code.name).ps1", # Name is used in stack traces.
|
29
|
+
[ref]$null,
|
30
|
+
[ref]$null).GetScriptBlock()
|
31
|
+
}
|
32
|
+
else {
|
33
|
+
# CLM needs to execute code from a file for it to run in FLM when trusted.
|
34
|
+
# Set-Item on 5.1 doesn't have a way to use UTF-8 without a BOM but luckily
|
35
|
+
# New-Item does that by default for both 5.1 and 7. We need to ensure we
|
36
|
+
# use UTF-8 without BOM so the signature is correct.
|
37
|
+
$filePath = Join-Path -Path $env:TEMP -ChildPath "$($code.name)-$(New-Guid).ps1"
|
38
|
+
$null = New-Item -Path $filePath -Value $code.script -ItemType File -Force
|
39
|
+
|
40
|
+
$filePath
|
41
|
+
}
|
26
42
|
|
27
|
-
$input | & $cmd @splat
|
43
|
+
$input | & $cmd @splat
|
44
|
+
}
|
45
|
+
finally {
|
46
|
+
if ($filePath -and (Test-Path -LiteralPath $filePath)) {
|
47
|
+
Remove-Item -LiteralPath $filePath -Force
|
48
|
+
}
|
49
|
+
}
|
@@ -28,8 +28,12 @@ $Host.Runspace.Debugger.SetDebugMode([DebugModes]::RemoteScript)
|
|
28
28
|
Function New-CoverageBreakpointsForScriptBlock {
|
29
29
|
Param (
|
30
30
|
[Parameter(Mandatory)]
|
31
|
-
[
|
32
|
-
$
|
31
|
+
[string]
|
32
|
+
$ScriptName,
|
33
|
+
|
34
|
+
[Parameter(Mandatory)]
|
35
|
+
[ScriptBlockAst]
|
36
|
+
$ScriptBlockAst,
|
33
37
|
|
34
38
|
[Parameter(Mandatory)]
|
35
39
|
[String]
|
@@ -39,7 +43,7 @@ Function New-CoverageBreakpointsForScriptBlock {
|
|
39
43
|
$predicate = {
|
40
44
|
$args[0] -is [CommandBaseAst]
|
41
45
|
}
|
42
|
-
$scriptCmds = $
|
46
|
+
$scriptCmds = $ScriptBlockAst.FindAll($predicate, $true)
|
43
47
|
|
44
48
|
# Create an object that tracks the Ansible path of the file and the breakpoints that have been set in it
|
45
49
|
$info = [PSCustomObject]@{
|
@@ -68,7 +72,7 @@ Function New-CoverageBreakpointsForScriptBlock {
|
|
68
72
|
}
|
69
73
|
|
70
74
|
# Action is explicitly $null as it will slow down the runtime quite dramatically.
|
71
|
-
$b = $lineCtor.Invoke(@($
|
75
|
+
$b = $lineCtor.Invoke(@($ScriptName, $cmd.Extent.StartLineNumber, $cmd.Extent.StartColumnNumber, $null))
|
72
76
|
$info.Breakpoints.Add($b)
|
73
77
|
}
|
74
78
|
|
@@ -102,11 +106,16 @@ try {
|
|
102
106
|
$coveragePathFilter = $PathFilter.Split(":", [StringSplitOptions]::RemoveEmptyEntries)
|
103
107
|
$breakpointInfo = @(
|
104
108
|
foreach ($scriptName in @($ModuleName; $actionParams.PowerShellModules)) {
|
105
|
-
|
109
|
+
# We don't use -IncludeScriptBlock as the script might be untrusted
|
110
|
+
# and will run under CLM. While we recreate the ScriptBlock here it
|
111
|
+
# is only to get the AST that contains the statements and their
|
112
|
+
# line numbers to create the breakpoint info for.
|
113
|
+
$scriptInfo = Get-AnsibleScript -Name $scriptName
|
106
114
|
|
107
115
|
if (Compare-PathFilterPattern -Patterns $coveragePathFilter -Path $scriptInfo.Path) {
|
108
116
|
$covParams = @{
|
109
|
-
|
117
|
+
ScriptName = $scriptInfo.Name
|
118
|
+
ScriptBlockAst = [ScriptBlock]::Create($scriptInfo.Script).Ast
|
110
119
|
AnsiblePath = $scriptInfo.Path
|
111
120
|
}
|
112
121
|
New-CoverageBreakpointsForScriptBlock @covParams
|