skypilot-nightly 1.0.0.dev20250521__py3-none-any.whl → 1.0.0.dev20250523__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sky/__init__.py +2 -2
- sky/adaptors/kubernetes.py +46 -16
- sky/backends/cloud_vm_ray_backend.py +16 -4
- sky/check.py +109 -44
- sky/cli.py +261 -90
- sky/client/cli.py +261 -90
- sky/client/sdk.py +122 -3
- sky/clouds/__init__.py +5 -0
- sky/clouds/aws.py +4 -2
- sky/clouds/azure.py +4 -2
- sky/clouds/cloud.py +30 -6
- sky/clouds/cudo.py +2 -1
- sky/clouds/do.py +2 -1
- sky/clouds/fluidstack.py +2 -1
- sky/clouds/gcp.py +160 -23
- sky/clouds/ibm.py +4 -2
- sky/clouds/kubernetes.py +66 -22
- sky/clouds/lambda_cloud.py +2 -1
- sky/clouds/nebius.py +18 -2
- sky/clouds/oci.py +4 -2
- sky/clouds/paperspace.py +2 -1
- sky/clouds/runpod.py +2 -1
- sky/clouds/scp.py +2 -1
- sky/clouds/service_catalog/__init__.py +3 -0
- sky/clouds/service_catalog/common.py +9 -2
- sky/clouds/service_catalog/constants.py +2 -1
- sky/clouds/service_catalog/ssh_catalog.py +167 -0
- sky/clouds/ssh.py +203 -0
- sky/clouds/vast.py +2 -1
- sky/clouds/vsphere.py +2 -1
- sky/core.py +59 -17
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/{hvWzC5E6Q4CcKzXcWbgig → ECKwDNS9v9y3_IKFZ2lpp}/_buildManifest.js +1 -1
- sky/dashboard/out/_next/static/chunks/pages/infra-abf08c4384190a39.js +1 -0
- sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
- sky/dashboard/out/clusters/[cluster].html +1 -1
- sky/dashboard/out/clusters.html +1 -1
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra.html +1 -1
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/data/storage.py +1 -0
- sky/execution.py +56 -7
- sky/jobs/server/core.py +4 -2
- sky/optimizer.py +29 -15
- sky/provision/__init__.py +1 -0
- sky/provision/aws/instance.py +17 -1
- sky/provision/gcp/constants.py +147 -4
- sky/provision/gcp/instance_utils.py +10 -0
- sky/provision/gcp/volume_utils.py +247 -0
- sky/provision/kubernetes/instance.py +16 -5
- sky/provision/kubernetes/utils.py +37 -19
- sky/provision/nebius/instance.py +3 -1
- sky/provision/nebius/utils.py +14 -2
- sky/provision/ssh/__init__.py +18 -0
- sky/resources.py +177 -4
- sky/serve/server/core.py +2 -4
- sky/server/common.py +46 -9
- sky/server/constants.py +2 -0
- sky/server/html/token_page.html +154 -0
- sky/server/requests/executor.py +3 -6
- sky/server/requests/payloads.py +7 -0
- sky/server/server.py +80 -8
- sky/setup_files/dependencies.py +1 -0
- sky/skypilot_config.py +117 -31
- sky/task.py +24 -1
- sky/templates/gcp-ray.yml.j2 +44 -1
- sky/templates/nebius-ray.yml.j2 +12 -2
- sky/utils/admin_policy_utils.py +26 -22
- sky/utils/context.py +36 -6
- sky/utils/context_utils.py +15 -0
- sky/utils/infra_utils.py +21 -1
- sky/utils/kubernetes/cleanup-tunnel.sh +62 -0
- sky/utils/kubernetes/create_cluster.sh +1 -0
- sky/utils/kubernetes/deploy_remote_cluster.py +1437 -0
- sky/utils/kubernetes/kubernetes_deploy_utils.py +117 -10
- sky/utils/kubernetes/ssh-tunnel.sh +387 -0
- sky/utils/log_utils.py +214 -1
- sky/utils/resources_utils.py +14 -0
- sky/utils/schemas.py +67 -0
- sky/utils/ux_utils.py +2 -1
- {skypilot_nightly-1.0.0.dev20250521.dist-info → skypilot_nightly-1.0.0.dev20250523.dist-info}/METADATA +6 -1
- {skypilot_nightly-1.0.0.dev20250521.dist-info → skypilot_nightly-1.0.0.dev20250523.dist-info}/RECORD +88 -81
- sky/dashboard/out/_next/static/chunks/pages/infra-9180cd91cee64b96.js +0 -1
- sky/utils/kubernetes/deploy_remote_cluster.sh +0 -308
- /sky/dashboard/out/_next/static/{hvWzC5E6Q4CcKzXcWbgig → ECKwDNS9v9y3_IKFZ2lpp}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250521.dist-info → skypilot_nightly-1.0.0.dev20250523.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250521.dist-info → skypilot_nightly-1.0.0.dev20250523.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250521.dist-info → skypilot_nightly-1.0.0.dev20250523.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250521.dist-info → skypilot_nightly-1.0.0.dev20250523.dist-info}/top_level.txt +0 -0
sky/__init__.py
CHANGED
@@ -5,7 +5,7 @@ from typing import Optional
|
|
5
5
|
import urllib.request
|
6
6
|
|
7
7
|
# Replaced with the current commit when building the wheels.
|
8
|
-
_SKYPILOT_COMMIT_SHA = '
|
8
|
+
_SKYPILOT_COMMIT_SHA = '8848c5c597089bae431284150803f4e557383b9e'
|
9
9
|
|
10
10
|
|
11
11
|
def _get_git_commit():
|
@@ -35,7 +35,7 @@ def _get_git_commit():
|
|
35
35
|
|
36
36
|
|
37
37
|
__commit__ = _get_git_commit()
|
38
|
-
__version__ = '1.0.0.
|
38
|
+
__version__ = '1.0.0.dev20250523'
|
39
39
|
__root_dir__ = os.path.dirname(os.path.abspath(__file__))
|
40
40
|
|
41
41
|
|
sky/adaptors/kubernetes.py
CHANGED
@@ -3,8 +3,8 @@ import logging
|
|
3
3
|
import os
|
4
4
|
from typing import Any, Callable, Optional, Set
|
5
5
|
|
6
|
+
from sky import sky_logging
|
6
7
|
from sky.adaptors import common
|
7
|
-
from sky.sky_logging import set_logging_level
|
8
8
|
from sky.utils import annotations
|
9
9
|
from sky.utils import common_utils
|
10
10
|
from sky.utils import ux_utils
|
@@ -26,6 +26,8 @@ DEFAULT_IN_CLUSTER_REGION = 'in-cluster'
|
|
26
26
|
# set to DEFAULT_IN_CLUSTER_REGION.
|
27
27
|
IN_CLUSTER_CONTEXT_NAME_ENV_VAR = 'SKYPILOT_IN_CLUSTER_CONTEXT_NAME'
|
28
28
|
|
29
|
+
logger = sky_logging.init_logger(__name__)
|
30
|
+
|
29
31
|
|
30
32
|
def _decorate_methods(obj: Any, decorator: Callable, decoration_type: str):
|
31
33
|
for attr_name in dir(obj):
|
@@ -43,7 +45,7 @@ def _decorate_methods(obj: Any, decorator: Callable, decoration_type: str):
|
|
43
45
|
return obj
|
44
46
|
|
45
47
|
|
46
|
-
def _api_logging_decorator(
|
48
|
+
def _api_logging_decorator(logger_src: str, level: int):
|
47
49
|
"""Decorator to set logging level for API calls.
|
48
50
|
|
49
51
|
This is used to suppress the verbose logging from urllib3 when calls to the
|
@@ -54,7 +56,9 @@ def _api_logging_decorator(logger: str, level: int):
|
|
54
56
|
|
55
57
|
def wrapped(*args, **kwargs):
|
56
58
|
obj = api(*args, **kwargs)
|
57
|
-
_decorate_methods(obj,
|
59
|
+
_decorate_methods(obj,
|
60
|
+
sky_logging.set_logging_level(logger_src, level),
|
61
|
+
'api_log')
|
58
62
|
return obj
|
59
63
|
|
60
64
|
return wrapped
|
@@ -71,27 +75,53 @@ def _load_config(context: Optional[str] = None):
|
|
71
75
|
except kubernetes.config.config_exception.ConfigException as e:
|
72
76
|
suffix = common_utils.format_exception(e, use_bracket=True)
|
73
77
|
context_name = '(current-context)' if context is None else context
|
78
|
+
is_ssh_node_pool = False
|
79
|
+
if context_name.startswith('ssh-'):
|
80
|
+
context_name = context_name.lstrip('ssh-')
|
81
|
+
is_ssh_node_pool = True
|
74
82
|
# Check if exception was due to no current-context
|
75
83
|
if 'Expected key current-context' in str(e):
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
84
|
+
if is_ssh_node_pool:
|
85
|
+
context_name = context_name.lstrip('ssh-')
|
86
|
+
err_str = ('Failed to load SSH Node Pool configuration for '
|
87
|
+
f'{context_name!r}.\n'
|
88
|
+
' Run `sky ssh up --infra {context_name}` to '
|
89
|
+
'set up or repair the cluster.')
|
90
|
+
else:
|
91
|
+
err_str = (
|
92
|
+
'Failed to load Kubernetes configuration for '
|
93
|
+
f'{context_name!r}. '
|
94
|
+
'Kubeconfig does not contain any valid context(s).'
|
95
|
+
f'\n{suffix}\n'
|
96
|
+
' If you were running a local Kubernetes '
|
97
|
+
'cluster, run `sky local up` to start the cluster.')
|
82
98
|
else:
|
83
99
|
kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
100
|
+
if is_ssh_node_pool:
|
101
|
+
err_str = (
|
102
|
+
f'Failed to load SSH Node Pool configuration for '
|
103
|
+
f'{context_name!r}. Run `sky ssh up --infra '
|
104
|
+
f'{context_name}` to set up or repair the cluster.')
|
105
|
+
else:
|
106
|
+
err_str = (
|
107
|
+
'Failed to load Kubernetes configuration for '
|
108
|
+
f'{context_name!r}. Please check if your kubeconfig '
|
109
|
+
f'file exists at {kubeconfig_path} and is valid.'
|
110
|
+
f'\n{suffix}\n')
|
111
|
+
if is_ssh_node_pool:
|
112
|
+
err_str += (f'\nTo disable SSH Node Pool {context_name!r}: '
|
113
|
+
'run `sky check`.')
|
114
|
+
else:
|
90
115
|
err_str += (
|
91
116
|
'\nHint: Kubernetes attempted to query the current-context '
|
92
117
|
'set in kubeconfig. Check if the current-context is valid.')
|
93
118
|
with ux_utils.print_exception_no_traceback():
|
94
|
-
|
119
|
+
if is_ssh_node_pool:
|
120
|
+
# For SSH Node Pool, we don't want to surface k8s errors
|
121
|
+
# (e.g., missing context) unless debug flag is set.
|
122
|
+
logging.debug(f'Kubernetes error: {suffix}')
|
123
|
+
else:
|
124
|
+
raise ValueError(err_str) from None
|
95
125
|
|
96
126
|
if context == in_cluster_context_name() or context is None:
|
97
127
|
try:
|
@@ -192,6 +192,7 @@ def _get_cluster_config_template(cloud):
|
|
192
192
|
clouds.DO: 'do-ray.yml.j2',
|
193
193
|
clouds.RunPod: 'runpod-ray.yml.j2',
|
194
194
|
clouds.Kubernetes: 'kubernetes-ray.yml.j2',
|
195
|
+
clouds.SSH: 'kubernetes-ray.yml.j2',
|
195
196
|
clouds.Vsphere: 'vsphere-ray.yml.j2',
|
196
197
|
clouds.Vast: 'vast-ray.yml.j2',
|
197
198
|
clouds.Fluidstack: 'fluidstack-ray.yml.j2',
|
@@ -1547,11 +1548,13 @@ class RetryingVmProvisioner(object):
|
|
1547
1548
|
controller_str = ('' if controller is None else
|
1548
1549
|
f' {controller.value.name}')
|
1549
1550
|
if isinstance(to_provision.cloud, clouds.Kubernetes):
|
1550
|
-
|
1551
|
+
suffix = '.'
|
1552
|
+
if region.name.startswith('ssh-'):
|
1553
|
+
suffix = f' ({region.name.lstrip("ssh-")})'
|
1551
1554
|
logger.info(
|
1552
1555
|
ux_utils.starting_message(
|
1553
1556
|
f'Launching{controller_str} on '
|
1554
|
-
f'{to_provision.cloud}
|
1557
|
+
f'{to_provision.cloud}{suffix}'))
|
1555
1558
|
else:
|
1556
1559
|
logger.info(
|
1557
1560
|
ux_utils.starting_message(
|
@@ -1724,8 +1727,17 @@ class RetryingVmProvisioner(object):
|
|
1724
1727
|
f'{requested_resources}. ')
|
1725
1728
|
elif to_provision.region is not None:
|
1726
1729
|
# For public clouds, provision.region is always set.
|
1727
|
-
|
1728
|
-
|
1730
|
+
if to_provision.cloud.is_same_cloud(clouds.SSH()):
|
1731
|
+
message = ('Failed to acquire resources in SSH Node Pool '
|
1732
|
+
f'({to_provision.region.lstrip("ssh-")}) for '
|
1733
|
+
f'{requested_resources}. The SSH Node Pool may not '
|
1734
|
+
'have enough resources.')
|
1735
|
+
elif to_provision.cloud.is_same_cloud(clouds.Kubernetes()):
|
1736
|
+
message = ('Failed to acquire resources in context '
|
1737
|
+
f'{to_provision.region} for {requested_resources}. ')
|
1738
|
+
else:
|
1739
|
+
message = ('Failed to acquire resources in all zones in '
|
1740
|
+
f'{to_provision.region} for {requested_resources}. ')
|
1729
1741
|
else:
|
1730
1742
|
message = (f'Failed to acquire resources in {to_provision.cloud} '
|
1731
1743
|
f'for {requested_resources}. ')
|
sky/check.py
CHANGED
@@ -4,8 +4,7 @@ import itertools
|
|
4
4
|
import os
|
5
5
|
import traceback
|
6
6
|
from types import ModuleType
|
7
|
-
from typing import
|
8
|
-
Union)
|
7
|
+
from typing import Callable, Dict, Iterable, List, Optional, Set, Tuple, Union
|
9
8
|
|
10
9
|
import click
|
11
10
|
import colorama
|
@@ -44,7 +43,8 @@ def check_capabilities(
|
|
44
43
|
def check_one_cloud_one_capability(
|
45
44
|
payload: Tuple[Tuple[str, Union[sky_clouds.Cloud, ModuleType]],
|
46
45
|
sky_cloud.CloudCapability]
|
47
|
-
) -> Optional[Tuple[sky_cloud.CloudCapability, bool, Optional[
|
46
|
+
) -> Optional[Tuple[sky_cloud.CloudCapability, bool, Optional[Union[
|
47
|
+
str, Dict[str, str]]]]]:
|
48
48
|
cloud_tuple, capability = payload
|
49
49
|
_, cloud = cloud_tuple
|
50
50
|
try:
|
@@ -53,7 +53,9 @@ def check_capabilities(
|
|
53
53
|
return None
|
54
54
|
except Exception: # pylint: disable=broad-except
|
55
55
|
ok, reason = False, traceback.format_exc()
|
56
|
-
|
56
|
+
if not isinstance(reason, dict):
|
57
|
+
reason = reason.strip() if reason else None
|
58
|
+
return (capability, ok, reason)
|
57
59
|
|
58
60
|
def get_cloud_tuple(
|
59
61
|
cloud_name: str) -> Tuple[str, Union[sky_clouds.Cloud, ModuleType]]:
|
@@ -100,13 +102,18 @@ def check_capabilities(
|
|
100
102
|
check_results_dict: Dict[
|
101
103
|
Tuple[str, Union[sky_clouds.Cloud, ModuleType]],
|
102
104
|
List[Tuple[sky_cloud.CloudCapability, bool,
|
103
|
-
Optional[str
|
105
|
+
Optional[Union[str,
|
106
|
+
Dict[str,
|
107
|
+
str]]]]]] = collections.defaultdict(list)
|
108
|
+
cloud2ctx2text: Dict[str, Dict[str, str]] = {}
|
104
109
|
for combination, check_result in zip(combinations, check_results):
|
105
110
|
if check_result is None:
|
106
111
|
continue
|
107
|
-
capability, ok,
|
112
|
+
capability, ok, ctx2text = check_result
|
108
113
|
cloud_tuple, _ = combination
|
109
114
|
cloud_repr = cloud_tuple[0]
|
115
|
+
if isinstance(ctx2text, dict):
|
116
|
+
cloud2ctx2text[cloud_repr] = ctx2text
|
110
117
|
if ok:
|
111
118
|
enabled_clouds.setdefault(cloud_repr, []).append(capability)
|
112
119
|
else:
|
@@ -115,7 +122,8 @@ def check_capabilities(
|
|
115
122
|
|
116
123
|
for cloud_tuple, check_result_list in sorted(check_results_dict.items(),
|
117
124
|
key=lambda item: item[0][0]):
|
118
|
-
_print_checked_cloud(echo, verbose, cloud_tuple, check_result_list
|
125
|
+
_print_checked_cloud(echo, verbose, cloud_tuple, check_result_list,
|
126
|
+
cloud2ctx2text.get(cloud_tuple[0], {}))
|
119
127
|
|
120
128
|
# Determine the set of enabled clouds: (previously enabled clouds + newly
|
121
129
|
# enabled clouds - newly disabled clouds) intersected with
|
@@ -184,7 +192,8 @@ def check_capabilities(
|
|
184
192
|
# Pretty print for UX.
|
185
193
|
if not quiet:
|
186
194
|
enabled_clouds_str = '\n ' + '\n '.join([
|
187
|
-
_format_enabled_cloud(cloud, capabilities
|
195
|
+
_format_enabled_cloud(cloud, capabilities,
|
196
|
+
cloud2ctx2text.get(cloud, None))
|
188
197
|
for cloud, capabilities in sorted(enabled_clouds.items(),
|
189
198
|
key=lambda item: item[0])
|
190
199
|
])
|
@@ -290,7 +299,8 @@ def _print_checked_cloud(
|
|
290
299
|
verbose: bool,
|
291
300
|
cloud_tuple: Tuple[str, Union[sky_clouds.Cloud, ModuleType]],
|
292
301
|
cloud_capabilities: List[Tuple[sky_cloud.CloudCapability, bool,
|
293
|
-
Optional[str]]],
|
302
|
+
Optional[Union[str, Dict[str, str]]]]],
|
303
|
+
ctx2text: Dict[str, str],
|
294
304
|
) -> None:
|
295
305
|
"""Prints whether a cloud is enabled, and the capabilities that are enabled.
|
296
306
|
If any hints (for enabled capabilities) or
|
@@ -317,24 +327,34 @@ def _print_checked_cloud(
|
|
317
327
|
for capability, ok, reason in cloud_capabilities:
|
318
328
|
if ok:
|
319
329
|
enabled_capabilities.append(capability)
|
330
|
+
# `dict` reasons for K8s and SSH will be printed in detail in
|
331
|
+
# _format_enabled_cloud. Skip here.
|
332
|
+
if not isinstance(reason, str):
|
333
|
+
continue
|
334
|
+
if ok:
|
320
335
|
if reason is not None:
|
321
336
|
hints_to_capabilities.setdefault(reason, []).append(capability)
|
322
337
|
elif reason is not None:
|
323
338
|
reasons_to_capabilities.setdefault(reason, []).append(capability)
|
339
|
+
style_str = f'{colorama.Style.DIM}'
|
324
340
|
status_msg: str = 'disabled'
|
325
|
-
styles: Dict[str, Any] = {'dim': True}
|
326
341
|
capability_string: str = ''
|
342
|
+
detail_string: str = ''
|
327
343
|
activated_account: Optional[str] = None
|
328
344
|
if enabled_capabilities:
|
345
|
+
style_str = f'{colorama.Fore.GREEN}{colorama.Style.NORMAL}'
|
329
346
|
status_msg = 'enabled'
|
330
|
-
styles = {'fg': 'green', 'bold': False}
|
331
347
|
capability_string = f'[{", ".join(enabled_capabilities)}]'
|
332
348
|
if verbose and cloud is not cloudflare:
|
333
349
|
activated_account = cloud.get_active_user_identity_str()
|
334
|
-
|
350
|
+
if isinstance(cloud_tuple[1], (sky_clouds.SSH, sky_clouds.Kubernetes)):
|
351
|
+
detail_string = _format_context_details(cloud_tuple[1],
|
352
|
+
show_details=True,
|
353
|
+
ctx2text=ctx2text)
|
335
354
|
echo(
|
336
|
-
click.style(
|
337
|
-
|
355
|
+
click.style(
|
356
|
+
f'{style_str} {cloud_repr}: {status_msg} {capability_string}'
|
357
|
+
f'{colorama.Style.RESET_ALL}{detail_string}'))
|
338
358
|
if activated_account is not None:
|
339
359
|
echo(f' Activated account: {activated_account}')
|
340
360
|
for reason, caps in hints_to_capabilities.items():
|
@@ -343,8 +363,77 @@ def _print_checked_cloud(
|
|
343
363
|
echo(f' Reason [{", ".join(caps)}]: {reason}')
|
344
364
|
|
345
365
|
|
366
|
+
def _green_color(str_to_format: str) -> str:
|
367
|
+
return f'{colorama.Fore.GREEN}{str_to_format}{colorama.Style.RESET_ALL}'
|
368
|
+
|
369
|
+
|
370
|
+
def _format_context_details(cloud: Union[str, sky_clouds.Cloud],
|
371
|
+
show_details: bool,
|
372
|
+
ctx2text: Optional[Dict[str, str]] = None) -> str:
|
373
|
+
if isinstance(cloud, str):
|
374
|
+
cloud_type = registry.CLOUD_REGISTRY.from_str(cloud)
|
375
|
+
assert cloud_type is not None
|
376
|
+
else:
|
377
|
+
cloud_type = cloud
|
378
|
+
if isinstance(cloud_type, sky_clouds.SSH):
|
379
|
+
# Get the cluster names by reading from the node pools file
|
380
|
+
contexts = sky_clouds.SSH.get_ssh_node_pool_contexts()
|
381
|
+
else:
|
382
|
+
assert isinstance(cloud_type, sky_clouds.Kubernetes)
|
383
|
+
contexts = sky_clouds.Kubernetes.existing_allowed_contexts()
|
384
|
+
|
385
|
+
filtered_contexts = []
|
386
|
+
for context in contexts:
|
387
|
+
if not show_details:
|
388
|
+
# Skip
|
389
|
+
if (ctx2text is None or context not in ctx2text or
|
390
|
+
'disabled' in ctx2text[context]):
|
391
|
+
continue
|
392
|
+
filtered_contexts.append(context)
|
393
|
+
|
394
|
+
if not filtered_contexts:
|
395
|
+
return ''
|
396
|
+
|
397
|
+
def _red_color(str_to_format: str) -> str:
|
398
|
+
return (f'{colorama.Fore.LIGHTRED_EX}'
|
399
|
+
f'{str_to_format}'
|
400
|
+
f'{colorama.Style.RESET_ALL}')
|
401
|
+
|
402
|
+
def _dim_color(str_to_format: str) -> str:
|
403
|
+
return (f'{colorama.Style.DIM}'
|
404
|
+
f'{str_to_format}'
|
405
|
+
f'{colorama.Style.RESET_ALL}')
|
406
|
+
|
407
|
+
# Format the context info with consistent styling
|
408
|
+
contexts_formatted = []
|
409
|
+
for i, context in enumerate(filtered_contexts):
|
410
|
+
if isinstance(cloud_type, sky_clouds.SSH):
|
411
|
+
# TODO: This is a hack to remove the 'ssh-' prefix from the
|
412
|
+
# context name. Once we have a separate kubeconfig for SSH,
|
413
|
+
# this will not be required.
|
414
|
+
cleaned_context = context.lstrip('ssh-')
|
415
|
+
else:
|
416
|
+
cleaned_context = context
|
417
|
+
symbol = (ux_utils.INDENT_LAST_SYMBOL if i == len(filtered_contexts) -
|
418
|
+
1 else ux_utils.INDENT_SYMBOL)
|
419
|
+
text_suffix = ''
|
420
|
+
if show_details:
|
421
|
+
if ctx2text is not None:
|
422
|
+
text_suffix = (
|
423
|
+
f': {ctx2text[context]}' if context in ctx2text else
|
424
|
+
(': ' + _red_color('disabled. ') +
|
425
|
+
_dim_color('Reason: Not set up. Use `sky ssh up --infra '
|
426
|
+
f'{context.lstrip("ssh-")}` to set up.')))
|
427
|
+
contexts_formatted.append(
|
428
|
+
f'\n {symbol}{cleaned_context}{text_suffix}')
|
429
|
+
identity_str = ('SSH Node Pools' if isinstance(cloud_type, sky_clouds.SSH)
|
430
|
+
else 'Allowed contexts')
|
431
|
+
return f'\n {identity_str}:{"".join(contexts_formatted)}'
|
432
|
+
|
433
|
+
|
346
434
|
def _format_enabled_cloud(cloud_name: str,
|
347
|
-
capabilities: List[sky_cloud.CloudCapability]
|
435
|
+
capabilities: List[sky_cloud.CloudCapability],
|
436
|
+
ctx2text: Optional[Dict[str, str]] = None) -> str:
|
348
437
|
"""Format the summary of enabled cloud and its enabled capabilities.
|
349
438
|
|
350
439
|
Args:
|
@@ -355,34 +444,10 @@ def _format_enabled_cloud(cloud_name: str,
|
|
355
444
|
A string of the formatted cloud and capabilities.
|
356
445
|
"""
|
357
446
|
cloud_and_capabilities = f'{cloud_name} [{", ".join(capabilities)}]'
|
447
|
+
title = _green_color(cloud_and_capabilities)
|
358
448
|
|
359
|
-
|
360
|
-
return (
|
361
|
-
|
362
|
-
|
363
|
-
if cloud_name == repr(sky_clouds.Kubernetes()):
|
364
|
-
# Get enabled contexts for Kubernetes
|
365
|
-
existing_contexts = sky_clouds.Kubernetes.existing_allowed_contexts()
|
366
|
-
if not existing_contexts:
|
367
|
-
return _green_color(cloud_and_capabilities)
|
368
|
-
|
369
|
-
# Check if allowed_contexts is explicitly set in config
|
370
|
-
allowed_contexts = skypilot_config.get_nested(
|
371
|
-
('kubernetes', 'allowed_contexts'), None)
|
372
|
-
|
373
|
-
# Format the context info with consistent styling
|
374
|
-
if allowed_contexts is not None:
|
375
|
-
contexts_formatted = []
|
376
|
-
for i, context in enumerate(existing_contexts):
|
377
|
-
symbol = (ux_utils.INDENT_LAST_SYMBOL
|
378
|
-
if i == len(existing_contexts) -
|
379
|
-
1 else ux_utils.INDENT_SYMBOL)
|
380
|
-
contexts_formatted.append(f'\n {symbol}{context}')
|
381
|
-
context_info = f' Allowed contexts:{"".join(contexts_formatted)}'
|
382
|
-
else:
|
383
|
-
context_info = f' Active context: {existing_contexts[0]}'
|
449
|
+
if cloud_name in [repr(sky_clouds.Kubernetes()), repr(sky_clouds.SSH())]:
|
450
|
+
return (f'{title}' + _format_context_details(
|
451
|
+
cloud_name, show_details=False, ctx2text=ctx2text))
|
384
452
|
|
385
|
-
|
386
|
-
f' {colorama.Style.DIM}{context_info}'
|
387
|
-
f'{colorama.Style.RESET_ALL}')
|
388
|
-
return _green_color(cloud_and_capabilities)
|
453
|
+
return title
|