diff --git a/console/novncd b/console/novncd index 823b688a..779016a6 100755 --- a/console/novncd +++ b/console/novncd @@ -200,7 +200,7 @@ if __name__ == "__main__": options.verbose = options.verbose or options.debug # nginx ends TLS: novncd speaks plain websockets on the loopback - WebSocketProxy( + server = WebSocketProxy( RequestHandlerClass=ConsoleProxyHandler, listen_host=options.host, listen_port=options.port, @@ -212,4 +212,10 @@ if __name__ == "__main__": # a ping while the VM's screen stays the same: nginx, and a proxy on # the way, end a websocket that sends nothing for 60 seconds heartbeat=30, - ).start_server() + ) + try: + server.start_server() + except SystemExit as stop: + # websockify ends with status 0 when it cannot listen (the port is + # taken): systemd's Restart=on-failure would not start it again + raise SystemExit(stop.code or 1) from None diff --git a/console/tests/test_novncd_exit.py b/console/tests/test_novncd_exit.py new file mode 100644 index 00000000..fe883928 --- /dev/null +++ b/console/tests/test_novncd_exit.py @@ -0,0 +1,22 @@ +"""novncd that cannot listen ends with a failure status, which systemd restarts.""" + +import socket +import subprocess +import sys +from pathlib import Path + +from django.test import SimpleTestCase + +ROOT = Path(__file__).resolve().parent.parent.parent + + +class NovncdExitTestCase(SimpleTestCase): + def test_a_taken_port_ends_novncd_with_a_failure_status(self): + with socket.socket() as taken: + taken.bind(("127.0.0.1", 0)) + taken.listen() + port = taken.getsockname()[1] + novncd = subprocess.run([sys.executable, str(ROOT / "console" / "novncd"), "-v", "-H", "127.0.0.1", + "-p", str(port)], cwd=ROOT, capture_output=True, timeout=60) + self.assertNotEqual(novncd.returncode, 0) + self.assertIn(b"Openening socket failed", novncd.stderr)