1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/console/tests/test_sshtunnels.py
catborise 0cd687e035 fix(console): a VNC console stays open while its screen does not change
nginx ends a proxied websocket that sends nothing for 60 seconds, and the
VNC console sends nothing while the VM's screen stays the same: an idle
console, an installer's progress screen, closed after a minute and had to
be connected again. novncd now pings the browser every 30 seconds. The SSH
tunnel of an SSH compute sends a keepalive as often, for a firewall on the
way that ends idle connections.
2026-11-01 16:18:34 +03:00

58 lines
2.7 KiB
Python

"""The SSH console tunnel: ssh -W to the console's address or socket, never a
prompt, no remote shell, and its error output when it fails."""
import sys
import time
from unittest import TestCase
from unittest.mock import patch
from console import sshtunnels
class SshTunnelTests(TestCase):
def test_ssh_forwards_to_the_console_without_a_shell_or_a_prompt(self):
with patch.object(sshtunnels.subprocess, "Popen") as popen:
sock, tunnel = sshtunnels.open_tunnel("kvm1.example.com", "root", "127.0.0.1:5900")
sock.close()
self.assertEqual(popen.call_args.args[0], [
"ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10", "-o", "ServerAliveInterval=30", "-W", "127.0.0.1:5900",
"-l", "root", "--", "kvm1.example.com"])
self.assertIs(tunnel.process, popen.return_value)
def test_the_target_on_the_compute(self):
for address, unix_socket, target in (
("192.0.2.40", None, "192.0.2.40:5900"),
("2001:db8::40", None, "[2001:db8::40]:5900"),
(None, None, "127.0.0.1:5900"),
("0.0.0.0", None, "127.0.0.1:5900"),
("::", None, "[::1]:5900"),
("localhost", None, "localhost:5900"),
("127.0.0.1", "/run/vnc.sock", "/run/vnc.sock"),
):
with self.subTest(address=address, unix_socket=unix_socket):
self.assertEqual(sshtunnels.tunnel_target(address, 5900, unix_socket), target)
def fake_ssh(self, code):
"""A Tunnel whose process stands in for ssh."""
return sshtunnels.Tunnel([sys.executable, "-c", code], None)
def test_a_failed_tunnel_reports_what_ssh_said(self):
failing = self.fake_ssh("import sys; sys.stderr.write('Host key verification failed.'); sys.exit(255)")
failing.process.wait() # ssh fails at once, before the session
self.assertEqual(failing.close(), "Host key verification failed.")
ended = self.fake_ssh("import time; time.sleep(30)")
self.assertEqual(ended.close(), "") # ended with the session: no error
def test_much_error_output_does_not_stop_the_tunnel(self):
# a pipe nobody reads stopped the writer at 64 KiB
chatty = self.fake_ssh("import sys; sys.stderr.write('x' * 1_000_000); sys.exit(255)")
chatty.process.wait(10)
self.assertEqual(len(chatty.close()), 1_000_000)
def test_an_ssh_that_ignores_terminate_is_killed(self):
stubborn = self.fake_ssh("import signal, time; signal.signal(signal.SIGTERM, signal.SIG_IGN); time.sleep(60)")
time.sleep(1) # the handler is set
started = time.monotonic()
stubborn.close()
self.assertLess(time.monotonic() - started, 10)
self.assertEqual(stubborn.process.returncode, -9)