skypilot-nightly 1.0.0.dev20250624__py3-none-any.whl → 1.0.0.dev20250626__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 +1 -6
- sky/backends/backend_utils.py +26 -11
- sky/backends/cloud_vm_ray_backend.py +16 -5
- sky/client/cli/command.py +232 -9
- sky/client/sdk.py +195 -91
- sky/clouds/aws.py +10 -7
- sky/clouds/azure.py +10 -7
- sky/clouds/cloud.py +2 -0
- sky/clouds/cudo.py +2 -0
- sky/clouds/do.py +10 -7
- sky/clouds/fluidstack.py +2 -0
- sky/clouds/gcp.py +10 -7
- sky/clouds/hyperbolic.py +10 -7
- sky/clouds/ibm.py +2 -0
- sky/clouds/kubernetes.py +26 -9
- sky/clouds/lambda_cloud.py +10 -7
- sky/clouds/nebius.py +10 -7
- sky/clouds/oci.py +10 -7
- sky/clouds/paperspace.py +10 -7
- sky/clouds/runpod.py +10 -7
- sky/clouds/scp.py +10 -7
- sky/clouds/ssh.py +36 -0
- sky/clouds/vast.py +10 -7
- sky/clouds/vsphere.py +2 -0
- sky/core.py +21 -0
- sky/dag.py +14 -0
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/bs6UB9V4Jq10TIZ5x-kBK/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/141-fa5a20cbf401b351.js +11 -0
- sky/dashboard/out/_next/static/chunks/230-d6e363362017ff3a.js +1 -0
- sky/dashboard/out/_next/static/chunks/25.76c246239df93d50.js +6 -0
- sky/dashboard/out/_next/static/chunks/43-36177d00f6956ab2.js +1 -0
- sky/dashboard/out/_next/static/chunks/430.ed51037d1a4a438b.js +1 -0
- sky/dashboard/out/_next/static/chunks/470-92dd1614396389be.js +1 -0
- sky/dashboard/out/_next/static/chunks/544.110e53813fb98e2e.js +1 -0
- sky/dashboard/out/_next/static/chunks/645.961f08e39b8ce447.js +1 -0
- sky/dashboard/out/_next/static/chunks/690.55f9eed3be903f56.js +16 -0
- sky/dashboard/out/_next/static/chunks/697.6460bf72e760addd.js +20 -0
- sky/dashboard/out/_next/static/chunks/785.dc2686c3c1235554.js +1 -0
- sky/dashboard/out/_next/static/chunks/871-3db673be3ee3750b.js +6 -0
- sky/dashboard/out/_next/static/chunks/875.52c962183328b3f2.js +25 -0
- sky/dashboard/out/_next/static/chunks/973-81b2d057178adb76.js +1 -0
- sky/dashboard/out/_next/static/chunks/982.1b61658204416b0f.js +1 -0
- sky/dashboard/out/_next/static/chunks/984.e8bac186a24e5178.js +1 -0
- sky/dashboard/out/_next/static/chunks/990-0ad5ea1699e03ee8.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/{_app-ce31493da9747ef4.js → _app-9a3ce3170d2edcec.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-aff040d7bc5d0086.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-8040f2483897ed0c.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/{clusters-7e9736af1c6345a6.js → clusters-f119a5630a1efd61.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/config-6b255eae088da6a3.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-b302aea4d65766bf.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-ee8cc4d449945d19.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-e4b23128db0774cd.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-0a5695ff3075d94a.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-4978cbb093e141e7.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/volumes-476b670ef33d1ecd.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspace/{new-31aa8bdcb7592635.js → new-5b59bce9eb208d84.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-cb7e720b739de53a.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-50e230828730cfb3.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-08fdb9e6070127fc.js +1 -0
- sky/dashboard/out/_next/static/css/52082cf558ec9705.css +3 -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/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.html +1 -1
- sky/dashboard/out/users.html +1 -1
- sky/dashboard/out/volumes.html +1 -0
- 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 +2 -4
- sky/exceptions.py +15 -0
- sky/execution.py +5 -0
- sky/global_user_state.py +129 -0
- sky/jobs/client/sdk.py +13 -11
- sky/jobs/server/core.py +4 -0
- sky/models.py +16 -0
- sky/provision/__init__.py +26 -0
- sky/provision/kubernetes/__init__.py +3 -0
- sky/provision/kubernetes/instance.py +38 -77
- sky/provision/kubernetes/utils.py +70 -4
- sky/provision/kubernetes/volume.py +147 -0
- sky/resources.py +20 -76
- sky/serve/client/sdk.py +13 -13
- sky/serve/server/core.py +5 -1
- sky/server/common.py +40 -5
- sky/server/constants.py +5 -1
- sky/server/metrics.py +105 -0
- sky/server/requests/executor.py +30 -14
- sky/server/requests/payloads.py +16 -0
- sky/server/requests/requests.py +35 -1
- sky/server/rest.py +153 -0
- sky/server/server.py +70 -43
- sky/server/state.py +20 -0
- sky/server/stream_utils.py +8 -3
- sky/server/uvicorn.py +153 -13
- sky/setup_files/dependencies.py +2 -0
- sky/skylet/constants.py +19 -3
- sky/skypilot_config.py +3 -0
- sky/ssh_node_pools/__init__.py +1 -0
- sky/ssh_node_pools/core.py +133 -0
- sky/ssh_node_pools/server.py +232 -0
- sky/task.py +141 -18
- sky/templates/kubernetes-ray.yml.j2 +30 -1
- sky/users/permission.py +2 -0
- sky/utils/context.py +3 -1
- sky/utils/kubernetes/deploy_remote_cluster.py +12 -185
- sky/utils/kubernetes/ssh_utils.py +221 -0
- sky/utils/resources_utils.py +66 -0
- sky/utils/rich_utils.py +6 -0
- sky/utils/schemas.py +146 -3
- sky/utils/status_lib.py +10 -0
- sky/utils/validator.py +11 -1
- sky/volumes/__init__.py +0 -0
- sky/volumes/client/__init__.py +0 -0
- sky/volumes/client/sdk.py +64 -0
- sky/volumes/server/__init__.py +0 -0
- sky/volumes/server/core.py +199 -0
- sky/volumes/server/server.py +85 -0
- sky/volumes/utils.py +158 -0
- sky/volumes/volume.py +198 -0
- {skypilot_nightly-1.0.0.dev20250624.dist-info → skypilot_nightly-1.0.0.dev20250626.dist-info}/METADATA +2 -1
- {skypilot_nightly-1.0.0.dev20250624.dist-info → skypilot_nightly-1.0.0.dev20250626.dist-info}/RECORD +135 -115
- sky/dashboard/out/_next/static/chunks/211.692afc57e812ae1a.js +0 -1
- sky/dashboard/out/_next/static/chunks/350.9e123a4551f68b0d.js +0 -1
- sky/dashboard/out/_next/static/chunks/37-4650f214e2119168.js +0 -6
- sky/dashboard/out/_next/static/chunks/42.2273cc2415291ceb.js +0 -6
- sky/dashboard/out/_next/static/chunks/443.b2242d0efcdf5f47.js +0 -1
- sky/dashboard/out/_next/static/chunks/470-1494c899266cf5c9.js +0 -1
- sky/dashboard/out/_next/static/chunks/513.309df9e18a9ff005.js +0 -1
- sky/dashboard/out/_next/static/chunks/641.c8e452bc5070a630.js +0 -1
- sky/dashboard/out/_next/static/chunks/682.4dd5dc116f740b5f.js +0 -6
- sky/dashboard/out/_next/static/chunks/760-a89d354797ce7af5.js +0 -1
- sky/dashboard/out/_next/static/chunks/843-bde186946d353355.js +0 -11
- sky/dashboard/out/_next/static/chunks/856-bfddc18e16f3873c.js +0 -1
- sky/dashboard/out/_next/static/chunks/901-b424d293275e1fd7.js +0 -1
- sky/dashboard/out/_next/static/chunks/973-56412c7976b4655b.js +0 -1
- sky/dashboard/out/_next/static/chunks/984.ae8c08791d274ca0.js +0 -50
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-4e065c812a52460b.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-520ec1ab65e2f2a4.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/config-e4f473661889e7cd.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-00fd23b9577492ca.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra-8a4bf7370d4d9bb7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-171c27f4ca94861c.js +0 -16
- sky/dashboard/out/_next/static/chunks/pages/jobs-55e5bcb16d563231.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/users-c9f4d785cdaa52d8.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-ecc5a7003776cfa7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces-f00cba35691483b1.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-c85998e6a5722f21.js +0 -1
- sky/dashboard/out/_next/static/css/6ab927686b492a4a.css +0 -3
- sky/dashboard/out/_next/static/zsALxITkbP8J8NVwSDwMo/_buildManifest.js +0 -1
- /sky/dashboard/out/_next/static/{zsALxITkbP8J8NVwSDwMo → bs6UB9V4Jq10TIZ5x-kBK}/_ssgManifest.js +0 -0
- /sky/dashboard/out/_next/static/chunks/{938-ce7991c156584b06.js → 938-068520cc11738deb.js} +0 -0
- {skypilot_nightly-1.0.0.dev20250624.dist-info → skypilot_nightly-1.0.0.dev20250626.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250624.dist-info → skypilot_nightly-1.0.0.dev20250626.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250624.dist-info → skypilot_nightly-1.0.0.dev20250626.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250624.dist-info → skypilot_nightly-1.0.0.dev20250626.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 = '512c53be5771c003cd9d94b5b89c23638a5e735e'
|
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.dev20250626'
|
39
39
|
__root_dir__ = os.path.dirname(os.path.abspath(__file__))
|
40
40
|
|
41
41
|
|
sky/adaptors/kubernetes.py
CHANGED
@@ -133,12 +133,7 @@ def _load_config(context: Optional[str] = None):
|
|
133
133
|
'\nHint: Kubernetes attempted to query the current-context '
|
134
134
|
'set in kubeconfig. Check if the current-context is valid.')
|
135
135
|
with ux_utils.print_exception_no_traceback():
|
136
|
-
|
137
|
-
# For SSH Node Pool, we don't want to surface k8s errors
|
138
|
-
# (e.g., missing context) unless debug flag is set.
|
139
|
-
logging.debug(f'Kubernetes error: {suffix}')
|
140
|
-
else:
|
141
|
-
raise ValueError(err_str) from None
|
136
|
+
raise ValueError(err_str) from None
|
142
137
|
|
143
138
|
if context == in_cluster_context_name() or context is None:
|
144
139
|
try:
|
sky/backends/backend_utils.py
CHANGED
@@ -66,6 +66,7 @@ if typing.TYPE_CHECKING:
|
|
66
66
|
from sky import task as task_lib
|
67
67
|
from sky.backends import cloud_vm_ray_backend
|
68
68
|
from sky.backends import local_docker_backend
|
69
|
+
from sky.volumes import volume as volume_lib
|
69
70
|
else:
|
70
71
|
yaml = adaptors_common.LazyImport('yaml')
|
71
72
|
requests = adaptors_common.LazyImport('requests')
|
@@ -541,16 +542,18 @@ def get_expirable_clouds(
|
|
541
542
|
# TODO: too many things happening here - leaky abstraction. Refactor.
|
542
543
|
@timeline.event
|
543
544
|
def write_cluster_config(
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
545
|
+
to_provision: 'resources_lib.Resources',
|
546
|
+
num_nodes: int,
|
547
|
+
cluster_config_template: str,
|
548
|
+
cluster_name: str,
|
549
|
+
local_wheel_path: pathlib.Path,
|
550
|
+
wheel_hash: str,
|
551
|
+
region: clouds.Region,
|
552
|
+
zones: Optional[List[clouds.Zone]] = None,
|
553
|
+
dryrun: bool = False,
|
554
|
+
keep_launch_fields_in_existing_config: bool = True,
|
555
|
+
volume_mounts: Optional[List['volume_lib.VolumeMount']] = None,
|
556
|
+
) -> Dict[str, str]:
|
554
557
|
"""Fills in cluster configuration templates and writes them out.
|
555
558
|
|
556
559
|
Returns:
|
@@ -598,7 +601,7 @@ def write_cluster_config(
|
|
598
601
|
resources_utils.ClusterName(
|
599
602
|
cluster_name,
|
600
603
|
cluster_name_on_cloud,
|
601
|
-
), region, zones, num_nodes, dryrun)
|
604
|
+
), region, zones, num_nodes, dryrun, volume_mounts)
|
602
605
|
config_dict = {}
|
603
606
|
|
604
607
|
specific_reservations = set(
|
@@ -731,6 +734,15 @@ def write_cluster_config(
|
|
731
734
|
high_availability_specified = controller_utils.high_availability_specified(
|
732
735
|
cluster_name)
|
733
736
|
|
737
|
+
volume_mount_vars = []
|
738
|
+
if volume_mounts is not None:
|
739
|
+
for vol in volume_mounts:
|
740
|
+
volume_mount_vars.append({
|
741
|
+
'name': vol.volume_name,
|
742
|
+
'path': vol.path,
|
743
|
+
'volume_name_on_cloud': vol.volume_config.name_on_cloud,
|
744
|
+
})
|
745
|
+
|
734
746
|
# Use a tmp file path to avoid incomplete YAML file being re-used in the
|
735
747
|
# future.
|
736
748
|
tmp_yaml_path = yaml_path + '.tmp'
|
@@ -821,6 +833,9 @@ def write_cluster_config(
|
|
821
833
|
|
822
834
|
# High availability
|
823
835
|
'high_availability': high_availability_specified,
|
836
|
+
|
837
|
+
# Volume mounts
|
838
|
+
'volume_mounts': volume_mount_vars,
|
824
839
|
}),
|
825
840
|
output_path=tmp_yaml_path)
|
826
841
|
config_dict['cluster_name'] = cluster_name
|
@@ -73,6 +73,7 @@ from sky.utils import status_lib
|
|
73
73
|
from sky.utils import subprocess_utils
|
74
74
|
from sky.utils import timeline
|
75
75
|
from sky.utils import ux_utils
|
76
|
+
from sky.volumes import volume as volume_lib
|
76
77
|
|
77
78
|
if typing.TYPE_CHECKING:
|
78
79
|
from sky import dag
|
@@ -1327,6 +1328,7 @@ class RetryingVmProvisioner(object):
|
|
1327
1328
|
prev_handle: Optional['CloudVmRayResourceHandle'],
|
1328
1329
|
prev_cluster_ever_up: bool,
|
1329
1330
|
skip_if_config_hash_matches: Optional[str],
|
1331
|
+
volume_mounts: Optional[List[volume_lib.VolumeMount]],
|
1330
1332
|
) -> Dict[str, Any]:
|
1331
1333
|
"""The provision retry loop.
|
1332
1334
|
|
@@ -1432,7 +1434,9 @@ class RetryingVmProvisioner(object):
|
|
1432
1434
|
region=region,
|
1433
1435
|
zones=zones,
|
1434
1436
|
dryrun=dryrun,
|
1435
|
-
keep_launch_fields_in_existing_config=cluster_exists
|
1437
|
+
keep_launch_fields_in_existing_config=cluster_exists,
|
1438
|
+
volume_mounts=volume_mounts,
|
1439
|
+
)
|
1436
1440
|
except exceptions.ResourcesUnavailableError as e:
|
1437
1441
|
# Failed due to catalog issue, e.g. image not found, or
|
1438
1442
|
# GPUs are requested in a Kubernetes cluster but the cluster
|
@@ -2081,7 +2085,9 @@ class RetryingVmProvisioner(object):
|
|
2081
2085
|
prev_cluster_status=prev_cluster_status,
|
2082
2086
|
prev_handle=prev_handle,
|
2083
2087
|
prev_cluster_ever_up=prev_cluster_ever_up,
|
2084
|
-
skip_if_config_hash_matches=skip_if_config_hash_matches
|
2088
|
+
skip_if_config_hash_matches=skip_if_config_hash_matches,
|
2089
|
+
volume_mounts=task.volume_mounts,
|
2090
|
+
)
|
2085
2091
|
if dryrun:
|
2086
2092
|
return config_dict
|
2087
2093
|
except (exceptions.InvalidClusterNameError,
|
@@ -2435,9 +2441,14 @@ class CloudVmRayResourceHandle(backends.backend.ResourceHandle):
|
|
2435
2441
|
zip(cluster_internal_ips, cluster_feasible_ips))
|
2436
2442
|
|
2437
2443
|
# Ensure head node is the first element, then sort based on the
|
2438
|
-
# external IPs for stableness
|
2439
|
-
|
2440
|
-
|
2444
|
+
# external IPs for stableness. Skip for k8s nodes since pods
|
2445
|
+
# worker ids are already mapped.
|
2446
|
+
if (cluster_info is not None and
|
2447
|
+
cluster_info.provider_name == 'kubernetes'):
|
2448
|
+
stable_internal_external_ips = internal_external_ips
|
2449
|
+
else:
|
2450
|
+
stable_internal_external_ips = [internal_external_ips[0]] + sorted(
|
2451
|
+
internal_external_ips[1:], key=lambda x: x[1])
|
2441
2452
|
self.stable_internal_external_ips = stable_internal_external_ips
|
2442
2453
|
|
2443
2454
|
@context_utils.cancellation_guard
|
sky/client/cli/command.py
CHANGED
@@ -81,6 +81,8 @@ from sky.utils import subprocess_utils
|
|
81
81
|
from sky.utils import timeline
|
82
82
|
from sky.utils import ux_utils
|
83
83
|
from sky.utils.cli_utils import status_utils
|
84
|
+
from sky.volumes import utils as volumes_utils
|
85
|
+
from sky.volumes.client import sdk as volumes_sdk
|
84
86
|
|
85
87
|
if typing.TYPE_CHECKING:
|
86
88
|
import types
|
@@ -202,13 +204,14 @@ def _get_cluster_records_and_set_ssh_config(
|
|
202
204
|
|
203
205
|
|
204
206
|
def _get_glob_matches(candidate_names: List[str],
|
205
|
-
glob_patterns: List[str]
|
207
|
+
glob_patterns: List[str],
|
208
|
+
resource_type: str = 'Storage') -> List[str]:
|
206
209
|
"""Returns a list of names that match the glob pattern."""
|
207
210
|
glob_storages = []
|
208
211
|
for glob_pattern in glob_patterns:
|
209
212
|
glob_storage = fnmatch.filter(candidate_names, glob_pattern)
|
210
213
|
if not glob_storage:
|
211
|
-
click.echo(f'
|
214
|
+
click.echo(f'{resource_type} {glob_pattern} not found.')
|
212
215
|
glob_storages.extend(glob_storage)
|
213
216
|
return list(set(glob_storages))
|
214
217
|
|
@@ -290,6 +293,19 @@ def _complete_storage_name(ctx: click.Context, param: click.Parameter,
|
|
290
293
|
return response.json()
|
291
294
|
|
292
295
|
|
296
|
+
def _complete_volume_name(ctx: click.Context, param: click.Parameter,
|
297
|
+
incomplete: str) -> List[str]:
|
298
|
+
"""Handle shell completion for volume names."""
|
299
|
+
del ctx, param # Unused.
|
300
|
+
response = requests_lib.get(
|
301
|
+
f'{server_common.get_server_url()}'
|
302
|
+
f'/api/completion/volume_name?incomplete={incomplete}',
|
303
|
+
timeout=2.0,
|
304
|
+
)
|
305
|
+
response.raise_for_status()
|
306
|
+
return response.json()
|
307
|
+
|
308
|
+
|
293
309
|
def _complete_file_name(ctx: click.Context, param: click.Parameter,
|
294
310
|
incomplete: str) -> List[str]:
|
295
311
|
"""Handle shell completion for file names.
|
@@ -531,8 +547,9 @@ def _parse_override_params(
|
|
531
547
|
return override_params
|
532
548
|
|
533
549
|
|
534
|
-
def
|
535
|
-
|
550
|
+
def _check_yaml_only(
|
551
|
+
entrypoint: str) -> Tuple[bool, Optional[Dict[str, Any]], bool, str]:
|
552
|
+
"""Checks if entrypoint is a readable YAML file without confirmation.
|
536
553
|
|
537
554
|
Args:
|
538
555
|
entrypoint: Path to a YAML file.
|
@@ -580,6 +597,17 @@ def _check_yaml(entrypoint: str) -> Tuple[bool, Optional[Dict[str, Any]]]:
|
|
580
597
|
invalid_reason = ('yaml.safe_load() failed. Please check if the'
|
581
598
|
' path is correct.')
|
582
599
|
is_yaml = False
|
600
|
+
return is_yaml, result, yaml_file_provided, invalid_reason
|
601
|
+
|
602
|
+
|
603
|
+
def _check_yaml(entrypoint: str) -> Tuple[bool, Optional[Dict[str, Any]]]:
|
604
|
+
"""Checks if entrypoint is a readable YAML file.
|
605
|
+
|
606
|
+
Args:
|
607
|
+
entrypoint: Path to a YAML file.
|
608
|
+
"""
|
609
|
+
is_yaml, result, yaml_file_provided, invalid_reason = _check_yaml_only(
|
610
|
+
entrypoint)
|
583
611
|
if not is_yaml:
|
584
612
|
if yaml_file_provided:
|
585
613
|
click.confirm(
|
@@ -3767,6 +3795,196 @@ def storage_delete(names: List[str], all: bool, yes: bool, async_call: bool): #
|
|
3767
3795
|
f'{colorama.Style.RESET_ALL}')
|
3768
3796
|
|
3769
3797
|
|
3798
|
+
@cli.group(cls=_NaturalOrderGroup)
|
3799
|
+
def volumes():
|
3800
|
+
"""SkyPilot Volumes CLI."""
|
3801
|
+
pass
|
3802
|
+
|
3803
|
+
|
3804
|
+
@volumes.command('apply', cls=_DocumentedCodeCommand)
|
3805
|
+
@flags.config_option(expose_value=False)
|
3806
|
+
@click.argument('entrypoint',
|
3807
|
+
required=False,
|
3808
|
+
type=str,
|
3809
|
+
nargs=-1,
|
3810
|
+
**_get_shell_complete_args(_complete_file_name))
|
3811
|
+
@click.option('--name',
|
3812
|
+
'-n',
|
3813
|
+
required=False,
|
3814
|
+
type=str,
|
3815
|
+
help='Volume name. Override the name defined in the YAML.')
|
3816
|
+
@click.option('--infra',
|
3817
|
+
required=False,
|
3818
|
+
type=str,
|
3819
|
+
help='Infra. Format: k8s, k8s/context-name. '
|
3820
|
+
'Override the infra defined in the YAML.')
|
3821
|
+
@click.option(
|
3822
|
+
'--type',
|
3823
|
+
required=False,
|
3824
|
+
type=str,
|
3825
|
+
help='Volume type. Format: pvc. Override the type defined in the YAML.')
|
3826
|
+
@click.option('--size',
|
3827
|
+
required=False,
|
3828
|
+
type=str,
|
3829
|
+
help='Volume size. Override the size defined in the YAML.')
|
3830
|
+
@click.option('--yes',
|
3831
|
+
'-y',
|
3832
|
+
is_flag=True,
|
3833
|
+
default=False,
|
3834
|
+
required=False,
|
3835
|
+
help='Skip confirmation prompt.')
|
3836
|
+
@_add_click_options(flags.COMMON_OPTIONS)
|
3837
|
+
@usage_lib.entrypoint
|
3838
|
+
def volumes_apply(
|
3839
|
+
entrypoint: Optional[Tuple[str, ...]],
|
3840
|
+
name: Optional[str],
|
3841
|
+
infra: Optional[str],
|
3842
|
+
type: Optional[str], # pylint: disable=redefined-builtin
|
3843
|
+
size: Optional[str],
|
3844
|
+
yes: bool,
|
3845
|
+
async_call: bool):
|
3846
|
+
"""Apply a volume.
|
3847
|
+
|
3848
|
+
Examples:
|
3849
|
+
|
3850
|
+
.. code-block:: bash
|
3851
|
+
|
3852
|
+
# Apply a volume from a YAML file.
|
3853
|
+
sky volumes apply volume.yaml
|
3854
|
+
\b
|
3855
|
+
# Apply a volume from a command.
|
3856
|
+
sky volumes apply --name pvc1 --infra k8s --type pvc --size 100Gi
|
3857
|
+
"""
|
3858
|
+
# pylint: disable=import-outside-toplevel
|
3859
|
+
from sky.volumes import volume as volume_lib
|
3860
|
+
|
3861
|
+
volume_config_dict: Dict[str, Any] = {}
|
3862
|
+
if entrypoint is not None and len(entrypoint) > 0:
|
3863
|
+
entrypoint_str = ' '.join(entrypoint)
|
3864
|
+
is_yaml, yaml_config, yaml_file_provided, invalid_reason = (
|
3865
|
+
_check_yaml_only(entrypoint_str))
|
3866
|
+
if not is_yaml:
|
3867
|
+
if yaml_file_provided:
|
3868
|
+
raise click.BadParameter(f'{entrypoint_str!r} looks like a '
|
3869
|
+
f'yaml path but {invalid_reason}')
|
3870
|
+
else:
|
3871
|
+
raise click.BadParameter(
|
3872
|
+
f'{entrypoint_str!r} needs to be a YAML file')
|
3873
|
+
if yaml_config is not None:
|
3874
|
+
volume_config_dict = yaml_config.copy()
|
3875
|
+
|
3876
|
+
# Create Volume instance
|
3877
|
+
volume = volume_lib.Volume.from_dict(volume_config_dict)
|
3878
|
+
|
3879
|
+
# Normalize the volume config with CLI options
|
3880
|
+
volume.normalize_config(name=name, infra=infra, type=type, size=size)
|
3881
|
+
|
3882
|
+
logger.debug(f'Volume config: {volume.to_dict()}')
|
3883
|
+
|
3884
|
+
if not yes:
|
3885
|
+
click.confirm(f'Proceed to create volume {volume.name!r}?',
|
3886
|
+
default=True,
|
3887
|
+
abort=True,
|
3888
|
+
show_default=True)
|
3889
|
+
|
3890
|
+
# Call SDK to create volume
|
3891
|
+
try:
|
3892
|
+
request_id = volumes_sdk.apply(volume)
|
3893
|
+
_async_call_or_wait(request_id, async_call, 'sky.volumes.apply')
|
3894
|
+
except RuntimeError as e:
|
3895
|
+
logger.error(f'{colorama.Fore.RED}Error applying volume: '
|
3896
|
+
f'{common_utils.format_exception(e, use_bracket=True)}'
|
3897
|
+
f'{colorama.Style.RESET_ALL}')
|
3898
|
+
|
3899
|
+
|
3900
|
+
@volumes.command('ls', cls=_DocumentedCodeCommand)
|
3901
|
+
@flags.config_option(expose_value=False)
|
3902
|
+
@click.option('--verbose',
|
3903
|
+
'-v',
|
3904
|
+
default=False,
|
3905
|
+
is_flag=True,
|
3906
|
+
required=False,
|
3907
|
+
help='Show all information in full.')
|
3908
|
+
@usage_lib.entrypoint
|
3909
|
+
def volumes_ls(verbose: bool):
|
3910
|
+
"""List volumes managed by SkyPilot."""
|
3911
|
+
request_id = volumes_sdk.ls()
|
3912
|
+
all_volumes = sdk.stream_and_get(request_id)
|
3913
|
+
volume_table = volumes_utils.format_volume_table(all_volumes,
|
3914
|
+
show_all=verbose)
|
3915
|
+
click.echo(volume_table)
|
3916
|
+
|
3917
|
+
|
3918
|
+
@volumes.command('delete', cls=_DocumentedCodeCommand)
|
3919
|
+
@flags.config_option(expose_value=False)
|
3920
|
+
@click.argument('names',
|
3921
|
+
required=False,
|
3922
|
+
type=str,
|
3923
|
+
nargs=-1,
|
3924
|
+
**_get_shell_complete_args(_complete_volume_name))
|
3925
|
+
@click.option('--all',
|
3926
|
+
'-a',
|
3927
|
+
default=False,
|
3928
|
+
is_flag=True,
|
3929
|
+
required=False,
|
3930
|
+
help='Delete all volumes.')
|
3931
|
+
@click.option('--yes',
|
3932
|
+
'-y',
|
3933
|
+
default=False,
|
3934
|
+
is_flag=True,
|
3935
|
+
required=False,
|
3936
|
+
help='Skip confirmation prompt.')
|
3937
|
+
@_add_click_options(flags.COMMON_OPTIONS)
|
3938
|
+
@usage_lib.entrypoint
|
3939
|
+
def volumes_delete(names: List[str], all: bool, yes: bool, async_call: bool): # pylint: disable=redefined-builtin
|
3940
|
+
"""Delete volumes.
|
3941
|
+
|
3942
|
+
Examples:
|
3943
|
+
|
3944
|
+
.. code-block:: bash
|
3945
|
+
|
3946
|
+
# Delete two volumes.
|
3947
|
+
sky volumes delete pvc1 pvc2
|
3948
|
+
\b
|
3949
|
+
# Delete all volumes matching glob pattern 'pvc*'.
|
3950
|
+
sky volumes delete "pvc*"
|
3951
|
+
\b
|
3952
|
+
# Delete all volumes.
|
3953
|
+
sky volumes delete -a
|
3954
|
+
"""
|
3955
|
+
if sum([bool(names), all]) != 1:
|
3956
|
+
raise click.UsageError('Either --all or a name must be specified.')
|
3957
|
+
all_volumes = sdk.get(volumes_sdk.ls())
|
3958
|
+
if all:
|
3959
|
+
if not all_volumes:
|
3960
|
+
click.echo('No volumes to delete.')
|
3961
|
+
return
|
3962
|
+
names = [volume['name'] for volume in all_volumes]
|
3963
|
+
else:
|
3964
|
+
existing_volume_names = [volume['name'] for volume in all_volumes]
|
3965
|
+
names = _get_glob_matches(existing_volume_names,
|
3966
|
+
names,
|
3967
|
+
resource_type='Volume')
|
3968
|
+
if names:
|
3969
|
+
if not yes:
|
3970
|
+
volume_names = ', '.join(names)
|
3971
|
+
volume_str = 'volumes' if len(names) > 1 else 'volume'
|
3972
|
+
click.confirm(
|
3973
|
+
f'Deleting {len(names)} {volume_str}: '
|
3974
|
+
f'{volume_names}. Proceed?',
|
3975
|
+
default=True,
|
3976
|
+
abort=True,
|
3977
|
+
show_default=True)
|
3978
|
+
|
3979
|
+
try:
|
3980
|
+
_async_call_or_wait(volumes_sdk.delete(names), async_call,
|
3981
|
+
'sky.volumes.delete')
|
3982
|
+
except Exception as e: # pylint: disable=broad-except
|
3983
|
+
logger.error(f'{colorama.Fore.RED}Error deleting volumes {names}: '
|
3984
|
+
f'{common_utils.format_exception(e, use_bracket=True)}'
|
3985
|
+
f'{colorama.Style.RESET_ALL}')
|
3986
|
+
|
3987
|
+
|
3770
3988
|
@cli.group(cls=_NaturalOrderGroup)
|
3771
3989
|
def jobs():
|
3772
3990
|
"""Managed Jobs CLI (jobs with auto-recovery)."""
|
@@ -5186,13 +5404,18 @@ def ssh():
|
|
5186
5404
|
is_flag=True,
|
5187
5405
|
hidden=True,
|
5188
5406
|
help='Run the command asynchronously.')
|
5189
|
-
|
5190
|
-
|
5407
|
+
@click.option('--file',
|
5408
|
+
'-f',
|
5409
|
+
required=False,
|
5410
|
+
help='The file containing the SSH targets.')
|
5411
|
+
def ssh_up(infra: Optional[str], async_call: bool, file: Optional[str]):
|
5412
|
+
"""Set up a cluster using SSH targets from a file. If not specified,
|
5413
|
+
~/.sky/ssh_node_pools.yaml will be used.
|
5191
5414
|
|
5192
|
-
This command sets up a Kubernetes cluster on the machines specified in
|
5193
|
-
|
5415
|
+
This command sets up a Kubernetes cluster on the machines specified in the
|
5416
|
+
config file and configures SkyPilot to use it.
|
5194
5417
|
"""
|
5195
|
-
request_id = sdk.ssh_up(infra=infra)
|
5418
|
+
request_id = sdk.ssh_up(infra=infra, file=file)
|
5196
5419
|
if async_call:
|
5197
5420
|
print(f'Request submitted with ID: {request_id}')
|
5198
5421
|
else:
|