Skip to content

Commit

Permalink
Issue#235 Can't execute background process on remote server
Browse files Browse the repository at this point in the history
  • Loading branch information
wherka-ama authored and Waldek Herka committed Jun 27, 2022
1 parent 51ca8ee commit bbb22ce
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 9 deletions.
10 changes: 8 additions & 2 deletions jumpssh/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ def run_cmd(
success_exit_code=0,
retry=0,
retry_interval=5,
keep_retry_history=False
keep_retry_history=False,
get_pty=True
):
""" Run command on the remote host and return result locally
Expand All @@ -247,6 +248,10 @@ def run_cmd(
:param retry_interval: number of seconds between each retry
:param keep_retry_history: if True, all retries results are kept and accessible in return result
default is False as we don't want to save by default all output for all retries especially for big output
:param get_pty: if True, opens a pseudo-terminal from the server
This is used after creating a client channel, to ask the server to provide some basic terminal semantics
for a shell invoked with invoke_shell.
It isn't necessary to call this method for a single command with exec_command.
:raises TimeoutError: if command run longer than the specified timeout
:raises TypeError: if `cmd` parameter is neither a string neither a list of string
:raises SSHException: if current SSHSession is already closed
Expand Down Expand Up @@ -315,7 +320,8 @@ def run_cmd(
# Commands executed after this point will see the forwarded agent on the remote end.

channel.set_combine_stderr(True)
channel.get_pty()
if get_pty:
channel.get_pty()
channel.exec_command(my_cmd)

# prepare timer for timeout
Expand Down
15 changes: 8 additions & 7 deletions tests/test__session.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def test_run_cmd_interrupt_remote_command(docker_env, monkeypatch, caplog):
with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')):
# request to terminate remote command simulating the user entering "Y" in the terminal
monkeypatch.setattr(mock_input, lambda x: "Y")
gateway_session.run_cmd('sleep 30')
gateway_session.run_cmd('sleep 30', get_pty=True)

# check command is no longer running on remote host
assert gateway_session.get_exit_code('ps aux | grep -v grep | grep "sleep 30"') == 1
Expand All @@ -287,7 +287,7 @@ def test_run_cmd_interrupt_remote_command(docker_env, monkeypatch, caplog):
with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')):
# request to terminate remote command simulating the user entering "N" in the terminal
monkeypatch.setattr(mock_input, lambda x: "N")
gateway_session.run_cmd('sleep 40')
gateway_session.run_cmd('sleep 40', get_pty=True)

# check command is still running on remote host
assert gateway_session.get_exit_code('ps aux | grep -v grep | grep "sleep 40"') == 0
Expand All @@ -298,7 +298,7 @@ def test_run_cmd_interrupt_remote_command(docker_env, monkeypatch, caplog):
with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')):
# send empty string simulating the user pressing enter in the terminal
monkeypatch.setattr(mock_input, lambda x: '')
gateway_session.run_cmd('sleep 50')
gateway_session.run_cmd('sleep 50', get_pty=True)

# check command is no longer running on remote host
assert gateway_session.get_exit_code('ps aux | grep -v grep | grep "sleep 50"') == 1
Expand All @@ -309,7 +309,7 @@ def test_run_cmd_interrupt_remote_command(docker_env, monkeypatch, caplog):
with mock.patch('select.select', side_effect=KeyboardInterrupt('Fake Ctrl-C')):
# user press a second time Contrl-C
with mock.patch(mock_input, side_effect=KeyboardInterrupt('2nd Fake Ctrl-C')):
gateway_session.run_cmd('sleep 60')
gateway_session.run_cmd('sleep 60', get_pty=True)

# check command is still running on remote host
assert gateway_session.get_exit_code('ps aux | grep -v grep | grep "sleep 60"') == 0
Expand All @@ -323,7 +323,7 @@ def test_run_cmd_interrupt_remote_command(docker_env, monkeypatch, caplog):
# but user answered after 4s while command finished after 3s so underline channel is already closed
# and command still successfully run
monkeypatch.setattr(mock_input, lambda x: time.sleep(4) or "Y")
gateway_session.run_cmd('sleep 3')
gateway_session.run_cmd('sleep 3', get_pty=True)
assert 'Remote command execution already finished with exit code' in caplog.text

# 6. user press Contrl-C once but take time to answer if remote must be closed or not, and channel is closed
Expand All @@ -334,7 +334,7 @@ def test_run_cmd_interrupt_remote_command(docker_env, monkeypatch, caplog):
# request to terminate remote command simulating the user entering "Y" in the terminal
# but user answered after 4s while command finished after 3s so underline channel is already closed
monkeypatch.setattr('paramiko.channel.Channel.recv_exit_status', lambda x: -1)
gateway_session.run_cmd('sleep 3')
gateway_session.run_cmd('sleep 3', get_pty=True)
assert 'Unable to terminate remote command because channel is closed.' in caplog.text


Expand Down Expand Up @@ -370,7 +370,8 @@ def test_input_data(docker_env):

# with input given, command should run correctly and return the value entered
assert gateway_session.get_cmd_output(commands,
input_data={'Requesting user input value': 'dummy_value'}
input_data={'Requesting user input value': 'dummy_value'},
get_pty=True
).split()[-1] == "dummy_value"


Expand Down

0 comments on commit bbb22ce

Please sign in to comment.