skypilot-nightly 1.0.0.dev20250826__py3-none-any.whl → 1.0.0.dev20250827__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.

Files changed (60) hide show
  1. sky/__init__.py +2 -2
  2. sky/authentication.py +3 -9
  3. sky/backends/backend_utils.py +30 -43
  4. sky/backends/cloud_vm_ray_backend.py +2 -2
  5. sky/client/cli/command.py +2 -1
  6. sky/client/common.py +41 -14
  7. sky/client/sdk.py +1 -1
  8. sky/clouds/aws.py +1 -1
  9. sky/clouds/cloud.py +15 -0
  10. sky/clouds/kubernetes.py +27 -0
  11. sky/clouds/ssh.py +2 -3
  12. sky/dashboard/out/404.html +1 -1
  13. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  14. sky/dashboard/out/clusters/[cluster].html +1 -1
  15. sky/dashboard/out/clusters.html +1 -1
  16. sky/dashboard/out/config.html +1 -1
  17. sky/dashboard/out/index.html +1 -1
  18. sky/dashboard/out/infra/[context].html +1 -1
  19. sky/dashboard/out/infra.html +1 -1
  20. sky/dashboard/out/jobs/[job].html +1 -1
  21. sky/dashboard/out/jobs/pools/[pool].html +1 -1
  22. sky/dashboard/out/jobs.html +1 -1
  23. sky/dashboard/out/users.html +1 -1
  24. sky/dashboard/out/volumes.html +1 -1
  25. sky/dashboard/out/workspace/new.html +1 -1
  26. sky/dashboard/out/workspaces/[name].html +1 -1
  27. sky/dashboard/out/workspaces.html +1 -1
  28. sky/global_user_state.py +103 -11
  29. sky/provision/kubernetes/config.py +2 -8
  30. sky/provision/kubernetes/instance.py +6 -0
  31. sky/provision/kubernetes/network_utils.py +3 -4
  32. sky/provision/kubernetes/utils.py +6 -5
  33. sky/provision/nebius/utils.py +15 -7
  34. sky/provision/vsphere/vsphere_utils.py +2 -8
  35. sky/schemas/api/responses.py +7 -0
  36. sky/serve/serve_utils.py +2 -2
  37. sky/serve/service_spec.py +2 -8
  38. sky/server/auth/authn.py +4 -0
  39. sky/server/common.py +7 -1
  40. sky/server/requests/executor.py +4 -0
  41. sky/server/server.py +18 -33
  42. sky/server/uvicorn.py +33 -0
  43. sky/setup_files/dependencies.py +1 -0
  44. sky/sky_logging.py +4 -1
  45. sky/skylet/events.py +2 -2
  46. sky/skypilot_config.py +4 -2
  47. sky/ssh_node_pools/core.py +3 -1
  48. sky/task.py +3 -9
  49. sky/users/server.py +6 -6
  50. sky/utils/common_utils.py +3 -2
  51. sky/utils/yaml_utils.py +35 -0
  52. sky/volumes/volume.py +8 -3
  53. {skypilot_nightly-1.0.0.dev20250826.dist-info → skypilot_nightly-1.0.0.dev20250827.dist-info}/METADATA +2 -1
  54. {skypilot_nightly-1.0.0.dev20250826.dist-info → skypilot_nightly-1.0.0.dev20250827.dist-info}/RECORD +60 -59
  55. /sky/dashboard/out/_next/static/{TPMkEeuj85tHTmIW7Gu3S → -eL7Ky3bxVivzeLHNB9U6}/_buildManifest.js +0 -0
  56. /sky/dashboard/out/_next/static/{TPMkEeuj85tHTmIW7Gu3S → -eL7Ky3bxVivzeLHNB9U6}/_ssgManifest.js +0 -0
  57. {skypilot_nightly-1.0.0.dev20250826.dist-info → skypilot_nightly-1.0.0.dev20250827.dist-info}/WHEEL +0 -0
  58. {skypilot_nightly-1.0.0.dev20250826.dist-info → skypilot_nightly-1.0.0.dev20250827.dist-info}/entry_points.txt +0 -0
  59. {skypilot_nightly-1.0.0.dev20250826.dist-info → skypilot_nightly-1.0.0.dev20250827.dist-info}/licenses/LICENSE +0 -0
  60. {skypilot_nightly-1.0.0.dev20250826.dist-info → skypilot_nightly-1.0.0.dev20250827.dist-info}/top_level.txt +0 -0
sky/server/server.py CHANGED
@@ -7,7 +7,6 @@ import contextlib
7
7
  import datetime
8
8
  import hashlib
9
9
  import json
10
- import logging
11
10
  import multiprocessing
12
11
  import os
13
12
  import pathlib
@@ -24,7 +23,6 @@ import zipfile
24
23
  import aiofiles
25
24
  import fastapi
26
25
  from fastapi.middleware import cors
27
- from passlib.hash import apr_md5_crypt
28
26
  import starlette.middleware.base
29
27
  import uvloop
30
28
 
@@ -69,7 +67,6 @@ from sky.utils import common_utils
69
67
  from sky.utils import context
70
68
  from sky.utils import context_utils
71
69
  from sky.utils import dag_utils
72
- from sky.utils import env_options
73
70
  from sky.utils import status_lib
74
71
  from sky.utils import subprocess_utils
75
72
  from sky.volumes.server import server as volumes_rest
@@ -85,31 +82,6 @@ P = ParamSpec('P')
85
82
 
86
83
  _SERVER_USER_HASH_KEY = 'server_user_hash'
87
84
 
88
-
89
- def _add_timestamp_prefix_for_server_logs() -> None:
90
- server_logger = sky_logging.init_logger('sky.server')
91
- # Clear existing handlers first to prevent duplicates
92
- server_logger.handlers.clear()
93
- # Disable propagation to avoid the root logger of SkyPilot being affected
94
- server_logger.propagate = False
95
- # Add date prefix to the log message printed by loggers under
96
- # server.
97
- stream_handler = logging.StreamHandler(sys.stdout)
98
- if env_options.Options.SHOW_DEBUG_INFO.get():
99
- stream_handler.setLevel(logging.DEBUG)
100
- else:
101
- stream_handler.setLevel(logging.INFO)
102
- stream_handler.flush = sys.stdout.flush # type: ignore
103
- stream_handler.setFormatter(sky_logging.FORMATTER)
104
- server_logger.addHandler(stream_handler)
105
- # Add date prefix to the log message printed by uvicorn.
106
- for name in ['uvicorn', 'uvicorn.access']:
107
- uvicorn_logger = logging.getLogger(name)
108
- uvicorn_logger.handlers.clear()
109
- uvicorn_logger.addHandler(stream_handler)
110
-
111
-
112
- _add_timestamp_prefix_for_server_logs()
113
85
  logger = sky_logging.init_logger(__name__)
114
86
 
115
87
  # TODO(zhwu): Streaming requests, such log tailing after sky launch or sky logs,
@@ -148,7 +120,7 @@ def _try_set_basic_auth_user(request: fastapi.Request):
148
120
  username_encoded = username.encode('utf8')
149
121
  db_username_encoded = user.name.encode('utf8')
150
122
  if (username_encoded == db_username_encoded and
151
- apr_md5_crypt.verify(password, user.password)):
123
+ common.crypt_ctx.verify(password, user.password)):
152
124
  request.state.auth_user = user
153
125
  break
154
126
 
@@ -248,7 +220,7 @@ class BasicAuthMiddleware(starlette.middleware.base.BaseHTTPMiddleware):
248
220
  username_encoded = username.encode('utf8')
249
221
  db_username_encoded = user.name.encode('utf8')
250
222
  if (username_encoded == db_username_encoded and
251
- apr_md5_crypt.verify(password, user.password)):
223
+ common.crypt_ctx.verify(password, user.password)):
252
224
  valid_user = True
253
225
  request.state.auth_user = user
254
226
  await authn.override_user_info_in_request_body(request, user)
@@ -852,6 +824,15 @@ async def upload_zip_file(request: fastapi.Request, user_hash: str,
852
824
  chunk_index: The chunk index, starting from 0.
853
825
  total_chunks: The total number of chunks.
854
826
  """
827
+ # Field _body would be set if the request body has been received, fail fast
828
+ # to surface potential memory issues, i.e. catch the issue in our smoke
829
+ # test.
830
+ # pylint: disable=protected-access
831
+ if hasattr(request, '_body'):
832
+ raise fastapi.HTTPException(
833
+ status_code=500,
834
+ detail='Upload request body should not be received before streaming'
835
+ )
855
836
  # Add the upload id to the cleanup list.
856
837
  upload_ids_to_cleanup[(upload_id,
857
838
  user_hash)] = (datetime.datetime.now() +
@@ -919,8 +900,9 @@ async def upload_zip_file(request: fastapi.Request, user_hash: str,
919
900
  zip_file_path.rename(zip_file_path.with_suffix(''))
920
901
  missing_chunks = get_missing_chunks(total_chunks)
921
902
  if missing_chunks:
922
- return payloads.UploadZipFileResponse(status='uploading',
923
- missing_chunks=missing_chunks)
903
+ return payloads.UploadZipFileResponse(
904
+ status=responses.UploadStatus.UPLOADING.value,
905
+ missing_chunks=missing_chunks)
924
906
  zip_file_path = client_file_mounts_dir / f'{upload_id}.zip'
925
907
  async with aiofiles.open(zip_file_path, 'wb') as zip_file:
926
908
  for chunk in range(total_chunks):
@@ -937,7 +919,8 @@ async def upload_zip_file(request: fastapi.Request, user_hash: str,
937
919
  unzip_file(zip_file_path, client_file_mounts_dir)
938
920
  if total_chunks > 1:
939
921
  shutil.rmtree(chunk_dir)
940
- return payloads.UploadZipFileResponse(status='completed')
922
+ return payloads.UploadZipFileResponse(
923
+ status=responses.UploadStatus.COMPLETED.value)
941
924
 
942
925
 
943
926
  def _is_relative_to(path: pathlib.Path, parent: pathlib.Path) -> bool:
@@ -1859,6 +1842,8 @@ if __name__ == '__main__':
1859
1842
 
1860
1843
  from sky.server import uvicorn as skyuvicorn
1861
1844
 
1845
+ skyuvicorn.add_timestamp_prefix_for_server_logs()
1846
+
1862
1847
  # Initialize global user state db
1863
1848
  global_user_state.initialize_and_get_db()
1864
1849
  # Initialize request db
sky/server/uvicorn.py CHANGED
@@ -4,8 +4,10 @@ This module is a wrapper around uvicorn to customize the behavior of the
4
4
  server.
5
5
  """
6
6
  import asyncio
7
+ import logging
7
8
  import os
8
9
  import signal
10
+ import sys
9
11
  import threading
10
12
  import time
11
13
  from types import FrameType
@@ -21,6 +23,7 @@ from sky.server import state
21
23
  from sky.server.requests import requests as requests_lib
22
24
  from sky.skylet import constants
23
25
  from sky.utils import context_utils
26
+ from sky.utils import env_options
24
27
  from sky.utils import subprocess_utils
25
28
 
26
29
  logger = sky_logging.init_logger(__name__)
@@ -47,6 +50,35 @@ _RETRIABLE_REQUEST_NAMES = [
47
50
  ]
48
51
 
49
52
 
53
+ def add_timestamp_prefix_for_server_logs() -> None:
54
+ """Configure logging for API server.
55
+
56
+ Note: we only do this in the main API server process and uvicorn processes,
57
+ to avoid affecting executor logs (including in modules like
58
+ sky.server.requests) that may get sent to the client.
59
+ """
60
+ server_logger = sky_logging.init_logger('sky.server')
61
+ # Clear existing handlers first to prevent duplicates
62
+ server_logger.handlers.clear()
63
+ # Disable propagation to avoid the root logger of SkyPilot being affected
64
+ server_logger.propagate = False
65
+ # Add date prefix to the log message printed by loggers under
66
+ # server.
67
+ stream_handler = logging.StreamHandler(sys.stdout)
68
+ if env_options.Options.SHOW_DEBUG_INFO.get():
69
+ stream_handler.setLevel(logging.DEBUG)
70
+ else:
71
+ stream_handler.setLevel(logging.INFO)
72
+ stream_handler.flush = sys.stdout.flush # type: ignore
73
+ stream_handler.setFormatter(sky_logging.FORMATTER)
74
+ server_logger.addHandler(stream_handler)
75
+ # Add date prefix to the log message printed by uvicorn.
76
+ for name in ['uvicorn', 'uvicorn.access']:
77
+ uvicorn_logger = logging.getLogger(name)
78
+ uvicorn_logger.handlers.clear()
79
+ uvicorn_logger.addHandler(stream_handler)
80
+
81
+
50
82
  class Server(uvicorn.Server):
51
83
  """Server wrapper for uvicorn.
52
84
 
@@ -162,6 +194,7 @@ class Server(uvicorn.Server):
162
194
 
163
195
  def run(self, *args, **kwargs):
164
196
  """Run the server process."""
197
+ add_timestamp_prefix_for_server_logs()
165
198
  context_utils.hijack_sys_attrs()
166
199
  # Use default loop policy of uvicorn (use uvloop if available).
167
200
  self.config.setup_event_loop()
@@ -65,6 +65,7 @@ install_requires = [
65
65
  # Required for API server metrics
66
66
  'prometheus_client>=0.8.0',
67
67
  'passlib',
68
+ 'bcrypt',
68
69
  'pyjwt',
69
70
  'gitpython',
70
71
  'types-paramiko',
sky/sky_logging.py CHANGED
@@ -202,7 +202,10 @@ def set_sky_logging_levels(level: int):
202
202
 
203
203
 
204
204
  def logging_enabled(logger: logging.Logger, level: int) -> bool:
205
- return logger.level <= level
205
+ # Note(cooperc): This may return true in a lot of cases where we won't
206
+ # actually log anything, since the log level is set on the handler in
207
+ # _setup_logger.
208
+ return logger.getEffectiveLevel() <= level
206
209
 
207
210
 
208
211
  @contextlib.contextmanager
sky/skylet/events.py CHANGED
@@ -7,7 +7,6 @@ import time
7
7
  import traceback
8
8
 
9
9
  import psutil
10
- import yaml
11
10
 
12
11
  from sky import clouds
13
12
  from sky import sky_logging
@@ -24,6 +23,7 @@ from sky.utils import cluster_utils
24
23
  from sky.utils import common_utils
25
24
  from sky.utils import registry
26
25
  from sky.utils import ux_utils
26
+ from sky.utils import yaml_utils
27
27
 
28
28
  # Seconds of sleep between the processing of skylet events.
29
29
  EVENT_CHECKING_INTERVAL_SECONDS = 20
@@ -309,7 +309,7 @@ class AutostopEvent(SkyletEvent):
309
309
  else:
310
310
  yaml_str = self._CATCH_NODES.sub(r'cache_stopped_nodes: true',
311
311
  yaml_str)
312
- config = yaml.safe_load(yaml_str)
312
+ config = yaml_utils.safe_load(yaml_str)
313
313
  # Set the private key with the existed key on the remote instance.
314
314
  config['auth']['ssh_private_key'] = '~/ray_bootstrap_key.pem'
315
315
  # NOTE: We must do this, otherwise with ssh_proxy_command still under
sky/skypilot_config.py CHANGED
@@ -75,6 +75,7 @@ from sky.utils import config_utils
75
75
  from sky.utils import context
76
76
  from sky.utils import schemas
77
77
  from sky.utils import ux_utils
78
+ from sky.utils import yaml_utils
78
79
  from sky.utils.db import db_utils
79
80
  from sky.utils.kubernetes import config_map_utils
80
81
 
@@ -532,7 +533,7 @@ def _parse_dotlist(dotlist: List[str]) -> config_utils.Config:
532
533
  if len(key) == 0 or len(value) == 0:
533
534
  raise ValueError(f'Invalid config override: {arg}. '
534
535
  'Please use the format: key=value')
535
- value = yaml.safe_load(value)
536
+ value = yaml_utils.safe_load(value)
536
537
  nested_keys = tuple(key.split('.'))
537
538
  config.set_nested(nested_keys, value)
538
539
  return config
@@ -585,7 +586,8 @@ def _reload_config_as_server() -> None:
585
586
  row = session.query(config_yaml_table).filter_by(
586
587
  key=key).first()
587
588
  if row:
588
- db_config = config_utils.Config(yaml.safe_load(row.value))
589
+ db_config = config_utils.Config(
590
+ yaml_utils.safe_load(row.value))
589
591
  db_config.pop_nested(('db',), None)
590
592
  return db_config
591
593
  return None
@@ -5,6 +5,8 @@ from typing import Any, Dict, List
5
5
 
6
6
  import yaml
7
7
 
8
+ from sky.utils import yaml_utils
9
+
8
10
 
9
11
  class SSHNodePoolManager:
10
12
  """Manager for SSH Node Pool configurations."""
@@ -21,7 +23,7 @@ class SSHNodePoolManager:
21
23
 
22
24
  try:
23
25
  with open(self.config_path, 'r', encoding='utf-8') as f:
24
- return yaml.safe_load(f) or {}
26
+ return yaml_utils.safe_load(f) or {}
25
27
  except Exception as e:
26
28
  raise RuntimeError(
27
29
  f'Failed to read SSH Node Pool config: {e}') from e
sky/task.py CHANGED
@@ -4,7 +4,6 @@ import inspect
4
4
  import json
5
5
  import os
6
6
  import re
7
- import typing
8
7
  from typing import (Any, Callable, Dict, Iterable, List, Optional, Set, Tuple,
9
8
  Union)
10
9
 
@@ -15,7 +14,6 @@ from sky import dag as dag_lib
15
14
  from sky import exceptions
16
15
  from sky import resources as resources_lib
17
16
  from sky import sky_logging
18
- from sky.adaptors import common as adaptors_common
19
17
  from sky.data import data_utils
20
18
  from sky.data import storage as storage_lib
21
19
  from sky.provision import docker_utils
@@ -26,11 +24,7 @@ from sky.utils import registry
26
24
  from sky.utils import schemas
27
25
  from sky.utils import ux_utils
28
26
  from sky.utils import volume as volume_lib
29
-
30
- if typing.TYPE_CHECKING:
31
- import yaml
32
- else:
33
- yaml = adaptors_common.LazyImport('yaml')
27
+ from sky.utils import yaml_utils
34
28
 
35
29
  logger = sky_logging.init_logger(__name__)
36
30
 
@@ -836,7 +830,7 @@ class Task:
836
830
  # https://github.com/yaml/pyyaml/issues/165#issuecomment-430074049
837
831
  # to raise errors on duplicate keys.
838
832
  user_specified_yaml = f.read()
839
- config = yaml.safe_load(user_specified_yaml)
833
+ config = yaml_utils.safe_load(user_specified_yaml)
840
834
 
841
835
  if isinstance(config, str):
842
836
  with ux_utils.print_exception_no_traceback():
@@ -1611,7 +1605,7 @@ class Task:
1611
1605
  if use_user_specified_yaml:
1612
1606
  if self._user_specified_yaml is None:
1613
1607
  return self._to_yaml_config(redact_secrets=True)
1614
- config = yaml.safe_load(self._user_specified_yaml)
1608
+ config = yaml_utils.safe_load(self._user_specified_yaml)
1615
1609
  if config.get('secrets') is not None:
1616
1610
  config['secrets'] = {k: '<redacted>' for k in config['secrets']}
1617
1611
  return config
sky/users/server.py CHANGED
@@ -10,11 +10,11 @@ from typing import Any, Dict, Generator, List
10
10
 
11
11
  import fastapi
12
12
  import filelock
13
- from passlib.hash import apr_md5_crypt
14
13
 
15
14
  from sky import global_user_state
16
15
  from sky import models
17
16
  from sky import sky_logging
17
+ from sky.server import common as server_common
18
18
  from sky.server.requests import payloads
19
19
  from sky.skylet import constants
20
20
  from sky.users import permission
@@ -86,7 +86,7 @@ async def user_create(user_create_body: payloads.UserCreateBody) -> None:
86
86
  role = rbac.get_default_role()
87
87
 
88
88
  # Create user
89
- password_hash = apr_md5_crypt.hash(password)
89
+ password_hash = server_common.crypt_ctx.hash(password)
90
90
  user_hash = hashlib.md5(
91
91
  username.encode()).hexdigest()[:common_utils.USER_HASH_LENGTH]
92
92
  with _user_lock(user_hash):
@@ -146,7 +146,7 @@ async def user_update(request: fastapi.Request,
146
146
 
147
147
  with _user_lock(user_info.id):
148
148
  if password:
149
- password_hash = apr_md5_crypt.hash(password)
149
+ password_hash = server_common.crypt_ctx.hash(password)
150
150
  global_user_state.add_or_update_user(
151
151
  models.User(id=user_info.id,
152
152
  name=user_info.name,
@@ -271,13 +271,13 @@ async def user_import(
271
271
  creation_errors.append(f'{username}: User already exists')
272
272
  continue
273
273
 
274
- # Check if password is already hashed (APR1 hash)
275
- if password.startswith('$apr1$'):
274
+ # Check if password is already hashed
275
+ if server_common.crypt_ctx.identify(password) is not None:
276
276
  # Password is already hashed, use it directly
277
277
  password_hash = password
278
278
  else:
279
279
  # Password is plain text, hash it
280
- password_hash = apr_md5_crypt.hash(password)
280
+ password_hash = server_common.crypt_ctx.hash(password)
281
281
 
282
282
  user_hash = hashlib.md5(
283
283
  username.encode()).hexdigest()[:common_utils.USER_HASH_LENGTH]
sky/utils/common_utils.py CHANGED
@@ -30,6 +30,7 @@ from sky.usage import constants as usage_constants
30
30
  from sky.utils import annotations
31
31
  from sky.utils import ux_utils
32
32
  from sky.utils import validator
33
+ from sky.utils import yaml_utils
33
34
 
34
35
  if typing.TYPE_CHECKING:
35
36
  import jinja2
@@ -577,13 +578,13 @@ def read_yaml(path: Optional[str]) -> Dict[str, Any]:
577
578
  if path is None:
578
579
  raise ValueError('Attempted to read a None YAML.')
579
580
  with open(path, 'r', encoding='utf-8') as f:
580
- config = yaml.safe_load(f)
581
+ config = yaml_utils.safe_load(f)
581
582
  return config
582
583
 
583
584
 
584
585
  def read_yaml_all_str(yaml_str: str) -> List[Dict[str, Any]]:
585
586
  stream = io.StringIO(yaml_str)
586
- config = yaml.safe_load_all(stream)
587
+ config = yaml_utils.safe_load_all(stream)
587
588
  configs = list(config)
588
589
  if not configs:
589
590
  # Empty YAML file.
@@ -0,0 +1,35 @@
1
+ """YAML utilities."""
2
+ from typing import Any, TYPE_CHECKING
3
+
4
+ from sky.adaptors import common
5
+
6
+ if TYPE_CHECKING:
7
+ import yaml
8
+ else:
9
+ yaml = common.LazyImport('yaml')
10
+
11
+ _csafe_loader_import_error = False
12
+
13
+
14
+ def safe_load(stream) -> Any:
15
+ global _csafe_loader_import_error
16
+ if _csafe_loader_import_error:
17
+ return yaml.load(stream, Loader=yaml.SafeLoader)
18
+
19
+ try:
20
+ return yaml.load(stream, Loader=yaml.CSafeLoader)
21
+ except ImportError:
22
+ _csafe_loader_import_error = True
23
+ return yaml.load(stream, Loader=yaml.SafeLoader)
24
+
25
+
26
+ def safe_load_all(stream) -> Any:
27
+ global _csafe_loader_import_error
28
+ if _csafe_loader_import_error:
29
+ return yaml.load_all(stream, Loader=yaml.SafeLoader)
30
+
31
+ try:
32
+ return yaml.load_all(stream, Loader=yaml.CSafeLoader)
33
+ except ImportError:
34
+ _csafe_loader_import_error = True
35
+ return yaml.load_all(stream, Loader=yaml.SafeLoader)
sky/volumes/volume.py CHANGED
@@ -125,14 +125,19 @@ class Volume:
125
125
 
126
126
  def _validate_config(self) -> None:
127
127
  """Validate the volume config."""
128
+ assert self.cloud is not None, 'Cloud must be specified'
129
+ cloud_obj = registry.CLOUD_REGISTRY.from_str(self.cloud)
130
+ assert cloud_obj is not None
131
+
132
+ valid, err_msg = cloud_obj.is_volume_name_valid(self.name)
133
+ if not valid:
134
+ raise ValueError(f'Invalid volume name: {err_msg}')
135
+
128
136
  if not self.resource_name and not self.size:
129
137
  raise ValueError('Size is required for new volumes. '
130
138
  'Please specify the size in the YAML file or '
131
139
  'use the --size flag.')
132
140
  if self.labels:
133
- assert self.cloud is not None, 'Cloud must be specified'
134
- cloud_obj = registry.CLOUD_REGISTRY.from_str(self.cloud)
135
- assert cloud_obj is not None
136
141
  for key, value in self.labels.items():
137
142
  valid, err_msg = cloud_obj.is_label_valid(key, value)
138
143
  if not valid:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skypilot-nightly
3
- Version: 1.0.0.dev20250826
3
+ Version: 1.0.0.dev20250827
4
4
  Summary: SkyPilot: Run AI on Any Infra — Unified, Faster, Cheaper.
5
5
  Author: SkyPilot Team
6
6
  License: Apache 2.0
@@ -53,6 +53,7 @@ Requires-Dist: casbin
53
53
  Requires-Dist: sqlalchemy_adapter
54
54
  Requires-Dist: prometheus_client>=0.8.0
55
55
  Requires-Dist: passlib
56
+ Requires-Dist: bcrypt
56
57
  Requires-Dist: pyjwt
57
58
  Requires-Dist: gitpython
58
59
  Requires-Dist: types-paramiko