import stat import sys import pytest # Skip this module if on Windows. Unix sockets are not available on Windows. pytestmark = pytest.mark.skipif( sys.platform.startswith("win"), reason="Unix sockets are not available on Windows." ) import os import subprocess import time from jupyter_server.utils import urlencode_unix_socket, urlencode_unix_socket_path @pytest.mark.integration_test def test_shutdown_sock_server_integration(jp_unix_socket_file): url = urlencode_unix_socket(jp_unix_socket_file).encode() encoded_sock_path = urlencode_unix_socket_path(jp_unix_socket_file) p = subprocess.Popen( ["jupyter-server", "--sock=%s" % jp_unix_socket_file, "--sock-mode=0700"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) complete = False for line in iter(p.stderr.readline, b""): if url in line: complete = True break assert complete, "did not find socket URL in stdout when launching notebook" socket_path = encoded_sock_path.encode() assert socket_path in subprocess.check_output(["jupyter-server", "list"]) # Ensure umask is properly applied. assert stat.S_IMODE(os.lstat(jp_unix_socket_file).st_mode) == 0o700 try: subprocess.check_output(["jupyter-server", "stop"], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: assert "There is currently no server running on" in e.output.decode() else: raise AssertionError("expected stop command to fail due to target mis-match") assert encoded_sock_path.encode() in subprocess.check_output(["jupyter-server", "list"]) subprocess.check_output(["jupyter-server", "stop", jp_unix_socket_file]) assert encoded_sock_path.encode() not in subprocess.check_output(["jupyter-server", "list"]) p.wait() @pytest.mark.integration_test def test_sock_server_validate_sockmode_type(): try: subprocess.check_output( ["jupyter-server", "--sock=/tmp/nonexistent", "--sock-mode=badbadbad"], stderr=subprocess.STDOUT, ) except subprocess.CalledProcessError as e: assert "badbadbad" in e.output.decode() else: raise AssertionError("expected execution to fail due to validation of --sock-mode param") @pytest.mark.integration_test def test_sock_server_validate_sockmode_accessible(): try: subprocess.check_output( ["jupyter-server", "--sock=/tmp/nonexistent", "--sock-mode=0444"], stderr=subprocess.STDOUT, ) except subprocess.CalledProcessError as e: assert "0444" in e.output.decode() else: raise AssertionError("expected execution to fail due to validation of --sock-mode param") def _ensure_stopped(check_msg="There are no running servers"): try: subprocess.check_output(["jupyter-server", "stop"], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: assert check_msg in e.output.decode() else: raise AssertionError("expected all servers to be stopped") @pytest.mark.integration_test def test_stop_multi_integration(jp_unix_socket_file, jp_http_port): """Tests lifecycle behavior for mixed-mode server types w/ default ports. Mostly suitable for local dev testing due to reliance on default port binding. """ TEST_PORT = "9797" MSG_TMPL = "Shutting down server on {}..." _ensure_stopped() # Default port. p1 = subprocess.Popen(["jupyter-server", "--no-browser"]) # Unix socket. p2 = subprocess.Popen(["jupyter-server", "--sock=%s" % jp_unix_socket_file]) # Specified port p3 = subprocess.Popen(["jupyter-server", "--no-browser", "--port=%s" % TEST_PORT]) time.sleep(3) shutdown_msg = MSG_TMPL.format(jp_http_port) assert shutdown_msg in subprocess.check_output(["jupyter-server", "stop"]).decode() _ensure_stopped("There is currently no server running on 8888") assert ( MSG_TMPL.format(jp_unix_socket_file) in subprocess.check_output(["jupyter-server", "stop", jp_unix_socket_file]).decode() ) assert ( MSG_TMPL.format(TEST_PORT) in subprocess.check_output(["jupyter-server", "stop", TEST_PORT]).decode() ) _ensure_stopped() p1.wait() p2.wait() p3.wait() @pytest.mark.integration_test def test_launch_socket_collision(jp_unix_socket_file): """Tests UNIX socket in-use detection for lifecycle correctness.""" sock = jp_unix_socket_file check_msg = "socket %s is already in use" % sock _ensure_stopped() # Start a server. cmd = ["jupyter-server", "--sock=%s" % sock] p1 = subprocess.Popen(cmd) time.sleep(3) # Try to start a server bound to the same UNIX socket. try: subprocess.check_output(cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as cpe: assert check_msg in cpe.output.decode() except Exception as ex: raise AssertionError(f"expected 'already in use' error, got '{ex}'!") else: raise AssertionError("expected 'already in use' error, got success instead!") # Stop the background server, ensure it's stopped and wait on the process to exit. subprocess.check_call(["jupyter-server", "stop", sock]) _ensure_stopped() p1.wait()