skypilot-nightly 1.0.0.dev20250912__py3-none-any.whl → 1.0.0.dev20250913__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 +4 -2
- sky/adaptors/seeweb.py +103 -0
- sky/authentication.py +38 -0
- sky/backends/backend_utils.py +24 -9
- sky/backends/cloud_vm_ray_backend.py +382 -151
- sky/catalog/data_fetchers/fetch_aws.py +0 -36
- sky/catalog/data_fetchers/fetch_seeweb.py +329 -0
- sky/catalog/seeweb_catalog.py +184 -0
- sky/clouds/__init__.py +2 -0
- sky/clouds/kubernetes.py +2 -0
- sky/clouds/seeweb.py +463 -0
- sky/core.py +46 -12
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/{DAiq7V2xJnO1LSfmunZl6 → Y0Q7LyrxiFoWWbTdwb5nh}/_buildManifest.js +1 -1
- sky/dashboard/out/_next/static/chunks/1141-159df2d4c441a9d1.js +1 -0
- sky/dashboard/out/_next/static/chunks/3015-2ea98b57e318bd6e.js +1 -0
- sky/dashboard/out/_next/static/chunks/3294.03e02ae73455f48e.js +6 -0
- sky/dashboard/out/_next/static/chunks/3785.0fa442e16dd3f00e.js +1 -0
- sky/dashboard/out/_next/static/chunks/5339.c033b29835da0f35.js +51 -0
- sky/dashboard/out/_next/static/chunks/6856-e0754534b3015377.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-11c8e9b982e8ffec.js +1 -0
- sky/dashboard/out/_next/static/chunks/9037-f9800e64eb05dd1c.js +6 -0
- sky/dashboard/out/_next/static/chunks/{webpack-e8a0c4c3c6f408fb.js → webpack-d1e29b3aa66bf4cf.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/exceptions.py +5 -0
- sky/global_user_state.py +41 -26
- sky/jobs/utils.py +61 -13
- sky/provision/__init__.py +1 -0
- sky/provision/kubernetes/utils.py +14 -3
- sky/provision/seeweb/__init__.py +11 -0
- sky/provision/seeweb/config.py +13 -0
- sky/provision/seeweb/instance.py +806 -0
- sky/schemas/generated/jobsv1_pb2.py +86 -0
- sky/schemas/generated/jobsv1_pb2.pyi +252 -0
- sky/schemas/generated/jobsv1_pb2_grpc.py +542 -0
- sky/setup_files/dependencies.py +8 -1
- sky/skylet/constants.py +2 -1
- sky/skylet/job_lib.py +128 -10
- sky/skylet/log_lib.py +3 -3
- sky/skylet/services.py +203 -0
- sky/skylet/skylet.py +4 -0
- sky/templates/seeweb-ray.yml.j2 +108 -0
- sky/utils/controller_utils.py +11 -5
- {skypilot_nightly-1.0.0.dev20250912.dist-info → skypilot_nightly-1.0.0.dev20250913.dist-info}/METADATA +39 -34
- {skypilot_nightly-1.0.0.dev20250912.dist-info → skypilot_nightly-1.0.0.dev20250913.dist-info}/RECORD +64 -53
- sky/dashboard/out/_next/static/chunks/1141-943efc7aff0f0c06.js +0 -1
- sky/dashboard/out/_next/static/chunks/3015-86cabed5d4669ad0.js +0 -1
- sky/dashboard/out/_next/static/chunks/3294.ba6586f9755b0edb.js +0 -6
- sky/dashboard/out/_next/static/chunks/3785.4872a2f3aa489880.js +0 -1
- sky/dashboard/out/_next/static/chunks/5339.3fda4a4010ff4e06.js +0 -51
- sky/dashboard/out/_next/static/chunks/6856-6e2bc8a6fd0867af.js +0 -1
- sky/dashboard/out/_next/static/chunks/6990-08b2a1cae076a943.js +0 -1
- sky/dashboard/out/_next/static/chunks/9037-fa1737818d0a0969.js +0 -6
- /sky/dashboard/out/_next/static/{DAiq7V2xJnO1LSfmunZl6 → Y0Q7LyrxiFoWWbTdwb5nh}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250912.dist-info → skypilot_nightly-1.0.0.dev20250913.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250912.dist-info → skypilot_nightly-1.0.0.dev20250913.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250912.dist-info → skypilot_nightly-1.0.0.dev20250913.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250912.dist-info → skypilot_nightly-1.0.0.dev20250913.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 = 'bf9b3c4e09e97cf2dafed9f351d0b36438adf4ec'
|
|
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.dev20250913'
|
|
41
41
|
__root_dir__ = directory_utils.get_sky_dir()
|
|
42
42
|
|
|
43
43
|
|
|
@@ -149,6 +149,7 @@ Vsphere = clouds.Vsphere
|
|
|
149
149
|
Fluidstack = clouds.Fluidstack
|
|
150
150
|
Nebius = clouds.Nebius
|
|
151
151
|
Hyperbolic = clouds.Hyperbolic
|
|
152
|
+
Seeweb = clouds.Seeweb
|
|
152
153
|
|
|
153
154
|
__all__ = [
|
|
154
155
|
'__version__',
|
|
@@ -169,6 +170,7 @@ __all__ = [
|
|
|
169
170
|
'Fluidstack',
|
|
170
171
|
'Nebius',
|
|
171
172
|
'Hyperbolic',
|
|
173
|
+
'Seeweb',
|
|
172
174
|
'Optimizer',
|
|
173
175
|
'OptimizeTarget',
|
|
174
176
|
'backends',
|
sky/adaptors/seeweb.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
""" Seeweb Adaptor """
|
|
2
|
+
import configparser
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from sky.adaptors import common
|
|
6
|
+
from sky.utils import annotations
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SeewebError(Exception):
|
|
10
|
+
"""Base exception for Seeweb adaptor errors."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SeewebCredentialsFileNotFound(SeewebError):
|
|
14
|
+
"""Raised when the Seeweb credentials file is missing."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SeewebApiKeyMissing(SeewebError):
|
|
18
|
+
"""Raised when the Seeweb API key is missing or empty."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SeewebAuthenticationError(SeewebError):
|
|
22
|
+
"""Raised when authenticating with Seeweb API fails."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_IMPORT_ERROR_MESSAGE = ('Failed to import dependencies for Seeweb.'
|
|
26
|
+
'Try pip install "skypilot[seeweb]"')
|
|
27
|
+
|
|
28
|
+
ecsapi = common.LazyImport(
|
|
29
|
+
'ecsapi',
|
|
30
|
+
import_error_message=_IMPORT_ERROR_MESSAGE,
|
|
31
|
+
)
|
|
32
|
+
boto3 = common.LazyImport('boto3', import_error_message=_IMPORT_ERROR_MESSAGE)
|
|
33
|
+
botocore = common.LazyImport('botocore',
|
|
34
|
+
import_error_message=_IMPORT_ERROR_MESSAGE)
|
|
35
|
+
|
|
36
|
+
_LAZY_MODULES = (ecsapi, boto3, botocore)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@common.load_lazy_modules(_LAZY_MODULES)
|
|
40
|
+
def check_compute_credentials() -> bool:
|
|
41
|
+
"""Checks if the user has access credentials to Seeweb's compute service.
|
|
42
|
+
|
|
43
|
+
Returns True if credentials are valid; otherwise raises a SeewebError.
|
|
44
|
+
"""
|
|
45
|
+
# Read API key from standard Seeweb configuration file
|
|
46
|
+
key_path = Path('~/.seeweb_cloud/seeweb_keys').expanduser()
|
|
47
|
+
if not key_path.exists():
|
|
48
|
+
raise SeewebCredentialsFileNotFound(
|
|
49
|
+
'Missing Seeweb API key file ~/.seeweb_cloud/seeweb_keys')
|
|
50
|
+
|
|
51
|
+
parser = configparser.ConfigParser()
|
|
52
|
+
parser.read(key_path)
|
|
53
|
+
try:
|
|
54
|
+
api_key = parser['DEFAULT']['api_key'].strip()
|
|
55
|
+
except KeyError as e:
|
|
56
|
+
raise SeewebApiKeyMissing(
|
|
57
|
+
'Missing api_key in ~/.seeweb_cloud/seeweb_keys') from e
|
|
58
|
+
if not api_key:
|
|
59
|
+
raise SeewebApiKeyMissing(
|
|
60
|
+
'Empty api_key in ~/.seeweb_cloud/seeweb_keys')
|
|
61
|
+
|
|
62
|
+
# Test connection by fetching servers list to validate the key
|
|
63
|
+
try:
|
|
64
|
+
seeweb_client = ecsapi.Api(token=api_key)
|
|
65
|
+
seeweb_client.fetch_servers()
|
|
66
|
+
except Exception as e: # pylint: disable=broad-except
|
|
67
|
+
raise SeewebAuthenticationError(
|
|
68
|
+
f'Unable to authenticate with Seeweb API: {e}') from e
|
|
69
|
+
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@common.load_lazy_modules(_LAZY_MODULES)
|
|
74
|
+
def check_storage_credentials() -> bool:
|
|
75
|
+
"""Checks if the user has access credentials to Seeweb's storage service.
|
|
76
|
+
|
|
77
|
+
Mirrors compute credentials validation.
|
|
78
|
+
"""
|
|
79
|
+
return check_compute_credentials()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@common.load_lazy_modules(_LAZY_MODULES)
|
|
83
|
+
@annotations.lru_cache(scope='global', maxsize=1)
|
|
84
|
+
def client():
|
|
85
|
+
"""Returns an authenticated ecsapi.Api object."""
|
|
86
|
+
# Create authenticated client using the same credential pattern
|
|
87
|
+
key_path = Path('~/.seeweb_cloud/seeweb_keys').expanduser()
|
|
88
|
+
if not key_path.exists():
|
|
89
|
+
raise SeewebCredentialsFileNotFound(
|
|
90
|
+
'Missing Seeweb API key file ~/.seeweb_cloud/seeweb_keys')
|
|
91
|
+
|
|
92
|
+
parser = configparser.ConfigParser()
|
|
93
|
+
parser.read(key_path)
|
|
94
|
+
try:
|
|
95
|
+
api_key = parser['DEFAULT']['api_key'].strip()
|
|
96
|
+
except KeyError as e:
|
|
97
|
+
raise SeewebApiKeyMissing(
|
|
98
|
+
'Missing api_key in ~/.seeweb_cloud/seeweb_keys') from e
|
|
99
|
+
if not api_key:
|
|
100
|
+
raise SeewebApiKeyMissing(
|
|
101
|
+
'Empty api_key in ~/.seeweb_cloud/seeweb_keys')
|
|
102
|
+
|
|
103
|
+
return ecsapi.Api(token=api_key)
|
sky/authentication.py
CHANGED
|
@@ -40,6 +40,7 @@ from sky.adaptors import gcp
|
|
|
40
40
|
from sky.adaptors import ibm
|
|
41
41
|
from sky.adaptors import kubernetes
|
|
42
42
|
from sky.adaptors import runpod
|
|
43
|
+
from sky.adaptors import seeweb as seeweb_adaptor
|
|
43
44
|
from sky.adaptors import vast
|
|
44
45
|
from sky.provision.fluidstack import fluidstack_utils
|
|
45
46
|
from sky.provision.kubernetes import utils as kubernetes_utils
|
|
@@ -601,3 +602,40 @@ def setup_hyperbolic_authentication(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
601
602
|
config['auth']['ssh_public_key'] = public_key_path
|
|
602
603
|
|
|
603
604
|
return configure_ssh_info(config)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def setup_seeweb_authentication(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
608
|
+
"""Registers the public key with Seeweb and notes the remote name."""
|
|
609
|
+
# 1. local key pair
|
|
610
|
+
get_or_generate_keys()
|
|
611
|
+
|
|
612
|
+
# 2. public key
|
|
613
|
+
_, public_key_path = get_or_generate_keys()
|
|
614
|
+
with open(public_key_path, 'r', encoding='utf-8') as f:
|
|
615
|
+
public_key = f.read().strip()
|
|
616
|
+
|
|
617
|
+
# 3. Seeweb API client
|
|
618
|
+
client = seeweb_adaptor.client()
|
|
619
|
+
|
|
620
|
+
# 4. Check if key is already registered
|
|
621
|
+
prefix = f'sky-key-{common_utils.get_user_hash()}'
|
|
622
|
+
remote_name = None
|
|
623
|
+
for k in client.fetch_ssh_keys():
|
|
624
|
+
if k.key.strip() == public_key:
|
|
625
|
+
remote_name = k.label # already present
|
|
626
|
+
break
|
|
627
|
+
|
|
628
|
+
# 5. doesn't exist, choose a unique name and create it
|
|
629
|
+
if remote_name is None:
|
|
630
|
+
suffix = 1
|
|
631
|
+
remote_name = prefix
|
|
632
|
+
existing_names = {k.label for k in client.fetch_ssh_keys()}
|
|
633
|
+
while remote_name in existing_names:
|
|
634
|
+
suffix += 1
|
|
635
|
+
remote_name = f'{prefix}-{suffix}'
|
|
636
|
+
client.create_ssh_key(label=remote_name, key=public_key)
|
|
637
|
+
|
|
638
|
+
# 6. Put the remote name in cluster-config (like for Lambda)
|
|
639
|
+
config['auth']['remote_key_name'] = remote_name
|
|
640
|
+
|
|
641
|
+
return config
|
sky/backends/backend_utils.py
CHANGED
|
@@ -1116,6 +1116,8 @@ def _add_auth_to_cluster_config(cloud: clouds.Cloud, tmp_yaml_path: str):
|
|
|
1116
1116
|
config = auth.setup_fluidstack_authentication(config)
|
|
1117
1117
|
elif isinstance(cloud, clouds.Hyperbolic):
|
|
1118
1118
|
config = auth.setup_hyperbolic_authentication(config)
|
|
1119
|
+
elif isinstance(cloud, clouds.Seeweb):
|
|
1120
|
+
config = auth.setup_seeweb_authentication(config)
|
|
1119
1121
|
else:
|
|
1120
1122
|
assert False, cloud
|
|
1121
1123
|
yaml_utils.dump_yaml(tmp_yaml_path, config)
|
|
@@ -2333,7 +2335,8 @@ def _update_cluster_status(cluster_name: str) -> Optional[Dict[str, Any]]:
|
|
|
2333
2335
|
handle,
|
|
2334
2336
|
requested_resources=None,
|
|
2335
2337
|
ready=True,
|
|
2336
|
-
is_launch=False
|
|
2338
|
+
is_launch=False,
|
|
2339
|
+
update_only=True)
|
|
2337
2340
|
return global_user_state.get_cluster_from_name(cluster_name)
|
|
2338
2341
|
|
|
2339
2342
|
# All cases below are transitioning the cluster to non-UP states.
|
|
@@ -2543,7 +2546,8 @@ def _update_cluster_status(cluster_name: str) -> Optional[Dict[str, Any]]:
|
|
|
2543
2546
|
handle,
|
|
2544
2547
|
requested_resources=None,
|
|
2545
2548
|
ready=False,
|
|
2546
|
-
is_launch=False
|
|
2549
|
+
is_launch=False,
|
|
2550
|
+
update_only=True)
|
|
2547
2551
|
return global_user_state.get_cluster_from_name(cluster_name)
|
|
2548
2552
|
# Now is_abnormal is False: either node_statuses is empty or all nodes are
|
|
2549
2553
|
# STOPPED.
|
|
@@ -3719,13 +3723,24 @@ def invoke_skylet_with_retries(func: Callable[..., T]) -> T:
|
|
|
3719
3723
|
return func()
|
|
3720
3724
|
except grpc.RpcError as e:
|
|
3721
3725
|
last_exception = e
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
raise exceptions.SkyletInternalError(e.details())
|
|
3725
|
-
elif e.code() == grpc.StatusCode.UNAVAILABLE:
|
|
3726
|
-
time.sleep(backoff.current_backoff())
|
|
3727
|
-
else:
|
|
3728
|
-
raise e
|
|
3726
|
+
_handle_grpc_error(e, backoff.current_backoff())
|
|
3727
|
+
|
|
3729
3728
|
raise RuntimeError(
|
|
3730
3729
|
f'Failed to invoke Skylet after {max_attempts} attempts: {last_exception}'
|
|
3731
3730
|
) from last_exception
|
|
3731
|
+
|
|
3732
|
+
|
|
3733
|
+
def _handle_grpc_error(e: 'grpc.RpcError', current_backoff: float) -> None:
|
|
3734
|
+
if e.code() == grpc.StatusCode.INTERNAL:
|
|
3735
|
+
with ux_utils.print_exception_no_traceback():
|
|
3736
|
+
raise exceptions.SkyletInternalError(e.details())
|
|
3737
|
+
elif e.code() == grpc.StatusCode.UNAVAILABLE:
|
|
3738
|
+
time.sleep(current_backoff)
|
|
3739
|
+
elif e.code() == grpc.StatusCode.UNIMPLEMENTED:
|
|
3740
|
+
# Handle backwards compatibility: old server doesn't implement this RPC.
|
|
3741
|
+
# Let the caller fall back to legacy execution.
|
|
3742
|
+
raise exceptions.SkyletMethodNotImplementedError(
|
|
3743
|
+
f'gRPC method not implemented on server, falling back to legacy execution: {e.details()}'
|
|
3744
|
+
)
|
|
3745
|
+
else:
|
|
3746
|
+
raise e
|