win_script: work when argument exceeds stdin buffer size (#33855)

* win_script: work when argument exceeds stdin buffer size

* Added test for large argument being passed
pull/35031/head
Jordan Borean 7 years ago committed by GitHub
parent e6d01ff7e3
commit f0134079e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -22,8 +22,9 @@ import re
import shlex import shlex
from ansible.errors import AnsibleError, AnsibleAction, AnsibleActionDone, AnsibleActionFail, AnsibleActionSkip from ansible.errors import AnsibleError, AnsibleAction, AnsibleActionDone, AnsibleActionFail, AnsibleActionSkip
from ansible.module_utils._text import to_native, to_text from ansible.module_utils._text import to_bytes, to_native, to_text
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
from ansible.plugins.shell.powershell import exec_wrapper
class ActionModule(ActionBase): class ActionModule(ActionBase):
@ -114,9 +115,14 @@ class ActionModule(ActionBase):
script_cmd = self._connection._shell.wrap_for_exec(script_cmd) script_cmd = self._connection._shell.wrap_for_exec(script_cmd)
exec_data = None exec_data = None
# HACK: come up with a sane way to pass around env outside the command # WinRM requires a special wrapper to work with environment variables
if self._connection.transport == "winrm": if self._connection.transport == "winrm":
exec_data = self._connection._create_raw_wrapper_payload(script_cmd, env_dict) pay = self._connection._create_raw_wrapper_payload(script_cmd,
env_dict)
exec_data = exec_wrapper.replace(b"$json_raw = ''",
b"$json_raw = @'\r\n%s\r\n'@"
% to_bytes(pay))
script_cmd = "-"
result.update(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir)) result.update(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir))

@ -391,8 +391,6 @@ class Connection(ConnectionBase):
stdin_push_failed = False stdin_push_failed = False
command_id = self.protocol.run_command(self.shell_id, to_bytes(command), map(to_bytes, args), console_mode_stdin=(stdin_iterator is None)) command_id = self.protocol.run_command(self.shell_id, to_bytes(command), map(to_bytes, args), console_mode_stdin=(stdin_iterator is None))
# TODO: try/except around this, so we can get/return the command result on a broken pipe or other failure (probably more useful than the 500 that
# comes from this)
try: try:
if stdin_iterator: if stdin_iterator:
for (data, is_last) in stdin_iterator: for (data, is_last) in stdin_iterator:
@ -400,12 +398,9 @@ class Connection(ConnectionBase):
except Exception as ex: except Exception as ex:
from traceback import format_exc from traceback import format_exc
display.warning("FATAL ERROR DURING FILE TRANSFER: %s" % format_exc()) display.warning("FATAL ERROR DURING FILE TRANSFER: %s" % to_text(ex))
stdin_push_failed = True stdin_push_failed = True
if stdin_push_failed:
raise AnsibleError('winrm send_input failed')
# NB: this can hang if the receiver is still running (eg, network failed a Send request but the server's still happy). # NB: this can hang if the receiver is still running (eg, network failed a Send request but the server's still happy).
# FUTURE: Consider adding pywinrm status check/abort operations to see if the target is still running after a failure. # FUTURE: Consider adding pywinrm status check/abort operations to see if the target is still running after a failure.
resptuple = self.protocol.get_command_output(self.shell_id, command_id) resptuple = self.protocol.get_command_output(self.shell_id, command_id)
@ -423,7 +418,11 @@ class Connection(ConnectionBase):
display.vvvvvv('WINRM STDERR %s' % to_text(response.std_err), host=self._winrm_host) display.vvvvvv('WINRM STDERR %s' % to_text(response.std_err), host=self._winrm_host)
if stdin_push_failed: if stdin_push_failed:
raise AnsibleError('winrm send_input failed; \nstdout: %s\nstderr %s' % (response.std_out, response.std_err)) stderr = to_bytes(response.std_err, encoding='utf-8')
if self.is_clixml(stderr):
stderr = self.parse_clixml_stream(stderr)
raise AnsibleError('winrm send_input failed; \nstdout: %s\nstderr %s' % (response.std_out, stderr))
return response return response
finally: finally:
@ -456,7 +455,9 @@ class Connection(ConnectionBase):
'powershell_modules': {}, 'powershell_modules': {},
'actions': ['exec'], 'actions': ['exec'],
'exec': to_text(base64.b64encode(to_bytes(leaf_exec))), 'exec': to_text(base64.b64encode(to_bytes(leaf_exec))),
'environment': environment 'environment': environment,
'min_ps_version': None,
'min_os_version': None
} }
return json.dumps(payload) return json.dumps(payload)

@ -54,6 +54,24 @@
- "test_script_with_args_result is not failed" - "test_script_with_args_result is not failed"
- "test_script_with_args_result is changed" - "test_script_with_args_result is changed"
# Bug: https://github.com/ansible/ansible/issues/32850
- name: set fact of long string
set_fact:
long_string: "{{ lookup('pipe', 'printf \"a%.0s\" {1..1000}') }}"
- name: run test script with args that exceed the stdin buffer
script: test_script_with_args.ps1 {{ long_string }}
register: test_script_with_large_args_result
- name: check that script ran and received arguments correctly
assert:
that:
- test_script_with_large_args_result.rc == 0
- test_script_with_large_args_result.stdout == long_string + "\r\n"
- not test_script_with_large_args_result.stderr
- test_script_with_large_args_result is not failed
- test_script_with_large_args_result is changed
- name: run test script that takes parameters passed via splatting - name: run test script that takes parameters passed via splatting
script: test_script_with_splatting.ps1 @{ This = 'this'; That = '{{ test_win_script_value }}'; Other = 'other'} script: test_script_with_splatting.ps1 @{ This = 'this'; That = '{{ test_win_script_value }}'; Other = 'other'}
register: test_script_with_splatting_result register: test_script_with_splatting_result
@ -201,20 +219,17 @@
- "test_script_bool_result.stdout_lines[0] == 'System.Boolean'" - "test_script_bool_result.stdout_lines[0] == 'System.Boolean'"
- "test_script_bool_result.stdout_lines[1] == 'True'" - "test_script_bool_result.stdout_lines[1] == 'True'"
# FIXME: re-enable this test once script can run under the wrapper with powershell - name: run test script that uses envvars
#- name: run test script that uses envvars script: test_script_with_env.ps1
# script: test_script_with_env.ps1 environment:
# environment: taskenv: task
# taskenv: task register: test_script_env_result
# register: test_script_env_result
#
#- name: ensure that script ran and that environment var was passed
# assert:
# that:
# - test_script_env_result is successful
# - test_script_env_result.stdout_lines[0] == 'task'
#
- name: ensure that script ran and that environment var was passed
assert:
that:
- test_script_env_result is successful
- test_script_env_result.stdout_lines[0] == 'task'
# check mode # check mode
- name: Run test script that creates a file in check mode - name: Run test script that creates a file in check mode

Loading…
Cancel
Save