Fix a deployment buffering issue spotted by @pquerna which could happen if the the executed command produced a lot of output.
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/a053cde5 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/a053cde5 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/a053cde5 Branch: refs/heads/trunk Commit: a053cde54fb6fffe8666aa01de46a354115f4f40 Parents: 6e53bff Author: Tomaz Muraus <[email protected]> Authored: Sun Dec 29 00:31:20 2013 +0100 Committer: Tomaz Muraus <[email protected]> Committed: Sun Dec 29 00:43:12 2013 +0100 ---------------------------------------------------------------------- libcloud/compute/ssh.py | 52 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/a053cde5/libcloud/compute/ssh.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/ssh.py b/libcloud/compute/ssh.py index 7cbcee6..88094a5 100644 --- a/libcloud/compute/ssh.py +++ b/libcloud/compute/ssh.py @@ -36,6 +36,11 @@ from os.path import split as psplit from os.path import join as pjoin from libcloud.utils.logging import ExtraLogFormatter +from libcloud.utils.py3 import StringIO + + +# Maximum number of bytes to read at once from a socket +CHUNK_SIZE = 1024 class BaseSSHClient(object): @@ -254,24 +259,53 @@ class ParamikoSSHClient(BaseSSHClient): chan.exec_command(cmd) - stdin = chan.makefile('wb', bufsize) - stdout = chan.makefile('rb', bufsize) - stderr = chan.makefile_stderr('rb', bufsize) + stdout = StringIO() + stderr = StringIO() + # Create a stdin file and immediately close it to prevent any + # interactive script from hanging the process. + stdin = chan.makefile('wb', bufsize) stdin.close() + # Receive all the output + # Note: This is used instead of chan.makefile approach to prevent + # buffering issues and hanging if the executed command produces a lot + # of output. + while not chan.exit_status_ready(): + if chan.recv_ready(): + data = chan.recv(CHUNK_SIZE) + + while data: + stdout.write(data) + ready = chan.recv_ready() + + if not ready: + break + + data = chan.recv(CHUNK_SIZE) + + if chan.recv_stderr_ready(): + data = chan.recv_stderr(CHUNK_SIZE) + + while data: + stderr.write(data) + ready = chan.recv_stderr_ready() + + if not ready: + break + + data = chan.recv_stderr(CHUNK_SIZE) + # Receive the exit status code of the command we ran. - # Note: If the command hasn't finished yet, this method will block - # until it does status = chan.recv_exit_status() - so = stdout.read() - se = stderr.read() + stdout = stdout.getvalue() + stderr = stderr.getvalue() - extra = {'_status': status, '_stdout': so, '_stderr': se} + extra = {'_status': status, '_stdout': stdout, '_stderr': stderr} self.logger.debug('Command finished', extra=extra) - return [so, se, status] + return [stdout, stderr, status] def close(self): self.logger.debug('Closing server connection')
