From 6a652b989304efe973ba920b3dbfbd2801b24bda Mon Sep 17 00:00:00 2001 From: Jon Burgess Date: Wed, 16 Oct 2024 13:10:40 +0100 Subject: [PATCH] Prevent Paramiko deadlock when test sends more than 2MB to stdout The paramiko library has a known problem where checking the exit status can cause a deadlock if the command has written a lot of output to the stdout (or stderr) channel: https://docs.paramiko.org/en/stable/api/channel.html#paramiko.channel.Channel.recv_exit_status We can work around this, for the stdout case, by reading the data before checking the exit status. --- test/test_backends.py | 9 +++++++++ testinfra/backend/paramiko.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/test/test_backends.py b/test/test_backends.py index b805eeb4..c3b717d5 100644 --- a/test/test_backends.py +++ b/test/test_backends.py @@ -640,3 +640,12 @@ def test_get_hosts(): ("a", 10), ("a", 1), ] + + +@pytest.mark.testinfra_hosts(*HOSTS) +def test_command_deadlock(host): + # Test for deadlock when exceeding Paramiko transport buffer (2MB) + # https://docs.paramiko.org/en/latest/api/channel.html#paramiko.channel.Channel.recv_exit_status + size = 3 * 1024 * 1024 + output = host.check_output(f"python3 -c 'print(\"a\" * {size})'") + assert len(output) == size diff --git a/testinfra/backend/paramiko.py b/testinfra/backend/paramiko.py index 4e5eae0b..65b5643c 100644 --- a/testinfra/backend/paramiko.py +++ b/testinfra/backend/paramiko.py @@ -138,9 +138,9 @@ def _exec_command(self, command: bytes) -> tuple[int, bytes, bytes]: if self.get_pty: chan.get_pty() chan.exec_command(command) - rc = chan.recv_exit_status() stdout = b"".join(chan.makefile("rb")) stderr = b"".join(chan.makefile_stderr("rb")) + rc = chan.recv_exit_status() return rc, stdout, stderr def run(self, command: str, *args: str, **kwargs: Any) -> base.CommandResult: