ansible: better emulate _low_level_execute_command()

Still needs a ton of work to emulate argument handling, shell selection,
and output emulation in every case. Unsurprisingly, Ansible documents
none of this.
wip-fakessh-exit-status
David Wilson 7 years ago
parent 5855f1739f
commit f3315fc172

@ -26,6 +26,8 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import json import json
import os
import pwd
import subprocess import subprocess
import time import time
@ -112,19 +114,41 @@ def run_module(module, raw_params=None, args=None):
return json.dumps(e.dct) return json.dumps(e.dct)
def exec_command(cmd, in_data=''): def get_user_shell():
""" """
Run a command in subprocess, arranging for `in_data` to be supplied on its For commands executed directly via an SSH command-line, SSH looks up the
standard input. user's shell via getpwuid() and only defaults to /bin/sh if that field is
missing or empty.
"""
try:
pw_shell = pwd.getpwuid(os.geteuid()).pw_shell
except KeyError:
pw_shell = None
return pw_shell or '/bin/sh'
def exec_command(cmd, in_data='', chdir=None, shell=None):
"""
Run a command in a subprocess, emulating the argument handling behaviour of
SSH.
:param bytes cmd:
String command line, passed to user's shell.
:param bytes in_data:
Optional standard input for the command.
:return: :return:
(return code, stdout bytes, stderr bytes) (return code, stdout bytes, stderr bytes)
""" """
proc = subprocess.Popen(cmd, assert isinstance(cmd, basestring)
proc = subprocess.Popen(
args=[get_user_shell(), '-c', cmd],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
shell=True) cwd=chdir,
)
stdout, stderr = proc.communicate(in_data) stdout, stderr = proc.communicate(in_data)
return proc.returncode, stdout, stderr return proc.returncode, stdout, stderr

@ -26,6 +26,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import from __future__ import absolute_import
import commands
import os import os
import pwd import pwd
import shutil import shutil
@ -177,17 +178,24 @@ class ActionModuleMixin(ansible.plugins.action.ActionBase):
def _low_level_execute_command(self, cmd, sudoable=True, in_data=None, def _low_level_execute_command(self, cmd, sudoable=True, in_data=None,
executable=None, executable=None,
encoding_errors='surrogate_then_replace'): encoding_errors='surrogate_then_replace',
chdir=None):
if executable is None: # executable defaults to False
executable = self._play_context.executable
if executable:
cmd = executable + ' -c ' + commands.mkarg(cmd)
# replaces 57 lines # replaces 57 lines
# replaces 126 lines of make_become_cmd() # replaces 126 lines of make_become_cmd()
rc, stdout, stderr = self.call( rc, stdout, stderr = self.call(
ansible_mitogen.helpers.exec_command, ansible_mitogen.helpers.exec_command,
cmd, cast(cmd),
in_data, cast(in_data),
chdir=cast(chdir),
) )
return { return {
'rc': rc, 'rc': rc,
'stdout': to_text(stdout, encoding_errors), 'stdout': to_text(stdout, errors=encoding_errors),
'stdout_lines': '\n'.split(to_text(stdout, encoding_errors)), 'stdout_lines': to_text(stdout, errors=encoding_errors).splitlines(),
'stderr': stderr, 'stderr': stderr,
} }

@ -0,0 +1,32 @@
---
# Verify the behaviour of _low_level_execute_command().
- hosts: all
gather_facts: false
tasks:
# "echo -en" to test we actually hit bash shell too.
- name: Run raw module without sudo
raw: 'echo -en $((1 + 1))'
register: raw
- name: Verify raw module output.
assert:
that:
- 'raw.rc == 0'
- 'raw.stdout_lines == ["2"]'
- 'raw.stdout == "2"'
- name: Run raw module with sudo
become: true
raw: 'whoami'
register: raw
# Can't test stdout because TTY inserts \r in Ansible version.
- debug: msg={{raw}}
- name: Verify raw module output.
assert:
that:
- 'raw.rc == 0'
- 'raw.stdout_lines == ["root"]'
Loading…
Cancel
Save