skypilot-nightly 1.0.0.dev20250520__py3-none-any.whl → 1.0.0.dev20250522__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/backends/backend_utils.py +4 -1
- sky/backends/cloud_vm_ray_backend.py +56 -37
- sky/check.py +3 -3
- sky/cli.py +89 -16
- sky/client/cli.py +89 -16
- sky/client/sdk.py +92 -4
- sky/clouds/__init__.py +2 -0
- sky/clouds/cloud.py +6 -0
- sky/clouds/gcp.py +156 -21
- sky/clouds/service_catalog/__init__.py +3 -0
- sky/clouds/service_catalog/common.py +9 -2
- sky/clouds/service_catalog/constants.py +1 -0
- sky/core.py +6 -8
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/CzOVV6JpRQBRt5GhZuhyK/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/236-1a3a9440417720eb.js +6 -0
- sky/dashboard/out/_next/static/chunks/37-d584022b0da4ac3b.js +6 -0
- sky/dashboard/out/_next/static/chunks/393-e1eaa440481337ec.js +1 -0
- sky/dashboard/out/_next/static/chunks/480-f28cd152a98997de.js +1 -0
- sky/dashboard/out/_next/static/chunks/{678-206dddca808e6d16.js → 582-683f4f27b81996dc.js} +2 -2
- sky/dashboard/out/_next/static/chunks/pages/_app-8cfab319f9fb3ae8.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33bc2bec322249b1.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-e2fc2dd1955e6c36.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-3a748bd76e5c2984.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-9180cd91cee64b96.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-70756c2dad850a7e.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-ecd804b9272f4a7c.js +1 -0
- sky/dashboard/out/_next/static/css/7e7ce4ff31d3977b.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/index.html +1 -1
- sky/dashboard/out/infra.html +1 -0
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/data/storage.py +1 -0
- sky/execution.py +57 -8
- sky/jobs/server/core.py +5 -3
- sky/jobs/utils.py +38 -7
- sky/optimizer.py +41 -39
- 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/provisioner.py +16 -7
- sky/resources.py +233 -18
- sky/serve/serve_utils.py +5 -13
- sky/serve/server/core.py +2 -4
- sky/server/common.py +60 -14
- 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 +3 -3
- sky/server/server.py +40 -8
- 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 +0 -2
- sky/utils/admin_policy_utils.py +26 -22
- sky/utils/cli_utils/status_utils.py +95 -56
- sky/utils/common_utils.py +35 -2
- sky/utils/context.py +36 -6
- sky/utils/context_utils.py +15 -0
- sky/utils/infra_utils.py +175 -0
- sky/utils/resources_utils.py +55 -21
- sky/utils/schemas.py +111 -5
- {skypilot_nightly-1.0.0.dev20250520.dist-info → skypilot_nightly-1.0.0.dev20250522.dist-info}/METADATA +1 -1
- {skypilot_nightly-1.0.0.dev20250520.dist-info → skypilot_nightly-1.0.0.dev20250522.dist-info}/RECORD +73 -68
- {skypilot_nightly-1.0.0.dev20250520.dist-info → skypilot_nightly-1.0.0.dev20250522.dist-info}/WHEEL +1 -1
- sky/dashboard/out/_next/static/8hlc2dkbIDDBOkxtEW7X6/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/236-f49500b82ad5392d.js +0 -6
- sky/dashboard/out/_next/static/chunks/37-0a572fe0dbb89c4d.js +0 -6
- sky/dashboard/out/_next/static/chunks/845-0ca6f2c1ba667c3b.js +0 -1
- sky/dashboard/out/_next/static/chunks/979-7bf73a4c7cea0f5c.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/_app-e6b013bc3f77ad60.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-e15db85d0ea1fbe1.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-f383db7389368ea7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters-a93b93e10b8b074e.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-03f279c6741fb48b.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs-a75029b67aab6a2e.js +0 -1
- sky/dashboard/out/_next/static/css/c6933bbb2ce7f4dd.css +0 -3
- /sky/dashboard/out/_next/static/{8hlc2dkbIDDBOkxtEW7X6 → CzOVV6JpRQBRt5GhZuhyK}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250520.dist-info → skypilot_nightly-1.0.0.dev20250522.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250520.dist-info → skypilot_nightly-1.0.0.dev20250522.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250520.dist-info → skypilot_nightly-1.0.0.dev20250522.dist-info}/top_level.txt +0 -0
sky/client/cli.py
CHANGED
@@ -78,6 +78,7 @@ from sky.utils import common_utils
|
|
78
78
|
from sky.utils import controller_utils
|
79
79
|
from sky.utils import dag_utils
|
80
80
|
from sky.utils import env_options
|
81
|
+
from sky.utils import infra_utils
|
81
82
|
from sky.utils import log_utils
|
82
83
|
from sky.utils import registry
|
83
84
|
from sky.utils import resources_utils
|
@@ -345,24 +346,39 @@ _TASK_OPTIONS = [
|
|
345
346
|
'where the task will be invoked. '
|
346
347
|
'Overrides the "workdir" config in the YAML if both are supplied.'
|
347
348
|
)),
|
349
|
+
click.option(
|
350
|
+
'--infra',
|
351
|
+
required=False,
|
352
|
+
type=str,
|
353
|
+
help='Infrastructure to use. '
|
354
|
+
'Format: cloud, cloud/region, cloud/region/zone, '
|
355
|
+
'or kubernetes/context-name. '
|
356
|
+
'Examples: aws, aws/us-east-1, aws/us-east-1/us-east-1a, '
|
357
|
+
# TODO(zhwu): we have to use `\*` to make sure the docs build
|
358
|
+
# not complaining about the `*`, but this will cause `--help`
|
359
|
+
# to show `\*` instead of `*`.
|
360
|
+
'aws/\\*/us-east-1a, kubernetes/my-cluster-context.'),
|
348
361
|
click.option(
|
349
362
|
'--cloud',
|
350
363
|
required=False,
|
351
364
|
type=str,
|
352
365
|
help=('The cloud to use. If specified, overrides the "resources.cloud" '
|
353
|
-
'config. Passing "none" resets the config.')
|
366
|
+
'config. Passing "none" resets the config.'),
|
367
|
+
hidden=True),
|
354
368
|
click.option(
|
355
369
|
'--region',
|
356
370
|
required=False,
|
357
371
|
type=str,
|
358
372
|
help=('The region to use. If specified, overrides the '
|
359
|
-
'"resources.region" config. Passing "none" resets the config.')
|
373
|
+
'"resources.region" config. Passing "none" resets the config.'),
|
374
|
+
hidden=True),
|
360
375
|
click.option(
|
361
376
|
'--zone',
|
362
377
|
required=False,
|
363
378
|
type=str,
|
364
379
|
help=('The zone to use. If specified, overrides the '
|
365
|
-
'"resources.zone" config. Passing "none" resets the config.')
|
380
|
+
'"resources.zone" config. Passing "none" resets the config.'),
|
381
|
+
hidden=True),
|
366
382
|
click.option(
|
367
383
|
'--num-nodes',
|
368
384
|
required=False,
|
@@ -1063,6 +1079,33 @@ def cli():
|
|
1063
1079
|
pass
|
1064
1080
|
|
1065
1081
|
|
1082
|
+
def _handle_infra_cloud_region_zone_options(infra: Optional[str],
|
1083
|
+
cloud: Optional[str],
|
1084
|
+
region: Optional[str],
|
1085
|
+
zone: Optional[str]):
|
1086
|
+
"""Handle the backward compatibility for --infra and --cloud/region/zone.
|
1087
|
+
|
1088
|
+
Returns:
|
1089
|
+
cloud, region, zone
|
1090
|
+
"""
|
1091
|
+
if cloud is not None or region is not None or zone is not None:
|
1092
|
+
click.secho(
|
1093
|
+
'The --cloud, --region, and --zone options are deprecated. '
|
1094
|
+
'Use --infra instead.',
|
1095
|
+
fg='yellow')
|
1096
|
+
if infra is not None:
|
1097
|
+
with ux_utils.print_exception_no_traceback():
|
1098
|
+
raise ValueError('Cannot specify both --infra and '
|
1099
|
+
'--cloud, --region, or --zone.')
|
1100
|
+
|
1101
|
+
if infra is not None:
|
1102
|
+
infra_info = infra_utils.InfraInfo.from_str(infra)
|
1103
|
+
cloud = infra_info.cloud
|
1104
|
+
region = infra_info.region
|
1105
|
+
zone = infra_info.zone
|
1106
|
+
return cloud, region, zone
|
1107
|
+
|
1108
|
+
|
1066
1109
|
@cli.command(cls=_DocumentedCodeCommand)
|
1067
1110
|
@config_option(expose_value=True)
|
1068
1111
|
@click.argument('entrypoint',
|
@@ -1172,6 +1215,7 @@ def launch(
|
|
1172
1215
|
backend_name: Optional[str],
|
1173
1216
|
name: Optional[str],
|
1174
1217
|
workdir: Optional[str],
|
1218
|
+
infra: Optional[str],
|
1175
1219
|
cloud: Optional[str],
|
1176
1220
|
region: Optional[str],
|
1177
1221
|
zone: Optional[str],
|
@@ -1219,6 +1263,9 @@ def launch(
|
|
1219
1263
|
if backend_name is None:
|
1220
1264
|
backend_name = backends.CloudVmRayBackend.NAME
|
1221
1265
|
|
1266
|
+
cloud, region, zone = _handle_infra_cloud_region_zone_options(
|
1267
|
+
infra, cloud, region, zone)
|
1268
|
+
|
1222
1269
|
task_or_dag = _make_task_or_dag_from_entrypoint_with_overrides(
|
1223
1270
|
entrypoint=entrypoint,
|
1224
1271
|
name=name,
|
@@ -1336,6 +1383,7 @@ def exec(cluster: Optional[str],
|
|
1336
1383
|
entrypoint: Tuple[str, ...],
|
1337
1384
|
detach_run: bool,
|
1338
1385
|
name: Optional[str],
|
1386
|
+
infra: Optional[str],
|
1339
1387
|
cloud: Optional[str],
|
1340
1388
|
region: Optional[str],
|
1341
1389
|
zone: Optional[str],
|
@@ -1427,6 +1475,9 @@ def exec(cluster: Optional[str],
|
|
1427
1475
|
controller_utils.check_cluster_name_not_controller(
|
1428
1476
|
cluster, operation_str='Executing task on it')
|
1429
1477
|
|
1478
|
+
cloud, region, zone = _handle_infra_cloud_region_zone_options(
|
1479
|
+
infra, cloud, region, zone)
|
1480
|
+
|
1430
1481
|
task_or_dag = _make_task_or_dag_from_entrypoint_with_overrides(
|
1431
1482
|
entrypoint=entrypoint,
|
1432
1483
|
name=name,
|
@@ -3265,7 +3316,7 @@ def _down_or_stop_clusters(
|
|
3265
3316
|
|
3266
3317
|
@cli.command(cls=_DocumentedCodeCommand)
|
3267
3318
|
@config_option(expose_value=False)
|
3268
|
-
@click.argument('
|
3319
|
+
@click.argument('infra_list', required=False, type=str, nargs=-1)
|
3269
3320
|
@click.option('--verbose',
|
3270
3321
|
'-v',
|
3271
3322
|
is_flag=True,
|
@@ -3273,7 +3324,7 @@ def _down_or_stop_clusters(
|
|
3273
3324
|
help='Show the activated account for each cloud.')
|
3274
3325
|
@usage_lib.entrypoint
|
3275
3326
|
# pylint: disable=redefined-outer-name
|
3276
|
-
def check(
|
3327
|
+
def check(infra_list: Tuple[str], verbose: bool):
|
3277
3328
|
"""Check which clouds are available to use.
|
3278
3329
|
|
3279
3330
|
This checks access credentials for all clouds supported by SkyPilot. If a
|
@@ -3295,8 +3346,8 @@ def check(clouds: Tuple[str], verbose: bool):
|
|
3295
3346
|
# Check only specific clouds - AWS and GCP.
|
3296
3347
|
sky check aws gcp
|
3297
3348
|
"""
|
3298
|
-
|
3299
|
-
request_id = sdk.check(
|
3349
|
+
infra_arg = infra_list if len(infra_list) > 0 else None
|
3350
|
+
request_id = sdk.check(infra_list=infra_arg, verbose=verbose)
|
3300
3351
|
sdk.stream_and_get(request_id)
|
3301
3352
|
api_server_url = server_common.get_server_url()
|
3302
3353
|
click.echo()
|
@@ -3312,10 +3363,15 @@ def check(clouds: Tuple[str], verbose: bool):
|
|
3312
3363
|
is_flag=True,
|
3313
3364
|
default=False,
|
3314
3365
|
help='Show details of all GPU/TPU/accelerator offerings.')
|
3366
|
+
@click.option('--infra',
|
3367
|
+
default=None,
|
3368
|
+
type=str,
|
3369
|
+
help='Infrastructure to query. Examples: "aws", "aws/us-east-1"')
|
3315
3370
|
@click.option('--cloud',
|
3316
3371
|
default=None,
|
3317
3372
|
type=str,
|
3318
|
-
help='Cloud provider to query.'
|
3373
|
+
help='Cloud provider to query.',
|
3374
|
+
hidden=True)
|
3319
3375
|
@click.option(
|
3320
3376
|
'--region',
|
3321
3377
|
required=False,
|
@@ -3323,6 +3379,7 @@ def check(clouds: Tuple[str], verbose: bool):
|
|
3323
3379
|
help=
|
3324
3380
|
('The region to use. If not specified, shows accelerators from all regions.'
|
3325
3381
|
),
|
3382
|
+
hidden=True,
|
3326
3383
|
)
|
3327
3384
|
@click.option(
|
3328
3385
|
'--all-regions',
|
@@ -3335,6 +3392,7 @@ def check(clouds: Tuple[str], verbose: bool):
|
|
3335
3392
|
def show_gpus(
|
3336
3393
|
accelerator_str: Optional[str],
|
3337
3394
|
all: bool, # pylint: disable=redefined-builtin
|
3395
|
+
infra: Optional[str],
|
3338
3396
|
cloud: Optional[str],
|
3339
3397
|
region: Optional[str],
|
3340
3398
|
all_regions: Optional[bool]):
|
@@ -3376,6 +3434,11 @@ def show_gpus(
|
|
3376
3434
|
* ``UTILIZATION`` (Kubernetes only): Total number of GPUs free / available
|
3377
3435
|
in the Kubernetes cluster.
|
3378
3436
|
"""
|
3437
|
+
cloud, region, _ = _handle_infra_cloud_region_zone_options(infra,
|
3438
|
+
cloud,
|
3439
|
+
region,
|
3440
|
+
zone=None)
|
3441
|
+
|
3379
3442
|
# validation for the --region flag
|
3380
3443
|
if region is not None and cloud is None:
|
3381
3444
|
raise click.UsageError(
|
@@ -3991,6 +4054,7 @@ def jobs_launch(
|
|
3991
4054
|
name: Optional[str],
|
3992
4055
|
cluster: Optional[str],
|
3993
4056
|
workdir: Optional[str],
|
4057
|
+
infra: Optional[str],
|
3994
4058
|
cloud: Optional[str],
|
3995
4059
|
region: Optional[str],
|
3996
4060
|
zone: Optional[str],
|
@@ -4032,6 +4096,8 @@ def jobs_launch(
|
|
4032
4096
|
'Use one of the flags as they are alias.')
|
4033
4097
|
name = cluster
|
4034
4098
|
env = _merge_env_vars(env_file, env)
|
4099
|
+
cloud, region, zone = _handle_infra_cloud_region_zone_options(
|
4100
|
+
infra, cloud, region, zone)
|
4035
4101
|
task_or_dag = _make_task_or_dag_from_entrypoint_with_overrides(
|
4036
4102
|
entrypoint,
|
4037
4103
|
name=name,
|
@@ -4509,6 +4575,7 @@ def serve_up(
|
|
4509
4575
|
service_yaml: Tuple[str, ...],
|
4510
4576
|
service_name: Optional[str],
|
4511
4577
|
workdir: Optional[str],
|
4578
|
+
infra: Optional[str],
|
4512
4579
|
cloud: Optional[str],
|
4513
4580
|
region: Optional[str],
|
4514
4581
|
zone: Optional[str],
|
@@ -4555,6 +4622,8 @@ def serve_up(
|
|
4555
4622
|
|
4556
4623
|
sky serve up service.yaml
|
4557
4624
|
"""
|
4625
|
+
cloud, region, zone = _handle_infra_cloud_region_zone_options(
|
4626
|
+
infra, cloud, region, zone)
|
4558
4627
|
if service_name is None:
|
4559
4628
|
service_name = serve_lib.generate_service_name()
|
4560
4629
|
|
@@ -4621,13 +4690,13 @@ def serve_up(
|
|
4621
4690
|
@timeline.event
|
4622
4691
|
@usage_lib.entrypoint
|
4623
4692
|
def serve_update(service_name: str, service_yaml: Tuple[str, ...],
|
4624
|
-
workdir: Optional[str],
|
4625
|
-
|
4626
|
-
|
4627
|
-
|
4628
|
-
|
4629
|
-
|
4630
|
-
cpus: Optional[str], memory: Optional[str],
|
4693
|
+
workdir: Optional[str], infra: Optional[str],
|
4694
|
+
cloud: Optional[str], region: Optional[str],
|
4695
|
+
zone: Optional[str], num_nodes: Optional[int],
|
4696
|
+
use_spot: Optional[bool], image_id: Optional[str],
|
4697
|
+
env_file: Optional[Dict[str, str]], env: List[Tuple[str, str]],
|
4698
|
+
gpus: Optional[str], instance_type: Optional[str],
|
4699
|
+
ports: Tuple[str], cpus: Optional[str], memory: Optional[str],
|
4631
4700
|
disk_size: Optional[int], disk_tier: Optional[str], mode: str,
|
4632
4701
|
yes: bool, async_call: bool):
|
4633
4702
|
"""Update a SkyServe service.
|
@@ -4659,6 +4728,8 @@ def serve_update(service_name: str, service_yaml: Tuple[str, ...],
|
|
4659
4728
|
sky serve update --mode blue_green sky-service-16aa new_service.yaml
|
4660
4729
|
|
4661
4730
|
"""
|
4731
|
+
cloud, region, zone = _handle_infra_cloud_region_zone_options(
|
4732
|
+
infra, cloud, region, zone)
|
4662
4733
|
task = _generate_task_with_service(
|
4663
4734
|
service_name=service_name,
|
4664
4735
|
service_yaml_args=service_yaml,
|
@@ -5173,6 +5244,7 @@ def benchmark_launch(
|
|
5173
5244
|
benchmark: str,
|
5174
5245
|
name: Optional[str],
|
5175
5246
|
workdir: Optional[str],
|
5247
|
+
infra: Optional[str],
|
5176
5248
|
cloud: Optional[str],
|
5177
5249
|
region: Optional[str],
|
5178
5250
|
zone: Optional[str],
|
@@ -5206,7 +5278,6 @@ def benchmark_launch(
|
|
5206
5278
|
raise click.BadParameter(f'Benchmark {benchmark} already exists. '
|
5207
5279
|
'To delete the previous benchmark result, '
|
5208
5280
|
f'run `sky bench delete {benchmark}`.')
|
5209
|
-
|
5210
5281
|
entrypoint = ' '.join(entrypoint)
|
5211
5282
|
if not entrypoint:
|
5212
5283
|
raise click.BadParameter('Please specify a task yaml to benchmark.')
|
@@ -5217,6 +5288,8 @@ def benchmark_launch(
|
|
5217
5288
|
'Sky Benchmark does not support command line tasks. '
|
5218
5289
|
'Please provide a YAML file.')
|
5219
5290
|
assert config is not None, (is_yaml, config)
|
5291
|
+
cloud, region, zone = _handle_infra_cloud_region_zone_options(
|
5292
|
+
infra, cloud, region, zone)
|
5220
5293
|
|
5221
5294
|
click.secho('Benchmarking a task from YAML: ', fg='cyan', nl=False)
|
5222
5295
|
click.secho(entrypoint, bold=True)
|
sky/client/sdk.py
CHANGED
@@ -10,14 +10,19 @@ Usage example:
|
|
10
10
|
statuses = sky.get(request_id)
|
11
11
|
|
12
12
|
"""
|
13
|
+
import base64
|
14
|
+
import binascii
|
13
15
|
import getpass
|
16
|
+
from http import cookiejar
|
14
17
|
import json
|
15
18
|
import logging
|
16
19
|
import os
|
17
20
|
import pathlib
|
18
21
|
import subprocess
|
22
|
+
import time
|
19
23
|
import typing
|
20
24
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
25
|
+
from urllib import parse as urlparse
|
21
26
|
import webbrowser
|
22
27
|
|
23
28
|
import click
|
@@ -42,6 +47,7 @@ from sky.utils import common
|
|
42
47
|
from sky.utils import common_utils
|
43
48
|
from sky.utils import dag_utils
|
44
49
|
from sky.utils import env_options
|
50
|
+
from sky.utils import infra_utils
|
45
51
|
from sky.utils import rich_utils
|
46
52
|
from sky.utils import status_lib
|
47
53
|
from sky.utils import subprocess_utils
|
@@ -87,12 +93,12 @@ def stream_response(request_id: Optional[str],
|
|
87
93
|
@usage_lib.entrypoint
|
88
94
|
@server_common.check_server_healthy_or_start
|
89
95
|
@annotations.client_api
|
90
|
-
def check(
|
96
|
+
def check(infra_list: Optional[Tuple[str, ...]],
|
91
97
|
verbose: bool) -> server_common.RequestId:
|
92
98
|
"""Checks the credentials to enable clouds.
|
93
99
|
|
94
100
|
Args:
|
95
|
-
|
101
|
+
infra: The infra to check.
|
96
102
|
verbose: Whether to show verbose output.
|
97
103
|
|
98
104
|
Returns:
|
@@ -101,6 +107,22 @@ def check(clouds: Optional[Tuple[str]],
|
|
101
107
|
Request Returns:
|
102
108
|
None
|
103
109
|
"""
|
110
|
+
if infra_list is None:
|
111
|
+
clouds = None
|
112
|
+
else:
|
113
|
+
specified_clouds = []
|
114
|
+
for infra_str in infra_list:
|
115
|
+
infra = infra_utils.InfraInfo.from_str(infra_str)
|
116
|
+
if infra.cloud is None:
|
117
|
+
with ux_utils.print_exception_no_traceback():
|
118
|
+
raise ValueError(f'Invalid infra to check: {infra_str}')
|
119
|
+
if infra.region is not None or infra.zone is not None:
|
120
|
+
region_zone = infra_str.partition('/')[-1]
|
121
|
+
logger.warning(f'Infra {infra_str} is specified, but `check` '
|
122
|
+
f'only supports checking {infra.cloud}, '
|
123
|
+
f'ignoring {region_zone}')
|
124
|
+
specified_clouds.append(infra.cloud)
|
125
|
+
clouds = tuple(specified_clouds)
|
104
126
|
body = payloads.CheckBody(clouds=clouds, verbose=verbose)
|
105
127
|
response = requests.post(f'{server_common.get_server_url()}/check',
|
106
128
|
json=json.loads(body.model_dump_json()),
|
@@ -344,7 +366,7 @@ def launch(
|
|
344
366
|
import sky
|
345
367
|
task = sky.Task(run='echo hello SkyPilot')
|
346
368
|
task.set_resources(
|
347
|
-
sky.Resources(
|
369
|
+
sky.Resources(infra='aws', accelerators='V100:4'))
|
348
370
|
sky.launch(task, cluster_name='my-cluster')
|
349
371
|
|
350
372
|
|
@@ -1824,7 +1846,73 @@ def api_login(endpoint: Optional[str] = None) -> None:
|
|
1824
1846
|
not endpoint.startswith('https://')):
|
1825
1847
|
raise click.BadParameter('Endpoint must be a valid URL.')
|
1826
1848
|
|
1827
|
-
server_common.check_server_healthy(endpoint)
|
1849
|
+
server_status = server_common.check_server_healthy(endpoint)
|
1850
|
+
if server_status == server_common.ApiServerStatus.NEEDS_AUTH:
|
1851
|
+
# We detected an auth proxy, so go through the auth proxy cookie flow.
|
1852
|
+
parsed_url = urlparse.urlparse(endpoint)
|
1853
|
+
token_url = f'{endpoint}/token'
|
1854
|
+
click.echo('Authentication is needed. Please visit this URL setup up '
|
1855
|
+
f'the token:{colorama.Style.BRIGHT}\n\n{token_url}'
|
1856
|
+
f'\n{colorama.Style.RESET_ALL}')
|
1857
|
+
if webbrowser.open(token_url):
|
1858
|
+
click.echo('Opening browser...')
|
1859
|
+
token: str = click.prompt('Paste the token')
|
1860
|
+
|
1861
|
+
# Parse the token.
|
1862
|
+
# b64decode will ignore invalid characters, but does some length and
|
1863
|
+
# padding checks.
|
1864
|
+
try:
|
1865
|
+
data = base64.b64decode(token)
|
1866
|
+
except binascii.Error as e:
|
1867
|
+
raise ValueError(f'Malformed token: {token}') from e
|
1868
|
+
logger.debug(f'Token data: {data!r}')
|
1869
|
+
try:
|
1870
|
+
cookie_dict = json.loads(data)
|
1871
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
1872
|
+
raise ValueError(f'Malformed token data: {data!r}') from e
|
1873
|
+
if not isinstance(cookie_dict, dict):
|
1874
|
+
raise ValueError(f'Malformed token JSON: {cookie_dict}')
|
1875
|
+
|
1876
|
+
cookie_jar = cookiejar.MozillaCookieJar()
|
1877
|
+
for (name, value) in cookie_dict.items():
|
1878
|
+
# dict keys in JSON must be strings
|
1879
|
+
assert isinstance(name, str)
|
1880
|
+
if not isinstance(value, str):
|
1881
|
+
raise ValueError('Malformed token - bad key/value: '
|
1882
|
+
f'{name}: {value}')
|
1883
|
+
|
1884
|
+
# See CookieJar._cookie_from_cookie_tuple
|
1885
|
+
# oauth2proxy default is Max-Age 604800
|
1886
|
+
expires = int(time.time()) + 604800
|
1887
|
+
domain = str(parsed_url.hostname)
|
1888
|
+
domain_initial_dot = domain.startswith('.')
|
1889
|
+
if not domain_initial_dot:
|
1890
|
+
domain = '.' + domain
|
1891
|
+
|
1892
|
+
cookie_jar.set_cookie(
|
1893
|
+
cookiejar.Cookie(
|
1894
|
+
version=0,
|
1895
|
+
name=name,
|
1896
|
+
value=value,
|
1897
|
+
port=None,
|
1898
|
+
port_specified=False,
|
1899
|
+
domain=domain,
|
1900
|
+
domain_specified=True,
|
1901
|
+
domain_initial_dot=domain_initial_dot,
|
1902
|
+
path='',
|
1903
|
+
path_specified=False,
|
1904
|
+
secure=False,
|
1905
|
+
expires=expires,
|
1906
|
+
discard=False,
|
1907
|
+
comment=None,
|
1908
|
+
comment_url=None,
|
1909
|
+
rest=dict(),
|
1910
|
+
))
|
1911
|
+
|
1912
|
+
# Now that the cookies are parsed, save them to the cookie jar.
|
1913
|
+
cookie_jar_path = os.path.expanduser(
|
1914
|
+
server_common.get_api_cookie_jar_path())
|
1915
|
+
cookie_jar.save(cookie_jar_path)
|
1828
1916
|
|
1829
1917
|
# Set the endpoint in the config file
|
1830
1918
|
config_path = pathlib.Path(
|
sky/clouds/__init__.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
from sky.clouds.cloud import Cloud
|
4
4
|
from sky.clouds.cloud import cloud_in_iterable
|
5
5
|
from sky.clouds.cloud import CloudImplementationFeatures
|
6
|
+
from sky.clouds.cloud import DummyCloud
|
6
7
|
from sky.clouds.cloud import OpenPortsVersion
|
7
8
|
from sky.clouds.cloud import ProvisionerVersion
|
8
9
|
from sky.clouds.cloud import Region
|
@@ -34,6 +35,7 @@ __all__ = [
|
|
34
35
|
'Azure',
|
35
36
|
'Cloud',
|
36
37
|
'Cudo',
|
38
|
+
'DummyCloud',
|
37
39
|
'GCP',
|
38
40
|
'Lambda',
|
39
41
|
'DO',
|
sky/clouds/cloud.py
CHANGED
@@ -888,6 +888,12 @@ class Cloud:
|
|
888
888
|
return state
|
889
889
|
|
890
890
|
|
891
|
+
class DummyCloud(Cloud):
|
892
|
+
"""A dummy Cloud that has zero egress cost from/to for optimization
|
893
|
+
purpose."""
|
894
|
+
pass
|
895
|
+
|
896
|
+
|
891
897
|
# === Helper functions ===
|
892
898
|
def cloud_in_iterable(cloud: Cloud, cloud_list: Iterable[Cloud]) -> bool:
|
893
899
|
"""Returns whether the cloud is in the given cloud list."""
|