mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 05:42:50 +00:00
re-enable non-pipelined mode for Powershell (#25012)
* fixes #23986 * fixes 3rd-party Windows connection plugins that don't support pipelining (eg awsrun)
This commit is contained in:
@@ -31,7 +31,7 @@ from abc import ABCMeta, abstractmethod
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleActionSkip, AnsibleActionFail
|
||||
from ansible.executor.module_common import modify_module, build_windows_module_payload
|
||||
from ansible.executor.module_common import modify_module
|
||||
from ansible.module_utils.json_utils import _filter_non_json_lines
|
||||
from ansible.module_utils.six import binary_type, string_types, text_type, iteritems, with_metaclass
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
@@ -153,18 +153,15 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
||||
"run 'git pull --rebase' to correct this problem." % (module_name))
|
||||
|
||||
# insert shared code and arguments into the module
|
||||
(module_data, module_style, module_shebang) = modify_module(module_name, module_path, module_args,
|
||||
task_vars=task_vars, module_compression=self._play_context.module_compression)
|
||||
final_environment = dict()
|
||||
self._compute_environment_string(final_environment)
|
||||
|
||||
# FUTURE: we'll have to get fancier about this to support powershell over SSH on Windows...
|
||||
if self._connection.transport == "winrm":
|
||||
# WinRM always pipelines, so we need to build up a fancier module payload...
|
||||
final_environment = dict()
|
||||
self._compute_environment_string(final_environment)
|
||||
module_data = build_windows_module_payload(module_name=module_name, module_path=module_path,
|
||||
b_module_data=module_data, module_args=module_args,
|
||||
task_vars=task_vars, task=self._task,
|
||||
play_context=self._play_context, environment=final_environment)
|
||||
(module_data, module_style, module_shebang) = modify_module(module_name, module_path, module_args,
|
||||
task_vars=task_vars, module_compression=self._play_context.module_compression,
|
||||
async_timeout=self._task.async, become=self._play_context.become,
|
||||
become_method=self._play_context.become_method, become_user=self._play_context.become_user,
|
||||
become_password=self._play_context.become_pass,
|
||||
environment=final_environment)
|
||||
|
||||
return (module_style, module_shebang, module_data, module_path)
|
||||
|
||||
@@ -184,7 +181,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
||||
# block then task 'win' in precedence
|
||||
environments.reverse()
|
||||
for environment in environments:
|
||||
if environment is None:
|
||||
if environment is None or len(environment) == 0:
|
||||
continue
|
||||
temp_environment = self._templar.template(environment)
|
||||
if not isinstance(temp_environment, dict):
|
||||
@@ -193,7 +190,8 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
||||
# these environment settings should not need to merge sub-dicts
|
||||
final_environment.update(temp_environment)
|
||||
|
||||
final_environment = self._templar.template(final_environment)
|
||||
if len(final_environment) > 0:
|
||||
final_environment = self._templar.template(final_environment)
|
||||
|
||||
if isinstance(raw_environment_out, dict):
|
||||
raw_environment_out.clear()
|
||||
@@ -212,13 +210,11 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
||||
'''
|
||||
Determines if we are required and can do pipelining
|
||||
'''
|
||||
if self._connection.always_pipeline_modules:
|
||||
return True # eg, winrm
|
||||
|
||||
# any of these require a true
|
||||
for condition in [
|
||||
self._connection.has_pipelining,
|
||||
self._play_context.pipelining,
|
||||
self._play_context.pipelining or self._connection.always_pipeline_modules, # pipelining enabled for play or connection requires it (eg winrm)
|
||||
module_style == "new", # old style modules do not support pipelining
|
||||
not C.DEFAULT_KEEP_REMOTE_FILES, # user wants remote files
|
||||
not wrap_async, # async does not support pipelining
|
||||
|
||||
@@ -335,17 +335,17 @@ class Connection(ConnectionBase):
|
||||
|
||||
def exec_command(self, cmd, in_data=None, sudoable=True):
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
cmd_parts = self._shell._encode_script(exec_wrapper, as_list=True, strict_mode=False, preserve_rc=False)
|
||||
cmd_parts = self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False)
|
||||
|
||||
# TODO: display something meaningful here
|
||||
display.vvv("EXEC (via pipeline wrapper)")
|
||||
|
||||
if not in_data:
|
||||
payload = self._create_raw_wrapper_payload(cmd)
|
||||
else:
|
||||
payload = in_data
|
||||
stdin_iterator = None
|
||||
|
||||
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True, stdin_iterator=self._wrapper_payload_stream(payload))
|
||||
if in_data:
|
||||
stdin_iterator = self._wrapper_payload_stream(in_data)
|
||||
|
||||
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True, stdin_iterator=stdin_iterator)
|
||||
|
||||
result.std_out = to_bytes(result.std_out)
|
||||
result.std_err = to_bytes(result.std_err)
|
||||
|
||||
@@ -55,7 +55,8 @@ begin {
|
||||
# stream JSON including become_pw, ps_module_payload, bin_module_payload, become_payload, write_payload_path, preserve directives
|
||||
# exec runspace, capture output, cleanup, return module output
|
||||
|
||||
$json_raw = ""
|
||||
# NB: do not adjust the following line- it is replaced when doing non-streamed module output
|
||||
$json_raw = ''
|
||||
}
|
||||
process {
|
||||
$input_as_string = [string]$input
|
||||
@@ -1102,7 +1103,7 @@ class ShellModule(object):
|
||||
def build_module_command(self, env_string, shebang, cmd, arg_path=None, rm_tmp=None):
|
||||
# pipelining bypass
|
||||
if cmd == '':
|
||||
return ''
|
||||
return '-'
|
||||
|
||||
# non-pipelining
|
||||
|
||||
@@ -1194,15 +1195,22 @@ class ShellModule(object):
|
||||
def _encode_script(self, script, as_list=False, strict_mode=True, preserve_rc=True):
|
||||
'''Convert a PowerShell script to a single base64-encoded command.'''
|
||||
script = to_text(script)
|
||||
if strict_mode:
|
||||
script = u'Set-StrictMode -Version Latest\r\n%s' % script
|
||||
# try to propagate exit code if present- won't work with begin/process/end-style scripts (ala put_file)
|
||||
# NB: the exit code returned may be incorrect in the case of a successful command followed by an invalid command
|
||||
if preserve_rc:
|
||||
script = u'%s\r\nIf (-not $?) { If (Get-Variable LASTEXITCODE -ErrorAction SilentlyContinue) { exit $LASTEXITCODE } Else { exit 1 } }\r\n' % script
|
||||
script = '\n'.join([x.strip() for x in script.splitlines() if x.strip()])
|
||||
encoded_script = base64.b64encode(script.encode('utf-16-le'))
|
||||
cmd_parts = _common_args + ['-EncodedCommand', encoded_script]
|
||||
|
||||
if script == u'-':
|
||||
cmd_parts = _common_args + ['-']
|
||||
|
||||
else:
|
||||
if strict_mode:
|
||||
script = u'Set-StrictMode -Version Latest\r\n%s' % script
|
||||
# try to propagate exit code if present- won't work with begin/process/end-style scripts (ala put_file)
|
||||
# NB: the exit code returned may be incorrect in the case of a successful command followed by an invalid command
|
||||
if preserve_rc:
|
||||
script = u'%s\r\nIf (-not $?) { If (Get-Variable LASTEXITCODE -ErrorAction SilentlyContinue) { exit $LASTEXITCODE } Else { exit 1 } }\r\n'\
|
||||
% script
|
||||
script = '\n'.join([x.strip() for x in script.splitlines() if x.strip()])
|
||||
encoded_script = base64.b64encode(script.encode('utf-16-le'))
|
||||
cmd_parts = _common_args + ['-EncodedCommand', encoded_script]
|
||||
|
||||
if as_list:
|
||||
return cmd_parts
|
||||
return ' '.join(cmd_parts)
|
||||
|
||||
Reference in New Issue
Block a user