Skip to content

Commit 4e01d82

Browse files
committed
address comments
1 parent dd00cbb commit 4e01d82

5 files changed

Lines changed: 205 additions & 210 deletions

File tree

centml/cli/shell.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,47 @@
99
from centml.sdk import auth
1010
from centml.sdk.api import get_centml_client
1111
from centml.sdk.config import settings
12-
from centml.sdk.shell import ShellError, build_ws_url, exec_session, interactive_session, resolve_pod
12+
from centml.sdk.shell import PodNotFoundError, ShellError, build_ws_url, exec_session, get_running_pods, interactive_session
1313

1414

15-
def _connect_args(deployment_id, pod, shell_type):
15+
def _resolve_pod(running_pods: list[str], pod_name: str) -> str:
16+
"""Validate that *pod_name* exists in *running_pods*."""
17+
if pod_name not in running_pods:
18+
pods_list = ", ".join(running_pods)
19+
raise PodNotFoundError(f"Pod '{pod_name}' not found. Available running pods: {pods_list}")
20+
return pod_name
21+
22+
23+
24+
def _select_pod(running_pods, deployment_id):
25+
click.echo(f"Multiple running pods found for deployment {deployment_id}:")
26+
for i, name in enumerate(running_pods, 1):
27+
click.echo(f" [{i}] {name}")
28+
29+
choice = click.prompt(
30+
"Select a pod",
31+
type=click.IntRange(1, len(running_pods)),
32+
prompt_suffix=f" [1-{len(running_pods)}]: ",
33+
)
34+
return running_pods[choice - 1]
35+
36+
37+
def _connect_args(deployment_id, pod, shell_type, first_pod=False):
1638
"""Resolve pod, build WebSocket URL, and obtain auth token."""
1739
with get_centml_client() as cclient:
18-
try:
19-
pod_name, warning = resolve_pod(cclient, deployment_id, pod)
20-
except ShellError as exc:
21-
raise click.ClickException(str(exc)) from exc
22-
if warning is not None:
23-
click.echo(f"{warning} Use --pod to specify a different pod.", err=True)
40+
running_pods = get_running_pods(cclient, deployment_id)
41+
if not running_pods:
42+
raise click.ClickException(f"No running pods found for deployment {deployment_id}")
43+
44+
if pod is not None:
45+
try:
46+
pod_name = _resolve_pod(running_pods, pod)
47+
except ShellError as exc:
48+
raise click.ClickException(str(exc)) from exc
49+
elif len(running_pods) == 1 or first_pod:
50+
pod_name = running_pods[0]
51+
else:
52+
pod_name = _select_pod(running_pods, deployment_id)
2453

2554
ws_url = build_ws_url(settings.CENTML_PLATFORM_API_URL, deployment_id, pod_name, shell_type)
2655
token = auth.get_centml_token()
@@ -29,14 +58,15 @@ def _connect_args(deployment_id, pod, shell_type):
2958

3059
@click.command(help="Open an interactive shell to a deployment pod")
3160
@click.argument("deployment_id", type=int)
32-
@click.option("--pod", default=None, help="Specify a pod name (Default: auto-selects first running pod)")
61+
@click.option("--pod", default=None, help="Specify a pod name")
3362
@click.option("--shell", "shell_type", default=None, type=click.Choice(["bash", "sh", "zsh"]), help="Shell type")
63+
@click.option("--first-pod", is_flag=True, default=False, help="Auto-select the first running pod (skip interactive selection)")
3464
@handle_exception
35-
def shell(deployment_id, pod, shell_type):
65+
def shell(deployment_id, pod, shell_type, first_pod):
3666
if not sys.stdin.isatty():
3767
raise click.ClickException("Interactive shell requires a terminal (TTY)")
3868

39-
ws_url, token = _connect_args(deployment_id, pod, shell_type)
69+
ws_url, token = _connect_args(deployment_id, pod, shell_type, first_pod)
4070
exit_code = asyncio.run(interactive_session(ws_url, token))
4171
sys.exit(exit_code)
4272

@@ -46,8 +76,9 @@ def shell(deployment_id, pod, shell_type):
4676
@click.argument("command", nargs=-1, required=True, type=click.UNPROCESSED)
4777
@click.option("--pod", default=None, help="Specific pod name")
4878
@click.option("--shell", "shell_type", default=None, type=click.Choice(["bash", "sh", "zsh"]), help="Shell type")
79+
@click.option("--first-pod", is_flag=True, default=False, help="Auto-select the first running pod (skip interactive selection)")
4980
@handle_exception
50-
def exec_cmd(deployment_id, command, pod, shell_type):
51-
ws_url, token = _connect_args(deployment_id, pod, shell_type)
81+
def exec_cmd(deployment_id, command, pod, shell_type, first_pod):
82+
ws_url, token = _connect_args(deployment_id, pod, shell_type, first_pod)
5283
exit_code = asyncio.run(exec_session(ws_url, token, " ".join(command)))
5384
sys.exit(exit_code)

centml/sdk/shell/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from centml.sdk.shell.exceptions import NoPodAvailableError, PodNotFoundError, ShellError
2-
from centml.sdk.shell.session import build_ws_url, exec_session, interactive_session, resolve_pod
2+
from centml.sdk.shell.session import build_ws_url, exec_session, get_running_pods, interactive_session
33

44
__all__ = [
55
"ShellError",
66
"NoPodAvailableError",
77
"PodNotFoundError",
88
"build_ws_url",
9-
"resolve_pod",
9+
"get_running_pods",
1010
"interactive_session",
1111
"exec_session",
1212
]

centml/sdk/shell/session.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66
import termios
77
import tty
88
import urllib.parse
9-
from typing import Optional, Tuple
109

1110
import pyte
1211
import websockets
1312

1413
from centml.sdk import PodStatus
15-
from centml.sdk.shell.exceptions import NoPodAvailableError, PodNotFoundError
1614

1715
# exec_session wraps commands between BEGIN/END markers so it can separate
1816
# real command output from shell noise (prompt, echoed input, login banners).
@@ -35,27 +33,14 @@ def build_ws_url(api_url, deployment_id, pod_name, shell_type=None):
3533
return url
3634

3735

38-
def resolve_pod(cclient, deployment_id, pod_name=None) -> Tuple[str, Optional[str]]:
36+
def get_running_pods(cclient, deployment_id) -> list[str]:
3937
status = cclient.get_status_v3(deployment_id)
4038
running_pods = []
4139
for revision in status.revision_pod_details_list or []:
4240
for pod in revision.pod_details_list or []:
4341
if pod.status == PodStatus.RUNNING and pod.name:
4442
running_pods.append(pod.name)
45-
46-
if not running_pods:
47-
raise NoPodAvailableError(f"No running pods found for deployment {deployment_id}")
48-
49-
if pod_name is not None:
50-
if pod_name not in running_pods:
51-
pods_list = ", ".join(running_pods)
52-
raise PodNotFoundError(f"Pod '{pod_name}' not found. Available running pods: {pods_list}")
53-
return pod_name, None
54-
55-
warning = None
56-
if len(running_pods) > 1:
57-
warning = f"Multiple running pods found, connecting to {running_pods[0]}."
58-
return running_pods[0], warning
43+
return running_pods
5944

6045

6146
async def forward_io(ws, term_size, shutdown):

0 commit comments

Comments
 (0)