Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"boto3>=1.35.3,<2.0",
"botocore>=1.35.6 ",
"kubernetes==33.1.0",
"kr8s>=0.20.0",
"pyyaml==6.0.2",
"ratelimit==2.2.1",
"tabulate==0.9.0",
Expand Down
31 changes: 31 additions & 0 deletions src/sagemaker/hyperpod/cli/commands/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
_hyperpod_telemetry_emitter,
)
from sagemaker.hyperpod.common.telemetry.constants import Feature
from sagemaker.hyperpod.cli.constants.space_constants import DEFAULT_SPACE_PORT
from sagemaker.hyperpod.common.cli_decorators import handle_cli_exceptions


@click.command("hyp-space")
Expand All @@ -18,6 +20,7 @@
schema_pkg="hyperpod_space_template",
registry=SCHEMA_REGISTRY,
)
@handle_cli_exceptions()
def space_create(version, debug, config):
"""Create a space resource."""
space_config = SpaceConfig(**config)
Expand All @@ -29,6 +32,7 @@ def space_create(version, debug, config):
@click.command("hyp-space")
@click.option("--namespace", "-n", required=False, default="default", help="Kubernetes namespace")
@click.option("--output", "-o", type=click.Choice(["table", "json"]), default="table")
@handle_cli_exceptions()
def space_list(namespace, output):
"""List space resources."""
spaces = HPSpace.list(namespace=namespace)
Expand Down Expand Up @@ -70,6 +74,7 @@ def space_list(namespace, output):
@click.option("--name", required=True, help="Name of the space")
@click.option("--namespace", "-n", required=False, default="default", help="Kubernetes namespace")
@click.option("--output", "-o", type=click.Choice(["yaml", "json"]), default="yaml")
@handle_cli_exceptions()
def space_describe(name, namespace, output):
"""Describe a space resource."""
current_space = HPSpace.get(name=name, namespace=namespace)
Expand All @@ -86,6 +91,7 @@ def space_describe(name, namespace, output):
@click.command("hyp-space")
@click.option("--name", required=True, help="Name of the space")
@click.option("--namespace", "-n", required=False, default="default", help="Kubernetes namespace")
@handle_cli_exceptions()
def space_delete(name, namespace):
"""Delete a space resource."""
current_space = HPSpace.get(name=name, namespace=namespace)
Expand All @@ -99,6 +105,7 @@ def space_delete(name, namespace):
registry=SCHEMA_REGISTRY,
is_update=True,
)
@handle_cli_exceptions()
def space_update(version, config):
"""Update a space resource."""
current_space = HPSpace.get(name=config['name'], namespace=config['namespace'])
Expand All @@ -112,6 +119,7 @@ def space_update(version, config):
@click.command("hyp-space")
@click.option("--name", required=True, help="Name of the space")
@click.option("--namespace", "-n", required=False, default="default", help="Kubernetes namespace")
@handle_cli_exceptions()
def space_start(name, namespace):
"""Start a space resource."""
current_space = HPSpace.get(name=name, namespace=namespace)
Expand All @@ -122,6 +130,7 @@ def space_start(name, namespace):
@click.command("hyp-space")
@click.option("--name", required=True, help="Name of the space")
@click.option("--namespace", "-n", required=False, default="default", help="Kubernetes namespace")
@handle_cli_exceptions()
def space_stop(name, namespace):
"""Stop a space resource."""
current_space = HPSpace.get(name=name, namespace=namespace)
Expand All @@ -134,8 +143,30 @@ def space_stop(name, namespace):
@click.option("--namespace", "-n", required=False, default="default", help="Kubernetes namespace")
@click.option("--pod-name", required=False, help="Name of the pod to get logs from")
@click.option("--container", required=False, help="Name of the container to get logs from")
@handle_cli_exceptions()
def space_get_logs(name, namespace, pod_name, container):
"""Get logs for a space resource."""
current_space = HPSpace.get(name=name, namespace=namespace)
logs = current_space.get_logs(pod_name=pod_name, container=container)
click.echo(logs)


@click.command("hyp-space")
@click.option("--name", required=True, help="Name of the space")
@click.option("--namespace", "-n", required=False, default="default", help="Kubernetes namespace")
@click.option("--local-port", required=False, default=DEFAULT_SPACE_PORT, help="Localhost port that is mapped to the space")
def space_portforward(name, namespace, local_port):
"""Port forward to localhost for a space resource."""
# Validate input port
try:
local_port = int(local_port)
except ValueError:
raise ValueError("Port values must be valid integers")

if not (1 <= local_port <= 65535):
raise ValueError(f"Port must be between 1 and 65535, got {local_port}")

current_space = HPSpace.get(name=name, namespace=namespace)
click.echo(f"Forwarding from local port {local_port} to space `{name}` in namespace `{namespace}`.")
click.echo(f"Please access the service via `http://localhost:{local_port}`. Press Ctrl+C to stop.")
current_space.portforward_space(local_port)
2 changes: 2 additions & 0 deletions src/sagemaker/hyperpod/cli/commands/space_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
_hyperpod_telemetry_emitter,
)
from sagemaker.hyperpod.common.telemetry.constants import Feature
from sagemaker.hyperpod.common.cli_decorators import handle_cli_exceptions


@click.command("hyp-space-access")
Expand All @@ -14,6 +15,7 @@
default="vscode-remote",
help="Remote access type supported values: [vscode-remote, web-ui] [default: vscode-remote]"
)
@handle_cli_exceptions()
def space_access_create(name, namespace, connection_type):
"""Create a space access resource."""
space = HPSpace.get(name=name, namespace=namespace)
Expand Down
6 changes: 6 additions & 0 deletions src/sagemaker/hyperpod/cli/commands/space_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import yaml
from tabulate import tabulate
from sagemaker.hyperpod.space.hyperpod_space_template import HPSpaceTemplate
from sagemaker.hyperpod.common.cli_decorators import handle_cli_exceptions


@click.command("hyp-space-template")
@click.option("--file", "-f", required=True, help="YAML file containing the configuration")
@handle_cli_exceptions()
def space_template_create(file):
"""Create a space-template resource."""
template = HPSpaceTemplate(file_path=file)
Expand All @@ -17,6 +19,7 @@ def space_template_create(file):
@click.command("hyp-space-template")
@click.option("--namespace", "-n", required=False, default=None, help="Kubernetes namespace")
@click.option("--output", "-o", type=click.Choice(["table", "json"]), default="table")
@handle_cli_exceptions()
def space_template_list(namespace, output):
"""List space-template resources."""
templates = HPSpaceTemplate.list(namespace)
Expand All @@ -43,6 +46,7 @@ def space_template_list(namespace, output):
@click.option("--name", required=True, help="Name of the space template")
@click.option("--namespace", "-n", required=False, default=None, help="Kubernetes namespace")
@click.option("--output", "-o", type=click.Choice(["yaml", "json"]), default="yaml")
@handle_cli_exceptions()
def space_template_describe(name, namespace, output):
"""Describe a space-template resource."""
template = HPSpaceTemplate.get(name, namespace)
Expand All @@ -56,6 +60,7 @@ def space_template_describe(name, namespace, output):
@click.command("hyp-space-template")
@click.option("--name", required=True, help="Name of the space template")
@click.option("--namespace", "-n", required=False, default=None, help="Kubernetes namespace")
@handle_cli_exceptions()
def space_template_delete(name, namespace):
"""Delete a space-template resource."""
template = HPSpaceTemplate.get(name, namespace)
Expand All @@ -67,6 +72,7 @@ def space_template_delete(name, namespace):
@click.option("--name", required=True, help="Name of the space template")
@click.option("--namespace", "-n", required=False, default=None, help="Kubernetes namespace")
@click.option("--file", "-f", required=True, help="YAML file containing the updated template")
@handle_cli_exceptions()
def space_template_update(name, namespace, file):
"""Update a space-template resource."""
template = HPSpaceTemplate.get(name, namespace)
Expand Down
2 changes: 1 addition & 1 deletion src/sagemaker/hyperpod/cli/constants/space_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
SPACE_GROUP = "workspace.jupyter.org"
SPACE_VERSION = "v1alpha1"
SPACE_PLURAL = "workspaces"
DEFAULT_SPACE_PORT = "8888"
# Immutable fields that cannot be updated after space creation
IMMUTABLE_FIELDS = {
"storage", # storage is immutable per Go struct validation
}
ENABLE_MIG_PROFILE_VALIDATION = False
8 changes: 6 additions & 2 deletions src/sagemaker/hyperpod/cli/hyp_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
space_start,
space_stop,
space_get_logs,
space_portforward,
)
from sagemaker.hyperpod.cli.commands.space_template import (
space_template_create,
Expand Down Expand Up @@ -164,7 +165,10 @@ def stop():
pass



@cli.group(cls=CLICommand)
def portforward():
"""Port forward for space resources."""
pass


@cli.group(cls=CLICommand)
Expand Down Expand Up @@ -252,7 +256,7 @@ def exec():
get_logs.add_command(custom_get_logs)
get_logs.add_command(space_get_logs)


portforward.add_command(space_portforward)

get_operator_logs.add_command(pytorch_get_operator_logs)
get_operator_logs.add_command(js_get_operator_logs)
Expand Down
Loading