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.
Files changed (225) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_ansiballz/__init__.py +0 -0
  3. ansible/_internal/_ansiballz/_builder.py +101 -0
  4. ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
  5. ansible/_internal/_collection_proxy.py +1 -1
  6. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  7. ansible/_internal/_errors/_captured.py +25 -30
  8. ansible/_internal/_errors/_error_factory.py +89 -0
  9. ansible/_internal/_errors/_error_utils.py +240 -0
  10. ansible/_internal/_errors/_task_timeout.py +28 -0
  11. ansible/_internal/_event_formatting.py +127 -0
  12. ansible/_internal/_json/__init__.py +5 -5
  13. ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
  14. ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
  15. ansible/_internal/_json/_profiles/_legacy.py +3 -11
  16. ansible/_internal/_ssh/__init__.py +0 -0
  17. ansible/_internal/_ssh/_agent_launch.py +91 -0
  18. ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
  19. ansible/_internal/_templating/__init__.py +5 -3
  20. ansible/_internal/_templating/_datatag.py +2 -1
  21. ansible/_internal/_templating/_engine.py +3 -4
  22. ansible/_internal/_templating/_jinja_bits.py +28 -20
  23. ansible/_internal/_templating/_jinja_common.py +18 -27
  24. ansible/_internal/_templating/_jinja_plugins.py +36 -5
  25. ansible/_internal/_templating/_lazy_containers.py +5 -5
  26. ansible/_internal/_templating/_template_vars.py +72 -0
  27. ansible/_internal/_templating/_transform.py +26 -19
  28. ansible/_internal/_templating/_utils.py +1 -1
  29. ansible/_internal/_yaml/_constructor.py +4 -4
  30. ansible/_internal/_yaml/_dumper.py +26 -18
  31. ansible/_internal/_yaml/_errors.py +7 -7
  32. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  33. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  34. ansible/cli/__init__.py +11 -93
  35. ansible/cli/arguments/option_helpers.py +3 -4
  36. ansible/cli/console.py +1 -1
  37. ansible/cli/doc.py +86 -30
  38. ansible/cli/inventory.py +5 -7
  39. ansible/compat/importlib_resources.py +9 -12
  40. ansible/config/base.yml +46 -0
  41. ansible/errors/__init__.py +98 -50
  42. ansible/executor/module_common.py +75 -49
  43. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  44. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  45. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  46. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  47. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  48. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  49. ansible/executor/powershell/module_manifest.py +52 -0
  50. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  51. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  52. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  53. ansible/executor/process/worker.py +40 -115
  54. ansible/executor/task_executor.py +26 -61
  55. ansible/executor/task_result.py +2 -4
  56. ansible/galaxy/api.py +1 -4
  57. ansible/galaxy/collection/__init__.py +2 -10
  58. ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
  59. ansible/galaxy/role.py +2 -2
  60. ansible/inventory/manager.py +1 -1
  61. ansible/module_utils/_internal/__init__.py +7 -7
  62. ansible/module_utils/_internal/_ambient_context.py +3 -3
  63. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  64. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  65. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  66. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  67. ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +13 -39
  68. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  69. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  70. ansible/module_utils/_internal/_datatag/__init__.py +43 -15
  71. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  72. ansible/module_utils/_internal/_deprecator.py +67 -55
  73. ansible/module_utils/_internal/_errors.py +88 -17
  74. ansible/module_utils/_internal/_event_utils.py +61 -0
  75. ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
  76. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  77. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  78. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  79. ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
  80. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  81. ansible/module_utils/_internal/_plugin_info.py +15 -2
  82. ansible/module_utils/_internal/_stack.py +22 -0
  83. ansible/module_utils/_internal/_text_utils.py +6 -0
  84. ansible/module_utils/_internal/_traceback.py +11 -8
  85. ansible/module_utils/ansible_release.py +1 -1
  86. ansible/module_utils/basic.py +95 -71
  87. ansible/module_utils/common/arg_spec.py +2 -2
  88. ansible/module_utils/common/collections.py +6 -0
  89. ansible/module_utils/common/json.py +2 -2
  90. ansible/module_utils/common/respawn.py +4 -41
  91. ansible/module_utils/common/text/converters.py +3 -3
  92. ansible/module_utils/common/validation.py +1 -1
  93. ansible/module_utils/common/warnings.py +80 -23
  94. ansible/module_utils/common/yaml.py +1 -1
  95. ansible/module_utils/connection.py +8 -11
  96. ansible/module_utils/datatag.py +5 -2
  97. ansible/module_utils/facts/hardware/linux.py +1 -1
  98. ansible/module_utils/facts/sysctl.py +4 -6
  99. ansible/module_utils/facts/system/caps.py +2 -2
  100. ansible/module_utils/facts/system/distribution.py +16 -3
  101. ansible/module_utils/facts/system/local.py +1 -1
  102. ansible/module_utils/facts/virtual/linux.py +2 -2
  103. ansible/module_utils/service.py +3 -10
  104. ansible/module_utils/urls.py +4 -4
  105. ansible/modules/apt_repository.py +17 -39
  106. ansible/modules/assemble.py +2 -2
  107. ansible/modules/async_status.py +13 -11
  108. ansible/modules/async_wrapper.py +12 -22
  109. ansible/modules/command.py +3 -3
  110. ansible/modules/copy.py +4 -4
  111. ansible/modules/cron.py +1 -1
  112. ansible/modules/dnf5.py +14 -22
  113. ansible/modules/file.py +16 -17
  114. ansible/modules/find.py +3 -3
  115. ansible/modules/get_url.py +17 -0
  116. ansible/modules/git.py +9 -7
  117. ansible/modules/hostname.py +0 -1
  118. ansible/modules/known_hosts.py +12 -14
  119. ansible/modules/package.py +6 -0
  120. ansible/modules/replace.py +2 -2
  121. ansible/modules/service.py +3 -9
  122. ansible/modules/slurp.py +10 -13
  123. ansible/modules/stat.py +5 -7
  124. ansible/modules/unarchive.py +6 -6
  125. ansible/modules/user.py +1 -1
  126. ansible/modules/wait_for.py +28 -30
  127. ansible/modules/yum_repository.py +4 -3
  128. ansible/parsing/ajson.py +3 -5
  129. ansible/parsing/dataloader.py +6 -6
  130. ansible/parsing/mod_args.py +1 -1
  131. ansible/parsing/plugin_docs.py +2 -2
  132. ansible/parsing/utils/yaml.py +3 -3
  133. ansible/parsing/vault/__init__.py +10 -14
  134. ansible/playbook/base.py +7 -2
  135. ansible/playbook/included_file.py +3 -1
  136. ansible/playbook/play_context.py +2 -0
  137. ansible/playbook/playbook_include.py +1 -1
  138. ansible/playbook/taggable.py +19 -8
  139. ansible/playbook/task.py +2 -0
  140. ansible/plugins/__init__.py +0 -25
  141. ansible/plugins/action/__init__.py +8 -31
  142. ansible/plugins/action/add_host.py +1 -1
  143. ansible/plugins/action/assemble.py +8 -16
  144. ansible/plugins/action/async_status.py +7 -2
  145. ansible/plugins/action/copy.py +8 -7
  146. ansible/plugins/action/fetch.py +3 -3
  147. ansible/plugins/action/gather_facts.py +8 -8
  148. ansible/plugins/action/package.py +5 -8
  149. ansible/plugins/action/script.py +8 -15
  150. ansible/plugins/action/service.py +3 -7
  151. ansible/plugins/action/template.py +11 -10
  152. ansible/plugins/action/unarchive.py +5 -15
  153. ansible/plugins/action/uri.py +9 -20
  154. ansible/plugins/cache/__init__.py +17 -19
  155. ansible/plugins/callback/__init__.py +4 -6
  156. ansible/plugins/callback/junit.py +4 -2
  157. ansible/plugins/callback/tree.py +5 -5
  158. ansible/plugins/connection/local.py +6 -6
  159. ansible/plugins/connection/paramiko_ssh.py +5 -5
  160. ansible/plugins/connection/ssh.py +25 -15
  161. ansible/plugins/connection/winrm.py +6 -3
  162. ansible/plugins/doc_fragments/constructed.py +2 -2
  163. ansible/plugins/filter/core.py +32 -27
  164. ansible/plugins/filter/encryption.py +14 -6
  165. ansible/plugins/inventory/__init__.py +11 -10
  166. ansible/plugins/inventory/script.py +1 -1
  167. ansible/plugins/list.py +73 -19
  168. ansible/plugins/loader.py +7 -7
  169. ansible/plugins/lookup/csvfile.py +16 -71
  170. ansible/plugins/lookup/first_found.py +2 -1
  171. ansible/plugins/lookup/template.py +9 -4
  172. ansible/plugins/shell/__init__.py +56 -2
  173. ansible/plugins/shell/powershell.py +67 -9
  174. ansible/plugins/shell/sh.py +10 -5
  175. ansible/plugins/strategy/__init__.py +3 -3
  176. ansible/plugins/test/core.py +22 -16
  177. ansible/plugins/test/finished.yml +1 -1
  178. ansible/plugins/test/uri.py +2 -5
  179. ansible/release.py +1 -1
  180. ansible/template/__init__.py +38 -54
  181. ansible/utils/collection_loader/_collection_finder.py +3 -3
  182. ansible/utils/display.py +124 -138
  183. ansible/utils/galaxy.py +2 -2
  184. ansible/utils/hashing.py +6 -8
  185. ansible/utils/listify.py +6 -4
  186. ansible/utils/path.py +5 -7
  187. ansible/utils/py3compat.py +2 -1
  188. ansible/utils/ssh_functions.py +3 -2
  189. ansible/utils/unsafe_proxy.py +1 -1
  190. ansible/vars/hostvars.py +1 -1
  191. ansible/vars/plugins.py +3 -3
  192. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
  193. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
  194. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
  195. ansible_test/_data/completion/docker.txt +3 -3
  196. ansible_test/_data/completion/remote.txt +1 -0
  197. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  198. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  199. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  200. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  201. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  202. ansible_test/_internal/commands/integration/coverage.py +7 -2
  203. ansible_test/_internal/host_profiles.py +62 -10
  204. ansible_test/_internal/provisioning.py +10 -4
  205. ansible_test/_internal/ssh.py +1 -5
  206. ansible_test/_internal/thread.py +2 -1
  207. ansible_test/_internal/timeout.py +1 -1
  208. ansible_test/_internal/util.py +40 -12
  209. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  210. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  211. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  212. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  213. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  214. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
  215. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  216. ansible_test/_util/target/setup/requirements.py +3 -9
  217. ansible/_internal/_errors/_utils.py +0 -310
  218. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
  219. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
  220. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  221. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  222. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  223. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  224. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  225. {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, _ansiballz
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.parent / '_internal/_ansiballz.py').read_text()
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-RELEASE: add support for ignoring imports with a "controller only" comment, this will allow replacing import_controller_module with standard imports
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=_messages.PluginInfo._from_collection_name(self._collection_name),
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-RELEASE: not sure if this case is even reachable
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
- # DTFIX-RELEASE: while module metadata works, this feature isn't fully baked and should be turned off before release
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(name: str, module_fqn: str, module_data: str | bytes, zf: zipfile.ZipFile, date_time: datetime.datetime) -> ModuleMetadata:
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(_ansiballz),
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][1]
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(zf: zipfile.ZipFile, date_time: datetime.datetime, remote_module_fqn: str, b_module_data: bytes) -> None:
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
- module_path = '/'.join(module_path_parts) + '.py'
907
+ zip_module_path = '/'.join(module_path_parts) + '.py'
888
908
  zf.writestr(
889
- _make_zinfo(module_path, date_time, zf=zf),
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
- # DTFIX-RELEASE: secure this (locked down pickle, don't use pickle, etc.)
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, "%s-%s" % (remote_module_fqn, module_compression))
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
- zipdata: bytes | None = None
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(module_name, remote_module_fqn, Origin(path=module_path).tag(b_module_data), zf, date_time)
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
- zipdata = base64.b64encode(zipoutput.getvalue())
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=zipdata, metadata=module_metadata)
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 zipdata:
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 IOError:
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
- coverage_config = os.environ.get('_ANSIBLE_COVERAGE_CONFIG')
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(module_metadata.serialization_profile, Direction.CONTROLLER_TO_MODULE)
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
- params=encoded_params,
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 = 1
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 = 1
139
- finished = 0
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 | & $c @p
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
- $cmd = [System.Management.Automation.Language.Parser]::ParseInput(
22
- $code.script,
23
- "$($code.name).ps1", # Name is used in stack traces.
24
- [ref]$null,
25
- [ref]$null).GetScriptBlock()
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
- [ScriptBlock]
32
- $ScriptBlock,
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 = $ScriptBlock.Ast.FindAll($predicate, $true)
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(@($ScriptBlock.File, $cmd.Extent.StartLineNumber, $cmd.Extent.StartColumnNumber, $null))
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
- $scriptInfo = Get-AnsibleScript -Name $scriptName -IncludeScriptBlock
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
- ScriptBlock = $scriptInfo.ScriptBlock
117
+ ScriptName = $scriptInfo.Name
118
+ ScriptBlockAst = [ScriptBlock]::Create($scriptInfo.Script).Ast
110
119
  AnsiblePath = $scriptInfo.Path
111
120
  }
112
121
  New-CoverageBreakpointsForScriptBlock @covParams