Spawn a Teleport proxy for the provided app_name.
(cls, app_name: str, port: str)
| 21 | class TeleportProxy: |
| 22 | @classmethod |
| 23 | def spawn(cls, app_name: str, port: str): |
| 24 | """Spawn a Teleport proxy for the provided app_name.""" |
| 25 | |
| 26 | teleport_state = build_config.TeleportLocalState.read() |
| 27 | |
| 28 | # If there is already a Teleport proxy running, no need to restart one. |
| 29 | running_pid = TeleportProxy.check(app_name) |
| 30 | if running_pid: |
| 31 | ui.say(f"Teleport proxy already running, PID: {running_pid}") |
| 32 | return |
| 33 | else: |
| 34 | # If the existing PID doesn't exist, clear it from state. |
| 35 | teleport_state.set_pid(app_name, None) |
| 36 | teleport_state.set_address(app_name, None) |
| 37 | teleport_state.write() |
| 38 | |
| 39 | # Otherwise spawn a Teleport proxy. |
| 40 | cmd_args = ["tsh", "proxy", "app", f"{app_name}", "--port", port] |
| 41 | child = subprocess.Popen( |
| 42 | cmd_args, |
| 43 | stdout=subprocess.DEVNULL, |
| 44 | stderr=subprocess.DEVNULL, |
| 45 | preexec_fn=os.setpgrp, |
| 46 | ) |
| 47 | ui.say(f"starting Teleport proxy for '{app_name}'...") |
| 48 | |
| 49 | def wait(child, teleport_state, address): |
| 50 | wait_start = time.time() |
| 51 | |
| 52 | while time.time() - wait_start < 2: |
| 53 | child_terminated = child.poll() |
| 54 | if child_terminated: |
| 55 | other_tshs = [ |
| 56 | p.pid |
| 57 | for p in psutil.process_iter(["pid", "name"]) |
| 58 | if p.name() == "tsh" |
| 59 | ] |
| 60 | ui.warn(dedent(f""" |
| 61 | Teleport proxy failed to start, 'tsh' process already running! |
| 62 | existing 'tsh' processes: {other_tshs} |
| 63 | exit code: {child_terminated} |
| 64 | """)) |
| 65 | break |
| 66 | |
| 67 | # Timed out! Check if the process is running. |
| 68 | child_pid_status = psutil.pid_exists(child.pid) |
| 69 | if child_pid_status: |
| 70 | # Record the PID, if the process started successfully. |
| 71 | teleport_state.set_pid(app_name, child.pid) |
| 72 | teleport_state.set_address(app_name, address) |
| 73 | teleport_state.write() |
| 74 | |
| 75 | # Spawn a thread that will wait for the Teleport proxy to start, and |
| 76 | # record it's PID, or warn that it failed to start. |
| 77 | address = f"http://localhost:{port}" |
| 78 | thread = threading.Thread(target=wait, args=[child, teleport_state, address]) |
| 79 | thread.start() |
| 80 |