skypilot-nightly 1.0.0.dev20251001__py3-none-any.whl → 1.0.0.dev20251003__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.
Potentially problematic release.
This version of skypilot-nightly might be problematic. Click here for more details.
- sky/__init__.py +2 -2
- sky/authentication.py +19 -109
- sky/client/cli/command.py +2 -3
- sky/client/cli/table_utils.py +222 -1
- sky/clouds/cudo.py +1 -1
- sky/clouds/kubernetes.py +7 -19
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/{m3YT2i5s6v4SsIdYc8WZa → Haazh5IQz6F8Wyiqxcaj8}/_buildManifest.js +1 -1
- sky/dashboard/out/_next/static/chunks/3015-8d748834fcc60b46.js +1 -0
- sky/dashboard/out/_next/static/chunks/8969-66237729cdf9749e.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/{[job]-ad77b12fc736dca3.js → [job]-72794fc3fcdd517a.js} +1 -1
- sky/dashboard/out/_next/static/chunks/{webpack-4f0c389a4ce5fd9c.js → webpack-3286453d56f3c0a0.js} +1 -1
- 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/config.html +1 -1
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra/[context].html +1 -1
- sky/dashboard/out/infra.html +1 -1
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs/pools/[pool].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -1
- sky/dashboard/out/volumes.html +1 -1
- sky/dashboard/out/workspace/new.html +1 -1
- sky/dashboard/out/workspaces/[name].html +1 -1
- sky/dashboard/out/workspaces.html +1 -1
- sky/data/storage_utils.py +9 -0
- sky/global_user_state.py +16 -0
- sky/jobs/server/core.py +60 -53
- sky/jobs/state.py +21 -1
- sky/jobs/utils.py +29 -11
- sky/provision/kubernetes/config.py +0 -42
- sky/provision/kubernetes/instance.py +1 -33
- sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml +1 -2
- sky/provision/kubernetes/network_utils.py +0 -21
- sky/provision/kubernetes/utils.py +68 -322
- sky/schemas/api/responses.py +21 -0
- sky/server/requests/serializers/decoders.py +8 -0
- sky/server/requests/serializers/encoders.py +6 -0
- sky/templates/kubernetes-ray.yml.j2 +4 -13
- sky/utils/env_options.py +4 -0
- sky/utils/kubernetes_enums.py +2 -15
- sky/utils/schemas.py +17 -6
- sky/volumes/client/sdk.py +3 -2
- sky/volumes/server/core.py +3 -2
- {skypilot_nightly-1.0.0.dev20251001.dist-info → skypilot_nightly-1.0.0.dev20251003.dist-info}/METADATA +37 -37
- {skypilot_nightly-1.0.0.dev20251001.dist-info → skypilot_nightly-1.0.0.dev20251003.dist-info}/RECORD +53 -56
- sky/dashboard/out/_next/static/chunks/3015-88c7c8d69b0b6dba.js +0 -1
- sky/dashboard/out/_next/static/chunks/8969-d8bc3a2b9cf839a9.js +0 -1
- sky/templates/kubernetes-ssh-jump.yml.j2 +0 -94
- sky/utils/kubernetes/ssh_jump_lifecycle_manager.py +0 -191
- sky/volumes/utils.py +0 -224
- /sky/dashboard/out/_next/static/{m3YT2i5s6v4SsIdYc8WZa → Haazh5IQz6F8Wyiqxcaj8}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20251001.dist-info → skypilot_nightly-1.0.0.dev20251003.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20251001.dist-info → skypilot_nightly-1.0.0.dev20251003.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20251001.dist-info → skypilot_nightly-1.0.0.dev20251003.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20251001.dist-info → skypilot_nightly-1.0.0.dev20251003.dist-info}/top_level.txt +0 -0
sky/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ import urllib.request
|
|
|
7
7
|
from sky.utils import directory_utils
|
|
8
8
|
|
|
9
9
|
# Replaced with the current commit when building the wheels.
|
|
10
|
-
_SKYPILOT_COMMIT_SHA = '
|
|
10
|
+
_SKYPILOT_COMMIT_SHA = 'd9e8cda1c05e4d5e7135d47a4720d236c932e8d7'
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _get_git_commit():
|
|
@@ -37,7 +37,7 @@ def _get_git_commit():
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
__commit__ = _get_git_commit()
|
|
40
|
-
__version__ = '1.0.0.
|
|
40
|
+
__version__ = '1.0.0.dev20251003'
|
|
41
41
|
__root_dir__ = directory_utils.get_sky_dir()
|
|
42
42
|
|
|
43
43
|
|
sky/authentication.py
CHANGED
|
@@ -35,10 +35,8 @@ from sky import clouds
|
|
|
35
35
|
from sky import exceptions
|
|
36
36
|
from sky import global_user_state
|
|
37
37
|
from sky import sky_logging
|
|
38
|
-
from sky import skypilot_config
|
|
39
38
|
from sky.adaptors import gcp
|
|
40
39
|
from sky.adaptors import ibm
|
|
41
|
-
from sky.adaptors import kubernetes
|
|
42
40
|
from sky.adaptors import runpod
|
|
43
41
|
from sky.adaptors import seeweb as seeweb_adaptor
|
|
44
42
|
from sky.adaptors import vast
|
|
@@ -47,8 +45,6 @@ from sky.provision.kubernetes import utils as kubernetes_utils
|
|
|
47
45
|
from sky.provision.lambda_cloud import lambda_utils
|
|
48
46
|
from sky.provision.primeintellect import utils as primeintellect_utils
|
|
49
47
|
from sky.utils import common_utils
|
|
50
|
-
from sky.utils import config_utils
|
|
51
|
-
from sky.utils import kubernetes_enums
|
|
52
48
|
from sky.utils import subprocess_utils
|
|
53
49
|
from sky.utils import ux_utils
|
|
54
50
|
from sky.utils import yaml_utils
|
|
@@ -431,116 +427,30 @@ def setup_ibm_authentication(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
431
427
|
|
|
432
428
|
def setup_kubernetes_authentication(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
433
429
|
context = kubernetes_utils.get_context_from_config(config['provider'])
|
|
434
|
-
|
|
435
|
-
# Default ssh session is established with kubectl port-forwarding with
|
|
436
|
-
# ClusterIP service.
|
|
437
|
-
nodeport_mode = kubernetes_enums.KubernetesNetworkingMode.NODEPORT
|
|
438
|
-
port_forward_mode = kubernetes_enums.KubernetesNetworkingMode.PORTFORWARD
|
|
439
|
-
network_mode_str = skypilot_config.get_effective_region_config(
|
|
440
|
-
cloud='kubernetes',
|
|
441
|
-
region=context,
|
|
442
|
-
keys=('networking',),
|
|
443
|
-
default_value=port_forward_mode.value)
|
|
444
|
-
try:
|
|
445
|
-
network_mode = kubernetes_enums.KubernetesNetworkingMode.from_str(
|
|
446
|
-
network_mode_str)
|
|
447
|
-
except ValueError as e:
|
|
448
|
-
# Add message saying "Please check: ~/.sky/config.yaml" to the error
|
|
449
|
-
# message.
|
|
450
|
-
with ux_utils.print_exception_no_traceback():
|
|
451
|
-
raise ValueError(str(e) +
|
|
452
|
-
' Please check: ~/.sky/config.yaml.') from None
|
|
453
|
-
_, public_key_path = get_or_generate_keys()
|
|
454
|
-
|
|
455
|
-
# Add the user's public key to the SkyPilot cluster.
|
|
456
|
-
secret_name = clouds.Kubernetes.SKY_SSH_KEY_SECRET_NAME
|
|
457
|
-
secret_field_name = clouds.Kubernetes().ssh_key_secret_field_name
|
|
458
430
|
namespace = kubernetes_utils.get_namespace_from_config(config['provider'])
|
|
459
|
-
k8s = kubernetes.kubernetes
|
|
460
|
-
with open(public_key_path, 'r', encoding='utf-8') as f:
|
|
461
|
-
public_key = f.read()
|
|
462
|
-
if not public_key.endswith('\n'):
|
|
463
|
-
public_key += '\n'
|
|
464
|
-
|
|
465
|
-
# Generate metadata
|
|
466
|
-
secret_metadata = {
|
|
467
|
-
'name': secret_name,
|
|
468
|
-
'labels': {
|
|
469
|
-
'parent': 'skypilot'
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
custom_metadata = skypilot_config.get_effective_region_config(
|
|
473
|
-
cloud='kubernetes',
|
|
474
|
-
region=context,
|
|
475
|
-
keys=('custom_metadata',),
|
|
476
|
-
default_value={})
|
|
477
|
-
config_utils.merge_k8s_configs(secret_metadata, custom_metadata)
|
|
478
|
-
|
|
479
|
-
secret = k8s.client.V1Secret(
|
|
480
|
-
metadata=k8s.client.V1ObjectMeta(**secret_metadata),
|
|
481
|
-
string_data={secret_field_name: public_key})
|
|
482
|
-
try:
|
|
483
|
-
if kubernetes_utils.check_secret_exists(secret_name, namespace,
|
|
484
|
-
context):
|
|
485
|
-
logger.debug(f'Key {secret_name} exists in the cluster, '
|
|
486
|
-
'patching it...')
|
|
487
|
-
kubernetes.core_api(context).patch_namespaced_secret(
|
|
488
|
-
secret_name, namespace, secret)
|
|
489
|
-
else:
|
|
490
|
-
logger.debug(f'Key {secret_name} does not exist in the cluster, '
|
|
491
|
-
'creating it...')
|
|
492
|
-
kubernetes.core_api(context).create_namespaced_secret(
|
|
493
|
-
namespace, secret)
|
|
494
|
-
except kubernetes.api_exception() as e:
|
|
495
|
-
if e.status == 409 and e.reason == 'AlreadyExists':
|
|
496
|
-
logger.debug(f'Key {secret_name} was created concurrently, '
|
|
497
|
-
'patching it...')
|
|
498
|
-
kubernetes.core_api(context).patch_namespaced_secret(
|
|
499
|
-
secret_name, namespace, secret)
|
|
500
|
-
else:
|
|
501
|
-
raise e
|
|
502
|
-
|
|
503
431
|
private_key_path, _ = get_or_generate_keys()
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
kubernetes_utils.check_port_forward_mode_dependencies()
|
|
522
|
-
# TODO(romilb): This can be further optimized. Instead of using the
|
|
523
|
-
# head node as a jump pod for worker nodes, we can also directly
|
|
524
|
-
# set the ssh_target to the worker node. However, that requires
|
|
525
|
-
# changes in the downstream code to return a mapping of node IPs to
|
|
526
|
-
# pod names (to be used as ssh_target) and updating the upstream
|
|
527
|
-
# SSHConfigHelper to use a different ProxyCommand for each pod.
|
|
528
|
-
# This optimization can reduce SSH time from ~0.35s to ~0.25s, tested
|
|
529
|
-
# on GKE.
|
|
530
|
-
ssh_target = config['cluster_name'] + '-head'
|
|
531
|
-
ssh_proxy_cmd = kubernetes_utils.get_ssh_proxy_command(
|
|
532
|
-
ssh_target,
|
|
533
|
-
port_forward_mode,
|
|
534
|
-
private_key_path=private_key_path,
|
|
535
|
-
context=context,
|
|
536
|
-
namespace=namespace)
|
|
537
|
-
else:
|
|
538
|
-
# This should never happen because we check for this in from_str above.
|
|
539
|
-
raise ValueError(f'Unsupported networking mode: {network_mode_str}')
|
|
432
|
+
# Using `kubectl port-forward` creates a direct tunnel to the pod and
|
|
433
|
+
# does not require a ssh jump pod.
|
|
434
|
+
kubernetes_utils.check_port_forward_mode_dependencies()
|
|
435
|
+
# TODO(romilb): This can be further optimized. Instead of using the
|
|
436
|
+
# head node as a jump pod for worker nodes, we can also directly
|
|
437
|
+
# set the ssh_target to the worker node. However, that requires
|
|
438
|
+
# changes in the downstream code to return a mapping of node IPs to
|
|
439
|
+
# pod names (to be used as ssh_target) and updating the upstream
|
|
440
|
+
# SSHConfigHelper to use a different ProxyCommand for each pod.
|
|
441
|
+
# This optimization can reduce SSH time from ~0.35s to ~0.25s, tested
|
|
442
|
+
# on GKE.
|
|
443
|
+
pod_name = config['cluster_name'] + '-head'
|
|
444
|
+
ssh_proxy_cmd = kubernetes_utils.get_ssh_proxy_command(
|
|
445
|
+
pod_name,
|
|
446
|
+
private_key_path=private_key_path,
|
|
447
|
+
context=context,
|
|
448
|
+
namespace=namespace)
|
|
540
449
|
config['auth']['ssh_proxy_command'] = ssh_proxy_cmd
|
|
541
450
|
config['auth']['ssh_private_key'] = private_key_path
|
|
542
451
|
|
|
543
|
-
|
|
452
|
+
# Add the user's public key to the SkyPilot cluster.
|
|
453
|
+
return configure_ssh_info(config)
|
|
544
454
|
|
|
545
455
|
|
|
546
456
|
# ---------------------------------- RunPod ---------------------------------- #
|
sky/client/cli/command.py
CHANGED
|
@@ -90,7 +90,6 @@ from sky.utils import ux_utils
|
|
|
90
90
|
from sky.utils import volume as volume_utils
|
|
91
91
|
from sky.utils import yaml_utils
|
|
92
92
|
from sky.utils.cli_utils import status_utils
|
|
93
|
-
from sky.volumes import utils as volumes_utils
|
|
94
93
|
from sky.volumes.client import sdk as volumes_sdk
|
|
95
94
|
|
|
96
95
|
if typing.TYPE_CHECKING:
|
|
@@ -4240,8 +4239,8 @@ def volumes_ls(verbose: bool):
|
|
|
4240
4239
|
"""List volumes managed by SkyPilot."""
|
|
4241
4240
|
request_id = volumes_sdk.ls()
|
|
4242
4241
|
all_volumes = sdk.stream_and_get(request_id)
|
|
4243
|
-
volume_table =
|
|
4244
|
-
|
|
4242
|
+
volume_table = table_utils.format_volume_table(all_volumes,
|
|
4243
|
+
show_all=verbose)
|
|
4245
4244
|
click.echo(volume_table)
|
|
4246
4245
|
|
|
4247
4246
|
|
sky/client/cli/table_utils.py
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
"""Utilities for formatting tables for CLI output."""
|
|
2
|
-
|
|
2
|
+
import abc
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Dict, List, Optional
|
|
3
5
|
|
|
6
|
+
import prettytable
|
|
7
|
+
|
|
8
|
+
from sky import sky_logging
|
|
4
9
|
from sky.jobs import utils as managed_jobs
|
|
5
10
|
from sky.schemas.api import responses
|
|
6
11
|
from sky.skylet import constants
|
|
7
12
|
from sky.utils import common_utils
|
|
8
13
|
from sky.utils import log_utils
|
|
14
|
+
from sky.utils import volume
|
|
15
|
+
|
|
16
|
+
logger = sky_logging.init_logger(__name__)
|
|
9
17
|
|
|
10
18
|
|
|
11
19
|
def format_job_queue(jobs: List[responses.ClusterJobRecord]):
|
|
@@ -89,3 +97,216 @@ def format_job_table(jobs: List[responses.ManagedJobRecord],
|
|
|
89
97
|
show_all=show_all,
|
|
90
98
|
show_user=show_user,
|
|
91
99
|
max_jobs=max_jobs)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
_BASIC_COLUMNS = [
|
|
103
|
+
'NAME',
|
|
104
|
+
'TYPE',
|
|
105
|
+
'INFRA',
|
|
106
|
+
'SIZE',
|
|
107
|
+
'USER',
|
|
108
|
+
'WORKSPACE',
|
|
109
|
+
'AGE',
|
|
110
|
+
'STATUS',
|
|
111
|
+
'LAST_USE',
|
|
112
|
+
'USED_BY',
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _get_infra_str(cloud: Optional[str], region: Optional[str],
|
|
117
|
+
zone: Optional[str]) -> str:
|
|
118
|
+
"""Get the infrastructure string for the volume."""
|
|
119
|
+
infra = ''
|
|
120
|
+
if cloud:
|
|
121
|
+
infra += cloud
|
|
122
|
+
if region:
|
|
123
|
+
infra += f'/{region}'
|
|
124
|
+
if zone:
|
|
125
|
+
infra += f'/{zone}'
|
|
126
|
+
return infra
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class VolumeTable(abc.ABC):
|
|
130
|
+
"""The volume table."""
|
|
131
|
+
|
|
132
|
+
def __init__(self,
|
|
133
|
+
volumes: List[responses.VolumeRecord],
|
|
134
|
+
show_all: bool = False):
|
|
135
|
+
super().__init__()
|
|
136
|
+
self.table = self._create_table(show_all)
|
|
137
|
+
self._add_rows(volumes, show_all)
|
|
138
|
+
|
|
139
|
+
def _get_row_base_columns(self,
|
|
140
|
+
row: responses.VolumeRecord,
|
|
141
|
+
show_all: bool = False) -> List[str]:
|
|
142
|
+
"""Get the base columns for a row."""
|
|
143
|
+
# Convert last_attached_at timestamp to human readable string
|
|
144
|
+
last_attached_at = row.get('last_attached_at')
|
|
145
|
+
if last_attached_at is not None:
|
|
146
|
+
last_attached_at_str = datetime.fromtimestamp(
|
|
147
|
+
last_attached_at).strftime('%Y-%m-%d %H:%M:%S')
|
|
148
|
+
else:
|
|
149
|
+
last_attached_at_str = '-'
|
|
150
|
+
size = row.get('size', '')
|
|
151
|
+
if size:
|
|
152
|
+
size = f'{size}Gi'
|
|
153
|
+
usedby_str = '-'
|
|
154
|
+
usedby_clusters = row.get('usedby_clusters')
|
|
155
|
+
usedby_pods = row.get('usedby_pods')
|
|
156
|
+
if usedby_clusters:
|
|
157
|
+
usedby_str = f'{", ".join(usedby_clusters)}'
|
|
158
|
+
elif usedby_pods:
|
|
159
|
+
usedby_str = f'{", ".join(usedby_pods)}'
|
|
160
|
+
if show_all:
|
|
161
|
+
usedby = usedby_str
|
|
162
|
+
else:
|
|
163
|
+
usedby = common_utils.truncate_long_string(
|
|
164
|
+
usedby_str, constants.USED_BY_TRUNC_LENGTH)
|
|
165
|
+
infra = _get_infra_str(row.get('cloud'), row.get('region'),
|
|
166
|
+
row.get('zone'))
|
|
167
|
+
return [
|
|
168
|
+
row.get('name', ''),
|
|
169
|
+
row.get('type', ''),
|
|
170
|
+
infra,
|
|
171
|
+
size,
|
|
172
|
+
row.get('user_name', '-'),
|
|
173
|
+
row.get('workspace', '-'),
|
|
174
|
+
log_utils.human_duration(row.get('launched_at', 0)),
|
|
175
|
+
row.get('status', ''),
|
|
176
|
+
last_attached_at_str,
|
|
177
|
+
usedby,
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
def _create_table(self, show_all: bool = False) -> prettytable.PrettyTable:
|
|
181
|
+
"""Create the volume table."""
|
|
182
|
+
raise NotImplementedError
|
|
183
|
+
|
|
184
|
+
def _add_rows(self,
|
|
185
|
+
volumes: List[responses.VolumeRecord],
|
|
186
|
+
show_all: bool = False) -> None:
|
|
187
|
+
"""Add rows to the volume table."""
|
|
188
|
+
raise NotImplementedError
|
|
189
|
+
|
|
190
|
+
@abc.abstractmethod
|
|
191
|
+
def format(self) -> str:
|
|
192
|
+
"""Format the volume table for display."""
|
|
193
|
+
raise NotImplementedError
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class PVCVolumeTable(VolumeTable):
|
|
197
|
+
"""The PVC volume table."""
|
|
198
|
+
|
|
199
|
+
def _create_table(self, show_all: bool = False) -> prettytable.PrettyTable:
|
|
200
|
+
"""Create the PVC volume table."""
|
|
201
|
+
# If show_all is False, show the table with the columns:
|
|
202
|
+
# NAME, TYPE, INFRA, SIZE, USER, WORKSPACE,
|
|
203
|
+
# AGE, STATUS, LAST_USE, USED_BY
|
|
204
|
+
# If show_all is True, show the table with the columns:
|
|
205
|
+
# NAME, TYPE, INFRA, SIZE, USER, WORKSPACE,
|
|
206
|
+
# AGE, STATUS, LAST_USE, USED_BY, NAME_ON_CLOUD
|
|
207
|
+
# STORAGE_CLASS, ACCESS_MODE
|
|
208
|
+
|
|
209
|
+
if show_all:
|
|
210
|
+
columns = _BASIC_COLUMNS + [
|
|
211
|
+
'NAME_ON_CLOUD',
|
|
212
|
+
'STORAGE_CLASS',
|
|
213
|
+
'ACCESS_MODE',
|
|
214
|
+
]
|
|
215
|
+
else:
|
|
216
|
+
columns = _BASIC_COLUMNS
|
|
217
|
+
|
|
218
|
+
table = log_utils.create_table(columns)
|
|
219
|
+
return table
|
|
220
|
+
|
|
221
|
+
def _add_rows(self,
|
|
222
|
+
volumes: List[responses.VolumeRecord],
|
|
223
|
+
show_all: bool = False) -> None:
|
|
224
|
+
"""Add rows to the PVC volume table."""
|
|
225
|
+
for row in volumes:
|
|
226
|
+
table_row = self._get_row_base_columns(row, show_all)
|
|
227
|
+
if show_all:
|
|
228
|
+
table_row.append(row.get('name_on_cloud', ''))
|
|
229
|
+
table_row.append(
|
|
230
|
+
row.get('config', {}).get('storage_class_name', '-'))
|
|
231
|
+
table_row.append(row.get('config', {}).get('access_mode', ''))
|
|
232
|
+
|
|
233
|
+
self.table.add_row(table_row)
|
|
234
|
+
|
|
235
|
+
def format(self) -> str:
|
|
236
|
+
"""Format the PVC volume table for display."""
|
|
237
|
+
return 'Kubernetes PVCs:\n' + str(self.table)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class RunPodVolumeTable(VolumeTable):
|
|
241
|
+
"""The RunPod volume table."""
|
|
242
|
+
|
|
243
|
+
def _create_table(self, show_all: bool = False) -> prettytable.PrettyTable:
|
|
244
|
+
"""Create the RunPod volume table."""
|
|
245
|
+
# If show_all is False, show the table with the columns:
|
|
246
|
+
# NAME, TYPE, INFRA, SIZE, USER, WORKSPACE,
|
|
247
|
+
# AGE, STATUS, LAST_USE, USED_BY
|
|
248
|
+
# If show_all is True, show the table with the columns:
|
|
249
|
+
# NAME, TYPE, INFRA, SIZE, USER, WORKSPACE,
|
|
250
|
+
# AGE, STATUS, LAST_USE, USED_BY, NAME_ON_CLOUD
|
|
251
|
+
|
|
252
|
+
if show_all:
|
|
253
|
+
columns = _BASIC_COLUMNS + ['NAME_ON_CLOUD']
|
|
254
|
+
else:
|
|
255
|
+
columns = _BASIC_COLUMNS
|
|
256
|
+
|
|
257
|
+
table = log_utils.create_table(columns)
|
|
258
|
+
return table
|
|
259
|
+
|
|
260
|
+
def _add_rows(self,
|
|
261
|
+
volumes: List[responses.VolumeRecord],
|
|
262
|
+
show_all: bool = False) -> None:
|
|
263
|
+
"""Add rows to the RunPod volume table."""
|
|
264
|
+
for row in volumes:
|
|
265
|
+
table_row = self._get_row_base_columns(row, show_all)
|
|
266
|
+
if show_all:
|
|
267
|
+
table_row.append(row.get('name_on_cloud', ''))
|
|
268
|
+
|
|
269
|
+
self.table.add_row(table_row)
|
|
270
|
+
|
|
271
|
+
def format(self) -> str:
|
|
272
|
+
"""Format the RunPod volume table for display."""
|
|
273
|
+
return 'RunPod Network Volumes:\n' + str(self.table)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def format_volume_table(volumes: List[responses.VolumeRecord],
|
|
277
|
+
show_all: bool = False) -> str:
|
|
278
|
+
"""Format the volume table for display.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
volume_table (dict): The volume table.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
str: The formatted volume table.
|
|
285
|
+
"""
|
|
286
|
+
volumes_per_type: Dict[str, List[responses.VolumeRecord]] = {}
|
|
287
|
+
supported_volume_types = [
|
|
288
|
+
volume_type.value for volume_type in volume.VolumeType
|
|
289
|
+
]
|
|
290
|
+
for row in volumes:
|
|
291
|
+
volume_type = row.get('type', '')
|
|
292
|
+
if volume_type in supported_volume_types:
|
|
293
|
+
if volume_type not in volumes_per_type:
|
|
294
|
+
volumes_per_type[volume_type] = []
|
|
295
|
+
volumes_per_type[volume_type].append(row)
|
|
296
|
+
else:
|
|
297
|
+
logger.warning(f'Unknown volume type: {volume_type}')
|
|
298
|
+
continue
|
|
299
|
+
table_str = ''
|
|
300
|
+
for volume_type, volume_list in volumes_per_type.items():
|
|
301
|
+
if table_str:
|
|
302
|
+
table_str += '\n\n'
|
|
303
|
+
if volume_type == volume.VolumeType.PVC.value:
|
|
304
|
+
pvc_table = PVCVolumeTable(volume_list, show_all)
|
|
305
|
+
table_str += pvc_table.format()
|
|
306
|
+
elif volume_type == volume.VolumeType.RUNPOD_NETWORK_VOLUME.value:
|
|
307
|
+
runpod_table = RunPodVolumeTable(volume_list, show_all)
|
|
308
|
+
table_str += runpod_table.format()
|
|
309
|
+
if table_str:
|
|
310
|
+
return table_str
|
|
311
|
+
else:
|
|
312
|
+
return 'No existing volumes.'
|
sky/clouds/cudo.py
CHANGED
|
@@ -288,7 +288,7 @@ class Cudo(clouds.Cloud):
|
|
|
288
288
|
cls) -> Tuple[bool, Optional[Union[str, Dict[str, str]]]]:
|
|
289
289
|
"""Checks if the user has access credentials to
|
|
290
290
|
Cudo's compute service."""
|
|
291
|
-
if not common.can_import_modules(['
|
|
291
|
+
if not common.can_import_modules(['cudo_compute']):
|
|
292
292
|
return False, (f'{cls._DEPENDENCY_HINT}\n'
|
|
293
293
|
f'{cls._INDENT_PREFIX}')
|
|
294
294
|
|
sky/clouds/kubernetes.py
CHANGED
|
@@ -25,6 +25,7 @@ from sky.provision.kubernetes.utils import normalize_tpu_accelerator_name
|
|
|
25
25
|
from sky.skylet import constants
|
|
26
26
|
from sky.utils import annotations
|
|
27
27
|
from sky.utils import common_utils
|
|
28
|
+
from sky.utils import env_options
|
|
28
29
|
from sky.utils import kubernetes_enums
|
|
29
30
|
from sky.utils import registry
|
|
30
31
|
from sky.utils import resources_utils
|
|
@@ -47,9 +48,6 @@ _FUSERMOUNT_SHARED_DIR = '/var/run/fusermount'
|
|
|
47
48
|
class Kubernetes(clouds.Cloud):
|
|
48
49
|
"""Kubernetes."""
|
|
49
50
|
|
|
50
|
-
SKY_SSH_KEY_SECRET_NAME = 'sky-ssh-keys'
|
|
51
|
-
SKY_SSH_JUMP_NAME = 'sky-ssh-jump-pod'
|
|
52
|
-
|
|
53
51
|
# Limit the length of the cluster name to avoid exceeding the limit of 63
|
|
54
52
|
# characters for Kubernetes resources. We limit to 42 characters (63-21) to
|
|
55
53
|
# allow additional characters for creating ingress services to expose ports.
|
|
@@ -98,14 +96,6 @@ class Kubernetes(clouds.Cloud):
|
|
|
98
96
|
# Set of contexts that has logged as temporarily unreachable
|
|
99
97
|
logged_unreachable_contexts: Set[str] = set()
|
|
100
98
|
|
|
101
|
-
@property
|
|
102
|
-
def ssh_key_secret_field_name(self):
|
|
103
|
-
# Use a fresh user hash to avoid conflicts in the secret object naming.
|
|
104
|
-
# This can happen when the controller is reusing the same user hash
|
|
105
|
-
# through USER_ID_ENV_VAR but has a different SSH key.
|
|
106
|
-
fresh_user_hash = common_utils.generate_user_hash()
|
|
107
|
-
return f'ssh-publickey-{fresh_user_hash}'
|
|
108
|
-
|
|
109
99
|
@classmethod
|
|
110
100
|
def _unsupported_features_for_resources(
|
|
111
101
|
cls, resources: 'resources_lib.Resources'
|
|
@@ -188,6 +178,12 @@ class Kubernetes(clouds.Cloud):
|
|
|
188
178
|
ctx for ctx in all_contexts if not ctx.startswith('ssh-')
|
|
189
179
|
]
|
|
190
180
|
|
|
181
|
+
allow_all_contexts = allowed_contexts == 'all' or (
|
|
182
|
+
allowed_contexts is None and
|
|
183
|
+
env_options.Options.ALLOW_ALL_KUBERNETES_CONTEXTS.get())
|
|
184
|
+
if allow_all_contexts:
|
|
185
|
+
allowed_contexts = all_contexts
|
|
186
|
+
|
|
191
187
|
if allowed_contexts is None:
|
|
192
188
|
# Try kubeconfig if present
|
|
193
189
|
current_context = (
|
|
@@ -517,9 +513,6 @@ class Kubernetes(clouds.Cloud):
|
|
|
517
513
|
return image_id
|
|
518
514
|
|
|
519
515
|
image_id = _get_image_id(resources)
|
|
520
|
-
# TODO(romilb): Create a lightweight image for SSH jump host
|
|
521
|
-
ssh_jump_image = catalog.get_image_id_from_tag(self.IMAGE_CPU,
|
|
522
|
-
clouds='kubernetes')
|
|
523
516
|
|
|
524
517
|
# Set environment variables for the pod. Note that SkyPilot env vars
|
|
525
518
|
# are set separately when the task is run. These env vars are
|
|
@@ -693,13 +686,8 @@ class Kubernetes(clouds.Cloud):
|
|
|
693
686
|
'accelerator_count': str(acc_count),
|
|
694
687
|
'timeout': str(timeout),
|
|
695
688
|
'k8s_port_mode': port_mode.value,
|
|
696
|
-
'k8s_networking_mode': network_utils.get_networking_mode(
|
|
697
|
-
None, context=context).value,
|
|
698
|
-
'k8s_ssh_key_secret_name': self.SKY_SSH_KEY_SECRET_NAME,
|
|
699
689
|
'k8s_acc_label_key': k8s_acc_label_key,
|
|
700
690
|
'k8s_acc_label_values': k8s_acc_label_values,
|
|
701
|
-
'k8s_ssh_jump_name': self.SKY_SSH_JUMP_NAME,
|
|
702
|
-
'k8s_ssh_jump_image': ssh_jump_image,
|
|
703
691
|
'k8s_service_account_name': k8s_service_account_name,
|
|
704
692
|
'k8s_automount_sa_token': 'true',
|
|
705
693
|
'k8s_fuse_device_required': fuse_device_required,
|
sky/dashboard/out/404.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/dashboard/_next/static/css/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/dashboard/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script><script src="/dashboard/_next/static/chunks/webpack-
|
|
1
|
+
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/dashboard/_next/static/css/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/dashboard/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script><script src="/dashboard/_next/static/chunks/webpack-3286453d56f3c0a0.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_error-c66a4e8afc46f17b.js" defer=""></script><script src="/dashboard/_next/static/Haazh5IQz6F8Wyiqxcaj8/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/Haazh5IQz6F8Wyiqxcaj8/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"Haazh5IQz6F8Wyiqxcaj8","assetPrefix":"/dashboard","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
|
sky/dashboard/out/_next/static/{m3YT2i5s6v4SsIdYc8WZa → Haazh5IQz6F8Wyiqxcaj8}/_buildManifest.js
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
self.__BUILD_MANIFEST=function(s,c,a,t,
|
|
1
|
+
self.__BUILD_MANIFEST=function(s,c,a,e,t,f,u,n,b,o,j,i,r,k){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-444f1804401f04ea.js"],"/_error":["static/chunks/pages/_error-c66a4e8afc46f17b.js"],"/clusters":["static/chunks/pages/clusters-469814d711d63b1b.js"],"/clusters/[cluster]":[s,c,a,f,u,"static/chunks/4676-9da7fdbde90b5549.js",o,e,t,n,j,b,i,"static/chunks/6856-5fdc9b851a18acdb.js",r,k,"static/chunks/9037-d0c00018a5ba198c.js","static/chunks/pages/clusters/[cluster]-e052384df65ef200.js"],"/clusters/[cluster]/[job]":[s,c,a,f,e,t,b,"static/chunks/pages/clusters/[cluster]/[job]-72794fc3fcdd517a.js"],"/config":["static/chunks/pages/config-dfb9bf07b13045f4.js"],"/infra":["static/chunks/pages/infra-aabba60d57826e0f.js"],"/infra/[context]":["static/chunks/pages/infra/[context]-6563820e094f68ca.js"],"/jobs":["static/chunks/pages/jobs-1f70d9faa564804f.js"],"/jobs/pools/[pool]":[s,c,a,u,o,e,t,n,"static/chunks/pages/jobs/pools/[pool]-509b2977a6373bf6.js"],"/jobs/[job]":[s,c,a,f,u,o,e,t,n,b,"static/chunks/pages/jobs/[job]-dd64309c3fe67ed2.js"],"/users":["static/chunks/pages/users-018bf31cda52e11b.js"],"/volumes":["static/chunks/pages/volumes-739726d6b823f532.js"],"/workspace/new":["static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js"],"/workspaces":["static/chunks/pages/workspaces-7528cc0ef8c522c5.js"],"/workspaces/[name]":[s,c,a,f,u,"static/chunks/1836-37fede578e2da5f8.js",e,t,n,j,b,i,r,k,"static/chunks/1141-159df2d4c441a9d1.js","static/chunks/pages/workspaces/[name]-af76bb06dbb3954f.js"],sortedPages:["/","/_app","/_error","/clusters","/clusters/[cluster]","/clusters/[cluster]/[job]","/config","/infra","/infra/[context]","/jobs","/jobs/pools/[pool]","/jobs/[job]","/users","/volumes","/workspace/new","/workspaces","/workspaces/[name]"]}}("static/chunks/616-3d59f75e2ccf9321.js","static/chunks/6130-2be46d70a38f1e82.js","static/chunks/5739-d67458fcb1386c92.js","static/chunks/6989-01359c57e018caa4.js","static/chunks/3850-ff4a9a69d978632b.js","static/chunks/7411-b15471acd2cba716.js","static/chunks/1272-1ef0bf0237faccdb.js","static/chunks/8969-66237729cdf9749e.js","static/chunks/6135-4b4d5e824b7f9d3c.js","static/chunks/754-d0da8ab45f9509e9.js","static/chunks/6990-f6818c84ed8f1c86.js","static/chunks/1121-d0782b9251f0fcd3.js","static/chunks/6601-06114c982db410b6.js","static/chunks/3015-8d748834fcc60b46.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|