Powershell module_utils loader and tests (#26932)
* supports custom module_utils loads (anything in module prefaced with `#Requires -Module Ansible.ModuleUtils.*`) * supports all usual PluginLoader module_utils locations (built-in lib/ansible/module_utils/, custom path from config, playbook module_utils/, ~/.ansible/module_utils, role module_utils, etc), * moves Powershell module_utils from module_utils/powershell.ps1 to module_utils/powershell/Ansible.ModuleUtils.PowerShellLegacy.psm1
This commit is contained in:
parent
37e757286d
commit
907b662dc6
12 changed files with 84 additions and 7 deletions
|
@ -32,6 +32,7 @@ Ansible Changes By Release
|
||||||
* Configuration has been changed from a hardcoded into the constants module to dynamically loaded from yaml definitions
|
* Configuration has been changed from a hardcoded into the constants module to dynamically loaded from yaml definitions
|
||||||
- Also added an ansible-config CLI to allow for listing config options and dumping current config (including origin)
|
- Also added an ansible-config CLI to allow for listing config options and dumping current config (including origin)
|
||||||
- TODO: build upon this to add many features detailed in ansible-config proposal https://github.com/ansible/proposals/issues/35
|
- TODO: build upon this to add many features detailed in ansible-config proposal https://github.com/ansible/proposals/issues/35
|
||||||
|
* Windows modules now support the use of multiple shared module_utils files in the form of Powershell modules (.psm1), via `#Requires -Module Ansible.ModuleUtils.Whatever.psm1`
|
||||||
|
|
||||||
### Deprecations
|
### Deprecations
|
||||||
* The behaviour when specifying `--tags` (or `--skip-tags`) multiple times on the command line
|
* The behaviour when specifying `--tags` (or `--skip-tags`) multiple times on the command line
|
||||||
|
|
|
@ -36,7 +36,7 @@ from ansible.release import __version__, __author__
|
||||||
from ansible import constants as C
|
from ansible import constants as C
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
from ansible.module_utils._text import to_bytes, to_text
|
from ansible.module_utils._text import to_bytes, to_text
|
||||||
from ansible.plugins import module_utils_loader
|
from ansible.plugins import module_utils_loader, ps_module_utils_loader
|
||||||
from ansible.plugins.shell.powershell import async_watchdog, async_wrapper, become_wrapper, leaf_exec, exec_wrapper
|
from ansible.plugins.shell.powershell import async_watchdog, async_wrapper, become_wrapper, leaf_exec, exec_wrapper
|
||||||
# Must import strategy and use write_locks from there
|
# Must import strategy and use write_locks from there
|
||||||
# If we import write_locks directly then we end up binding a
|
# If we import write_locks directly then we end up binding a
|
||||||
|
@ -623,7 +623,7 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
|
||||||
elif b'from ansible.module_utils.' in b_module_data:
|
elif b'from ansible.module_utils.' in b_module_data:
|
||||||
module_style = 'new'
|
module_style = 'new'
|
||||||
module_substyle = 'python'
|
module_substyle = 'python'
|
||||||
elif REPLACER_WINDOWS in b_module_data or b'#Requires -Module' in b_module_data:
|
elif REPLACER_WINDOWS in b_module_data or re.search(b'#Requires \-Module', b_module_data, re.IGNORECASE):
|
||||||
module_style = 'new'
|
module_style = 'new'
|
||||||
module_substyle = 'powershell'
|
module_substyle = 'powershell'
|
||||||
elif REPLACER_JSONARGS in b_module_data:
|
elif REPLACER_JSONARGS in b_module_data:
|
||||||
|
@ -786,20 +786,25 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
|
||||||
lines = b_module_data.split(b'\n')
|
lines = b_module_data.split(b'\n')
|
||||||
module_names = set()
|
module_names = set()
|
||||||
|
|
||||||
requires_module_list = re.compile(r'(?i)^#requires \-module(?:s?) (.+)')
|
requires_module_list = re.compile(to_bytes(r'(?i)^#\s*requires\s+\-module(?:s?)\s*(Ansible\.ModuleUtils\..+)'))
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# legacy, equivalent to #Requires -Modules powershell
|
# legacy, equivalent to #Requires -Modules powershell
|
||||||
if REPLACER_WINDOWS in line:
|
if REPLACER_WINDOWS in line:
|
||||||
module_names.add(b'powershell')
|
module_names.add(b'Ansible.ModuleUtils.PowerShellLegacy')
|
||||||
# TODO: add #Requires checks for Ansible.ModuleUtils.X
|
line_match = requires_module_list.match(line)
|
||||||
|
if line_match:
|
||||||
|
module_names.add(line_match.group(1))
|
||||||
|
|
||||||
for m in module_names:
|
for m in set(module_names):
|
||||||
m = to_text(m)
|
m = to_text(m)
|
||||||
|
mu_path = ps_module_utils_loader.find_plugin(m, ".psm1")
|
||||||
|
if not mu_path:
|
||||||
|
raise AnsibleError('Could not find imported module support code for \'%s\'.' % m)
|
||||||
exec_manifest["powershell_modules"][m] = to_text(
|
exec_manifest["powershell_modules"][m] = to_text(
|
||||||
base64.b64encode(
|
base64.b64encode(
|
||||||
to_bytes(
|
to_bytes(
|
||||||
_slurp(os.path.join(_MODULE_UTILS_PATH, m + ".ps1"))
|
_slurp(mu_path)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
0
lib/ansible/module_utils/powershell/__init__.py
Normal file
0
lib/ansible/module_utils/powershell/__init__.py
Normal file
|
@ -500,6 +500,15 @@ module_utils_loader = PluginLoader(
|
||||||
'module_utils',
|
'module_utils',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# NB: dedicated loader is currently necessary because PS module_utils expects "with subdir" lookup where
|
||||||
|
# regular module_utils doesn't. This can be revisited once we have more granular loaders.
|
||||||
|
ps_module_utils_loader = PluginLoader(
|
||||||
|
'',
|
||||||
|
'ansible.module_utils',
|
||||||
|
C.DEFAULT_MODULE_UTILS_PATH,
|
||||||
|
'module_utils',
|
||||||
|
)
|
||||||
|
|
||||||
lookup_loader = PluginLoader(
|
lookup_loader = PluginLoader(
|
||||||
'LookupModule',
|
'LookupModule',
|
||||||
'ansible.plugins.lookup',
|
'ansible.plugins.lookup',
|
||||||
|
|
1
test/integration/targets/win_module_utils/aliases
Normal file
1
test/integration/targets/win_module_utils/aliases
Normal file
|
@ -0,0 +1 @@
|
||||||
|
windows/ci/smoketest
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!powershell
|
||||||
|
|
||||||
|
#Requires -Module Ansible.ModuleUtils.PowerShellLegacy
|
||||||
|
|
||||||
|
Exit-Json @{ data="success" }
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!powershell
|
||||||
|
|
||||||
|
# POWERSHELL_COMMON
|
||||||
|
|
||||||
|
Exit-Json @{ data="success" }
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!powershell
|
||||||
|
|
||||||
|
# this should fail
|
||||||
|
#Requires -Module Ansible.ModuleUtils.BogusModule
|
||||||
|
|
||||||
|
Exit-Json @{ data="success" }
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!powershell
|
||||||
|
|
||||||
|
# use different cases, spacing and plural of 'module' to exercise flexible powershell dialect
|
||||||
|
#ReQuiReS -ModUleS Ansible.ModuleUtils.PowerShellLegacy
|
||||||
|
#Requires -Module Ansible.ModuleUtils.ValidTestModule
|
||||||
|
|
||||||
|
$o = CustomFunction
|
||||||
|
|
||||||
|
Exit-Json @{data=$o}
|
|
@ -0,0 +1,3 @@
|
||||||
|
Function CustomFunction {
|
||||||
|
return "ValueFromCustomFunction"
|
||||||
|
}
|
33
test/integration/targets/win_module_utils/tasks/main.yml
Normal file
33
test/integration/targets/win_module_utils/tasks/main.yml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
- name: call old WANTS_JSON module
|
||||||
|
legacy_only_old_way:
|
||||||
|
register: old_way
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- old_way.data == 'success'
|
||||||
|
|
||||||
|
- name: call module with only legacy requires
|
||||||
|
legacy_only_new_way:
|
||||||
|
register: new_way
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- new_way.data == 'success'
|
||||||
|
|
||||||
|
- name: call module with local module_utils
|
||||||
|
uses_local_utils:
|
||||||
|
register: local_utils
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- local_utils.data == "ValueFromCustomFunction"
|
||||||
|
|
||||||
|
- name: call module that imports bogus Ansible-named module_utils
|
||||||
|
uses_bogus_utils:
|
||||||
|
ignore_errors: true
|
||||||
|
register: bogus_utils
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- bogus_utils | failed
|
||||||
|
- bogus_utils.msg | search("Could not find")
|
Loading…
Reference in a new issue