Skip to content

Commit 18a2aa5

Browse files
Merge pull request #279 from carsonmh/view-rayclusters-cli
View rayclusters CLI changes
2 parents 68dff40 + 8b52a55 commit 18a2aa5

File tree

9 files changed

+57
-40
lines changed

9 files changed

+57
-40
lines changed

Diff for: src/codeflare_sdk/cli/cli_utils.py

+16
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,19 @@ def load_auth():
5757
click.echo("No authentication found, trying default kubeconfig")
5858
except client.ApiException:
5959
click.echo("Invalid authentication, trying default kubeconfig")
60+
61+
62+
class PluralAlias(click.Group):
63+
def get_command(self, ctx, cmd_name):
64+
rv = click.Group.get_command(self, ctx, cmd_name)
65+
if rv is not None:
66+
return rv
67+
for x in self.list_commands(ctx):
68+
if x + "s" == cmd_name:
69+
return click.Group.get_command(self, ctx, x)
70+
return None
71+
72+
def resolve_command(self, ctx, args):
73+
# always return the full command name
74+
_, cmd, args = super().resolve_command(ctx, args)
75+
return cmd.name, cmd, args

Diff for: src/codeflare_sdk/cli/codeflare_cli.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
import os
33

44
from codeflare_sdk.cli.cli_utils import load_auth
5+
from codeflare_sdk.cluster.cluster import get_current_namespace
56

67
cmd_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), "commands"))
78

89

910
class CodeflareContext:
1011
def __init__(self):
1112
self.codeflare_path = _initialize_codeflare_folder()
13+
self.current_namespace = get_current_namespace()
1214

1315

1416
def _initialize_codeflare_folder():

Diff for: src/codeflare_sdk/cli/commands/define.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ def cli():
1212

1313

1414
@cli.command()
15+
@click.pass_context
1516
@click.option("--name", type=str, required=True)
16-
@click.option("--namespace", "-n", type=str, required=True)
17+
@click.option("--namespace", "-n", type=str)
1718
@click.option("--head_info", cls=PythonLiteralOption, type=list)
1819
@click.option("--machine_types", cls=PythonLiteralOption, type=list)
1920
@click.option("--min_cpus", type=int)
@@ -29,8 +30,10 @@ def cli():
2930
@click.option("--image", type=str)
3031
@click.option("--local_interactive", type=bool)
3132
@click.option("--image_pull_secrets", cls=PythonLiteralOption, type=list)
32-
def raycluster(**kwargs):
33+
def raycluster(ctx, **kwargs):
3334
"""Define a RayCluster with parameter specifications"""
3435
filtered_kwargs = {k: v for k, v in kwargs.items() if v is not None}
36+
if "namespace" not in filtered_kwargs.keys():
37+
filtered_kwargs["namespace"] = ctx.obj.current_namespace
3538
clusterConfig = ClusterConfiguration(**filtered_kwargs)
3639
Cluster(clusterConfig) # Creates yaml file

Diff for: src/codeflare_sdk/cli/commands/delete.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ def cli():
1212

1313

1414
@cli.command()
15+
@click.pass_context
1516
@click.argument("name", type=str)
16-
@click.option("--namespace", type=str, required=True)
17-
def raycluster(name, namespace):
17+
@click.option("--namespace", type=str)
18+
def raycluster(ctx, name, namespace):
1819
"""
1920
Delete a specified RayCluster from the Kubernetes cluster
2021
"""
22+
namespace = namespace or ctx.obj.current_namespace
2123
try:
2224
cluster = get_cluster(name, namespace)
2325
except FileNotFoundError:

Diff for: src/codeflare_sdk/cli/commands/details.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ def cli():
1111

1212
@cli.command()
1313
@click.argument("name", type=str)
14-
@click.option("--namespace", type=str, required=True)
14+
@click.option("--namespace", type=str)
1515
@click.pass_context
1616
def raycluster(ctx, name, namespace):
1717
"""Get the details of a specified RayCluster"""
18+
namespace = namespace or ctx.obj.current_namespace
1819
try:
1920
cluster = get_cluster(name, namespace)
2021
except FileNotFoundError:

Diff for: src/codeflare_sdk/cli/commands/list.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
from codeflare_sdk.cluster.cluster import (
55
list_clusters_all_namespaces,
66
list_all_clusters,
7-
get_current_namespace,
87
)
9-
from codeflare_sdk.cli.cli_utils import load_auth
8+
from codeflare_sdk.cli.cli_utils import PluralAlias
109

1110

12-
@click.group()
11+
@click.group(cls=PluralAlias)
1312
def cli():
1413
"""List a specified resource"""
1514
pass
@@ -19,14 +18,12 @@ def cli():
1918
@click.option("--namespace", type=str)
2019
@click.option("--all", is_flag=True)
2120
@click.pass_context
22-
def rayclusters(ctx, namespace, all):
21+
def raycluster(ctx, namespace, all):
2322
"""List all rayclusters in a specified namespace"""
2423
if all and namespace:
2524
click.echo("--all and --namespace are mutually exclusive")
2625
return
27-
if not all and not namespace:
28-
click.echo("You must specify either --namespace or --all")
29-
return
26+
namespace = namespace or ctx.obj.current_namespace
3027
if not all:
3128
list_all_clusters(namespace)
3229
return

Diff for: src/codeflare_sdk/cli/commands/status.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ def cli():
1111

1212
@cli.command()
1313
@click.argument("name", type=str)
14-
@click.option("--namespace", type=str, required=True)
14+
@click.option("--namespace", type=str)
1515
@click.pass_context
1616
def raycluster(ctx, name, namespace):
1717
"""Get the status of a specified RayCluster"""
18+
namespace = namespace or ctx.obj.current_namespace
1819
try:
1920
cluster = get_cluster(name, namespace)
2021
except FileNotFoundError:

Diff for: src/codeflare_sdk/cluster/cluster.py

+15-25
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ def list_all_clusters(namespace: str, print_to_console: bool = True):
412412
"""
413413
Returns (and prints by default) a list of all clusters in a given namespace.
414414
"""
415-
clusters = _get_ray_clusters_in_namespace(namespace)
415+
clusters = _get_all_ray_clusters(namespace)
416416
if print_to_console:
417417
pretty_print.print_clusters(clusters)
418418
return clusters
@@ -539,17 +539,24 @@ def _ray_cluster_status(name, namespace="default") -> Optional[RayCluster]:
539539
return None
540540

541541

542-
def _get_ray_clusters_in_namespace(namespace="default") -> List[RayCluster]:
542+
def _get_all_ray_clusters(namespace: str = None) -> List[RayCluster]:
543543
list_of_clusters = []
544544
try:
545545
config_check()
546546
api_instance = client.CustomObjectsApi(api_config_handler())
547-
rcs = api_instance.list_namespaced_custom_object(
548-
group="ray.io",
549-
version="v1alpha1",
550-
namespace=namespace,
551-
plural="rayclusters",
552-
)
547+
if namespace:
548+
rcs = api_instance.list_namespaced_custom_object(
549+
group="ray.io",
550+
version="v1alpha1",
551+
namespace=namespace,
552+
plural="rayclusters",
553+
)
554+
else:
555+
rcs = api_instance.list_cluster_custom_object(
556+
group="ray.io",
557+
version="v1alpha1",
558+
plural="rayclusters",
559+
)
553560
except Exception as e: # pragma: no cover
554561
return _kube_api_error_handling(e)
555562

@@ -558,23 +565,6 @@ def _get_ray_clusters_in_namespace(namespace="default") -> List[RayCluster]:
558565
return list_of_clusters
559566

560567

561-
def _get_all_ray_clusters() -> List[RayCluster]:
562-
list_of_clusters = []
563-
try:
564-
config_check()
565-
api_instance = client.CustomObjectsApi(api_config_handler())
566-
rcs = api_instance.list_cluster_custom_object(
567-
group="ray.io",
568-
version="v1alpha1",
569-
plural="rayclusters",
570-
)
571-
except Exception as e:
572-
return _kube_api_error_handling(e)
573-
for rc in rcs["items"]:
574-
list_of_clusters.append(_map_to_ray_cluster(rc))
575-
return list_of_clusters
576-
577-
578568
def _get_app_wrappers(
579569
namespace="default", filter=List[AppWrapperStatus]
580570
) -> List[AppWrapper]:

Diff for: tests/unit_test.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ def test_login_tls_cli(mocker):
160160
tls_result = runner.invoke(cli, k8s_tls_login_command)
161161
skip_tls_result = runner.invoke(cli, k8s_skip_tls_login_command)
162162
assert (
163-
tls_result.output == skip_tls_result.output == "Logged into 'testserver:6443'\n"
163+
"Logged into 'testserver:6443'\n" in tls_result.output
164+
and "Logged into 'testserver:6443'\n" in skip_tls_result.output
164165
)
165166

166167

@@ -169,7 +170,7 @@ def test_logout_cli(mocker):
169170
mocker.patch.object(client, "ApiClient")
170171
k8s_logout_command = "logout"
171172
logout_result = runner.invoke(cli, k8s_logout_command)
172-
assert logout_result.output == "Successfully logged out of 'testserver:6443'\n"
173+
assert "Successfully logged out of 'testserver:6443'\n" in logout_result.output
173174
assert not os.path.exists(os.path.expanduser("~/.codeflare/auth"))
174175

175176

@@ -198,6 +199,10 @@ def test_cluster_deletion_cli(mocker):
198199
"kubernetes.client.CustomObjectsApi.list_namespaced_custom_object",
199200
side_effect=get_ray_obj,
200201
)
202+
mocker.patch(
203+
"codeflare_sdk.cluster.cluster.get_current_namespace",
204+
return_value="ns",
205+
)
201206
runner = CliRunner()
202207
delete_cluster_command = """
203208
delete raycluster

0 commit comments

Comments
 (0)