PIPE = Sentinel('PIPE')
+class KludgeFile(object):
+ """
+ Wrap Paramiko's ChannelFile in a way that lets ``f.close()``
+ actually cause an EOF for the remote command.
+ """
+ def __init__(self, wrapped):
+ self._wrapped = wrapped
+
+ def __getattr__(self, name):
+ return getattr(self._wrapped, name)
+
+ def close(self):
+ self._wrapped.close()
+ self._wrapped.channel.shutdown_write()
+
def run(
client, args,
stdin=None, stdout=None, stderr=None,
"""
r = execute(client, args)
+ r.stdin = KludgeFile(wrapped=r.stdin)
+
g_in = None
if stdin is not PIPE:
g_in = gevent.spawn(copy_and_close, stdin, r.stdin)
if stdout is None:
stdout = logger.getChild('out')
- copy_file_to(r.stdout, stdout)
+ g_out = gevent.spawn(copy_file_to, r.stdout, stdout)
r.stdout = stdout
- g_err.get()
- if g_in is not None:
- g_in.get()
-
def _check_status(status):
+ g_err.get()
+ g_out.get()
+ if g_in is not None:
+ g_in.get()
+
status = status()
if check_status:
if status is None:
from .. import monkey; monkey.patch_all()
from nose.tools import eq_ as eq
+from cStringIO import StringIO
import os
import nose
)
eq(e.command, "sh -c 'kill -ABRT $PPID'")
eq(str(e), "SSH connection was lost: \"sh -c 'kill -ABRT $PPID'\"")
+
+def test_pipe():
+ ssh = connection.connect(HOST)
+ r = run.run(
+ client=ssh,
+ args=['cat'],
+ stdin=run.PIPE,
+ stdout=StringIO(),
+ wait=False,
+ )
+ eq(r.stdout.getvalue(), '')
+ r.stdin.write('foo\n')
+ r.stdin.write('bar\n')
+ r.stdin.close()
+
+ got = r.exitstatus.get()
+ eq(got, 0)
+ eq(r.stdout.getvalue(), 'foo\nbar\n')
err = fudge.Fake('ChannelFile(stderr)')
cmd.returns((in_, out, err))
in_.expects('close').with_args()
+ in_chan = fudge.Fake('channel')
+ in_chan.expects('shutdown_write').with_args()
+ in_.has_attr(channel=in_chan)
out.expects('xreadlines').with_args().returns(['foo', 'bar'])
err.expects('xreadlines').with_args().returns(['bad'])
logger = fudge.Fake('logger')
err = fudge.Fake('ChannelFile(stderr)')
cmd.returns((in_, out, err))
in_.expects('close').with_args()
+ in_chan = fudge.Fake('channel')
+ in_chan.expects('shutdown_write').with_args()
+ in_.has_attr(channel=in_chan)
out.remember_order()
out.expects('read').with_args().returns('foo\nb')
out.expects('read').with_args().returns('ar\n')