skypilot-nightly 1.0.0.dev20250623__py3-none-any.whl → 1.0.0.dev20250624__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.
Files changed (96) hide show
  1. sky/__init__.py +2 -2
  2. sky/admin_policy.py +16 -5
  3. sky/backends/__init__.py +2 -1
  4. sky/backends/backend_utils.py +12 -0
  5. sky/backends/cloud_vm_ray_backend.py +36 -13
  6. sky/client/cli/command.py +42 -21
  7. sky/client/sdk.py +12 -6
  8. sky/clouds/kubernetes.py +1 -0
  9. sky/core.py +88 -15
  10. sky/dashboard/out/404.html +1 -1
  11. sky/dashboard/out/_next/static/chunks/37-4650f214e2119168.js +6 -0
  12. sky/dashboard/out/_next/static/chunks/42.2273cc2415291ceb.js +6 -0
  13. sky/dashboard/out/_next/static/chunks/470-1494c899266cf5c9.js +1 -0
  14. sky/dashboard/out/_next/static/chunks/{513.211357a2914a34b2.js → 513.309df9e18a9ff005.js} +1 -1
  15. sky/dashboard/out/_next/static/chunks/856-bfddc18e16f3873c.js +1 -0
  16. sky/dashboard/out/_next/static/chunks/938-ce7991c156584b06.js +1 -0
  17. sky/dashboard/out/_next/static/chunks/969-d3a0b53f728d280a.js +1 -0
  18. sky/dashboard/out/_next/static/chunks/989-db34c16ad7ea6155.js +1 -0
  19. sky/dashboard/out/_next/static/chunks/pages/{_app-c416e87d5c2715cf.js → _app-ce31493da9747ef4.js} +1 -1
  20. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-4e065c812a52460b.js +6 -0
  21. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-520ec1ab65e2f2a4.js +6 -0
  22. sky/dashboard/out/_next/static/chunks/pages/clusters-7e9736af1c6345a6.js +1 -0
  23. sky/dashboard/out/_next/static/chunks/pages/config-e4f473661889e7cd.js +1 -0
  24. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-00fd23b9577492ca.js +1 -0
  25. sky/dashboard/out/_next/static/chunks/pages/infra-8a4bf7370d4d9bb7.js +1 -0
  26. sky/dashboard/out/_next/static/chunks/pages/jobs/{[job]-cf490d1fa38f3740.js → [job]-171c27f4ca94861c.js} +1 -1
  27. sky/dashboard/out/_next/static/chunks/pages/jobs-55e5bcb16d563231.js +1 -0
  28. sky/dashboard/out/_next/static/chunks/pages/users-c9f4d785cdaa52d8.js +1 -0
  29. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-c4ff1ec05e2f3daf.js → [name]-ecc5a7003776cfa7.js} +1 -1
  30. sky/dashboard/out/_next/static/chunks/pages/workspaces-f00cba35691483b1.js +1 -0
  31. sky/dashboard/out/_next/static/chunks/webpack-c85998e6a5722f21.js +1 -0
  32. sky/dashboard/out/_next/static/css/6ab927686b492a4a.css +3 -0
  33. sky/dashboard/out/_next/static/zsALxITkbP8J8NVwSDwMo/_buildManifest.js +1 -0
  34. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  35. sky/dashboard/out/clusters/[cluster].html +1 -1
  36. sky/dashboard/out/clusters.html +1 -1
  37. sky/dashboard/out/config.html +1 -1
  38. sky/dashboard/out/index.html +1 -1
  39. sky/dashboard/out/infra/[context].html +1 -1
  40. sky/dashboard/out/infra.html +1 -1
  41. sky/dashboard/out/jobs/[job].html +1 -1
  42. sky/dashboard/out/jobs.html +1 -1
  43. sky/dashboard/out/users.html +1 -1
  44. sky/dashboard/out/workspace/new.html +1 -1
  45. sky/dashboard/out/workspaces/[name].html +1 -1
  46. sky/dashboard/out/workspaces.html +1 -1
  47. sky/exceptions.py +11 -0
  48. sky/global_user_state.py +134 -20
  49. sky/jobs/client/sdk.py +0 -1
  50. sky/jobs/controller.py +5 -1
  51. sky/jobs/scheduler.py +4 -3
  52. sky/jobs/server/core.py +117 -51
  53. sky/jobs/state.py +15 -0
  54. sky/jobs/utils.py +114 -8
  55. sky/resources.py +1 -1
  56. sky/server/requests/payloads.py +6 -3
  57. sky/server/requests/requests.py +24 -1
  58. sky/server/server.py +4 -3
  59. sky/skylet/constants.py +5 -11
  60. sky/task.py +1 -26
  61. sky/templates/jobs-controller.yaml.j2 +12 -1
  62. sky/templates/kubernetes-ray.yml.j2 +1 -1
  63. sky/utils/admin_policy_utils.py +5 -1
  64. sky/utils/cli_utils/status_utils.py +25 -17
  65. sky/utils/command_runner.py +118 -12
  66. sky/utils/command_runner.pyi +57 -0
  67. sky/utils/common_utils.py +9 -1
  68. sky/utils/controller_utils.py +1 -2
  69. sky/utils/schemas.py +34 -35
  70. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/METADATA +1 -1
  71. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/RECORD +78 -77
  72. sky/dashboard/out/_next/static/F4kiZ6Zh72jA6HzZ3ncFo/_buildManifest.js +0 -1
  73. sky/dashboard/out/_next/static/chunks/37-3a4d77ad62932eaf.js +0 -6
  74. sky/dashboard/out/_next/static/chunks/42.d39e24467181b06b.js +0 -6
  75. sky/dashboard/out/_next/static/chunks/470-4d1a5dbe58a8a2b9.js +0 -1
  76. sky/dashboard/out/_next/static/chunks/856-c2c39c0912285e54.js +0 -1
  77. sky/dashboard/out/_next/static/chunks/938-1493ac755eadeb35.js +0 -1
  78. sky/dashboard/out/_next/static/chunks/969-20d54a9d998dc102.js +0 -1
  79. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-89216c616dbaa9c5.js +0 -6
  80. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-36bc0962129f72df.js +0 -6
  81. sky/dashboard/out/_next/static/chunks/pages/clusters-82a651dbad53ec6e.js +0 -1
  82. sky/dashboard/out/_next/static/chunks/pages/config-497a35a7ed49734a.js +0 -1
  83. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-d2910be98e9227cb.js +0 -1
  84. sky/dashboard/out/_next/static/chunks/pages/infra-780860bcc1103945.js +0 -1
  85. sky/dashboard/out/_next/static/chunks/pages/jobs-336ab80e270ce2ce.js +0 -1
  86. sky/dashboard/out/_next/static/chunks/pages/users-928edf039219e47b.js +0 -1
  87. sky/dashboard/out/_next/static/chunks/pages/workspaces-82e6601baa5dd280.js +0 -1
  88. sky/dashboard/out/_next/static/chunks/webpack-0263b00d6a10e64a.js +0 -1
  89. sky/dashboard/out/_next/static/css/6c12ecc3bd2239b6.css +0 -3
  90. /sky/dashboard/out/_next/static/chunks/{843-b3040e493f6e7947.js → 843-bde186946d353355.js} +0 -0
  91. /sky/dashboard/out/_next/static/chunks/{973-db3c97c2bfbceb65.js → 973-56412c7976b4655b.js} +0 -0
  92. /sky/dashboard/out/_next/static/{F4kiZ6Zh72jA6HzZ3ncFo → zsALxITkbP8J8NVwSDwMo}/_ssgManifest.js +0 -0
  93. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/WHEEL +0 -0
  94. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/entry_points.txt +0 -0
  95. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/licenses/LICENSE +0 -0
  96. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250624.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import hashlib
4
4
  import os
5
5
  import pathlib
6
6
  import shlex
7
+ import sys
7
8
  import time
8
9
  from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, Union
9
10
 
@@ -231,9 +232,9 @@ class CommandRunner:
231
232
  self,
232
233
  source: str,
233
234
  target: str,
234
- node_destination: str,
235
+ node_destination: Optional[str],
235
236
  up: bool,
236
- rsh_option: str,
237
+ rsh_option: Optional[str],
237
238
  # Advanced options.
238
239
  log_path: str = os.devnull,
239
240
  stream_logs: bool = True,
@@ -283,28 +284,43 @@ class CommandRunner:
283
284
  RSYNC_EXCLUDE_OPTION.format(
284
285
  shlex.quote(str(resolved_source / GIT_EXCLUDE))))
285
286
 
286
- rsync_command.append(f'-e {shlex.quote(rsh_option)}')
287
+ if rsh_option is not None:
288
+ rsync_command.append(f'-e {shlex.quote(rsh_option)}')
289
+ maybe_dest_prefix = ('' if node_destination is None else
290
+ f'{node_destination}:')
287
291
 
288
292
  if up:
289
293
  resolved_target = target
290
- if target.startswith('~'):
291
- remote_home_dir = _get_remote_home_dir_with_retry()
292
- resolved_target = target.replace('~', remote_home_dir)
294
+ if node_destination is None:
295
+ # Is a local rsync. Directly resolve the target.
296
+ resolved_target = str(
297
+ pathlib.Path(target).expanduser().resolve())
298
+ else:
299
+ if target.startswith('~'):
300
+ remote_home_dir = _get_remote_home_dir_with_retry()
301
+ resolved_target = target.replace('~', remote_home_dir)
293
302
  full_source_str = str(resolved_source)
294
303
  if resolved_source.is_dir():
295
304
  full_source_str = os.path.join(full_source_str, '')
296
305
  rsync_command.extend([
297
306
  f'{full_source_str!r}',
298
- f'{node_destination}:{resolved_target!r}',
307
+ f'{maybe_dest_prefix}{resolved_target!r}',
299
308
  ])
300
309
  else:
301
310
  resolved_source = source
302
- if source.startswith('~'):
303
- remote_home_dir = _get_remote_home_dir_with_retry()
304
- resolved_source = source.replace('~', remote_home_dir)
311
+ if node_destination is None:
312
+ resolved_target = str(
313
+ pathlib.Path(target).expanduser().resolve())
314
+ resolved_source = str(
315
+ pathlib.Path(source).expanduser().resolve())
316
+ else:
317
+ resolved_target = os.path.expanduser(target)
318
+ if source.startswith('~'):
319
+ remote_home_dir = _get_remote_home_dir_with_retry()
320
+ resolved_source = source.replace('~', remote_home_dir)
305
321
  rsync_command.extend([
306
- f'{node_destination}:{resolved_source!r}',
307
- f'{os.path.expanduser(target)!r}',
322
+ f'{maybe_dest_prefix}{resolved_source!r}',
323
+ f'{resolved_target!r}',
308
324
  ])
309
325
  command = ' '.join(rsync_command)
310
326
  logger.debug(f'Running rsync command: {command}')
@@ -964,3 +980,93 @@ class KubernetesCommandRunner(CommandRunner):
964
980
  # /~/xx, so we need to replace ~ with the remote home directory. We
965
981
  # only need to do this when ~ is at the beginning of the path.
966
982
  get_remote_home_dir=get_remote_home_dir)
983
+
984
+
985
+ class LocalProcessCommandRunner(CommandRunner):
986
+ """Runner for local process commands."""
987
+
988
+ def __init__(self):
989
+ super().__init__('local')
990
+
991
+ @timeline.event
992
+ @context_utils.cancellation_guard
993
+ def run(
994
+ self,
995
+ cmd: Union[str, List[str]],
996
+ *,
997
+ require_outputs: bool = False,
998
+ port_forward: Optional[List[Tuple[int, int]]] = None,
999
+ # Advanced options.
1000
+ log_path: str = os.devnull,
1001
+ # If False, do not redirect stdout/stderr to optimize performance.
1002
+ process_stream: bool = True,
1003
+ stream_logs: bool = True,
1004
+ ssh_mode: SshMode = SshMode.NON_INTERACTIVE,
1005
+ separate_stderr: bool = False,
1006
+ connect_timeout: Optional[int] = None,
1007
+ source_bashrc: bool = False,
1008
+ skip_num_lines: int = 0,
1009
+ **kwargs) -> Union[int, Tuple[int, str, str]]:
1010
+ """Use subprocess to run the command."""
1011
+ del port_forward, ssh_mode, connect_timeout # Unused.
1012
+
1013
+ command_str = self._get_command_to_run(cmd,
1014
+ process_stream,
1015
+ separate_stderr,
1016
+ skip_num_lines=skip_num_lines,
1017
+ source_bashrc=source_bashrc)
1018
+
1019
+ log_dir = os.path.expanduser(os.path.dirname(log_path))
1020
+ os.makedirs(log_dir, exist_ok=True)
1021
+
1022
+ executable = None
1023
+ command = [command_str]
1024
+ if not process_stream:
1025
+ if stream_logs:
1026
+ command += [
1027
+ f'| tee {log_path}',
1028
+ # This also requires the executor to be '/bin/bash' instead
1029
+ # of the default '/bin/sh'.
1030
+ '; exit ${PIPESTATUS[0]}'
1031
+ ]
1032
+ else:
1033
+ command += [f'> {log_path}']
1034
+ executable = '/bin/bash'
1035
+ command_str = ' '.join(command)
1036
+ # For local process, the API server might not have this python path
1037
+ # setup. But this command runner should only be triggered from the API
1038
+ # server (in controller consolidation mode), so we can safely replace
1039
+ # the python path with the executable of the API server.
1040
+ command_str = command_str.replace(constants.SKY_PYTHON_CMD,
1041
+ sys.executable)
1042
+ logger.debug(f'Running command locally: {command_str}')
1043
+ return log_lib.run_with_log(command_str,
1044
+ log_path,
1045
+ require_outputs=require_outputs,
1046
+ stream_logs=stream_logs,
1047
+ process_stream=process_stream,
1048
+ shell=True,
1049
+ executable=executable,
1050
+ **kwargs)
1051
+
1052
+ @timeline.event
1053
+ def rsync(
1054
+ self,
1055
+ source: str,
1056
+ target: str,
1057
+ *,
1058
+ up: bool,
1059
+ # Advanced options.
1060
+ log_path: str = os.devnull,
1061
+ stream_logs: bool = True,
1062
+ max_retry: int = 1,
1063
+ ) -> None:
1064
+ """Use rsync to sync the source to the target."""
1065
+ self._rsync(source,
1066
+ target,
1067
+ node_destination=None,
1068
+ up=up,
1069
+ rsh_option=None,
1070
+ log_path=log_path,
1071
+ stream_logs=stream_logs,
1072
+ max_retry=max_retry)
@@ -271,3 +271,60 @@ class KubernetesCommandRunner(CommandRunner):
271
271
  stream_logs: bool = ...,
272
272
  max_retry: int = ...) -> None:
273
273
  ...
274
+
275
+
276
+ class LocalProcessCommandRunner(CommandRunner):
277
+
278
+ def __init__(self) -> None:
279
+ ...
280
+
281
+ @typing.overload
282
+ def run(self,
283
+ cmd: Union[str, List[str]],
284
+ *,
285
+ port_forward: Optional[List[int]] = ...,
286
+ require_outputs: Literal[False] = ...,
287
+ log_path: str = ...,
288
+ process_stream: bool = ...,
289
+ stream_logs: bool = ...,
290
+ ssh_mode: SshMode = ...,
291
+ separate_stderr: bool = ...,
292
+ connect_timeout: Optional[int] = ...,
293
+ source_bashrc: bool = ...,
294
+ skip_lines: int = ...,
295
+ **kwargs) -> int:
296
+ ...
297
+
298
+ @typing.overload
299
+ def run(self,
300
+ cmd: Union[str, List[str]],
301
+ *,
302
+ port_forward: Optional[List[int]] = ...,
303
+ require_outputs: Literal[True],
304
+ log_path: str = ...,
305
+ process_stream: bool = ...,
306
+ stream_logs: bool = ...,
307
+ ssh_mode: SshMode = ...,
308
+ separate_stderr: bool = ...,
309
+ connect_timeout: Optional[int] = ...,
310
+ source_bashrc: bool = ...,
311
+ skip_lines: int = ...,
312
+ **kwargs) -> Tuple[int, str, str]:
313
+ ...
314
+
315
+ @typing.overload
316
+ def run(self,
317
+ cmd: Union[str, List[str]],
318
+ *,
319
+ port_forward: Optional[List[int]] = ...,
320
+ require_outputs: bool = ...,
321
+ log_path: str = ...,
322
+ process_stream: bool = ...,
323
+ stream_logs: bool = ...,
324
+ ssh_mode: SshMode = ...,
325
+ separate_stderr: bool = ...,
326
+ connect_timeout: Optional[int] = ...,
327
+ source_bashrc: bool = ...,
328
+ skip_lines: int = ...,
329
+ **kwargs) -> Union[Tuple[int, str, str], int]:
330
+ ...
sky/utils/common_utils.py CHANGED
@@ -26,6 +26,7 @@ from sky.adaptors import common as adaptors_common
26
26
  from sky.skylet import constants
27
27
  from sky.usage import constants as usage_constants
28
28
  from sky.utils import annotations
29
+ from sky.utils import common_utils
29
30
  from sky.utils import ux_utils
30
31
  from sky.utils import validator
31
32
 
@@ -298,6 +299,13 @@ def get_current_user() -> 'models.User':
298
299
  return models.User.get_current_user()
299
300
 
300
301
 
302
+ def get_current_user_name() -> str:
303
+ """Returns the current user name."""
304
+ name = common_utils.get_current_user().name
305
+ assert name is not None
306
+ return name
307
+
308
+
301
309
  def set_current_user(user: 'models.User'):
302
310
  """Sets the current user."""
303
311
  global _current_user
@@ -754,7 +762,7 @@ def get_cleaned_username(username: str = '') -> str:
754
762
  Returns:
755
763
  A cleaned username.
756
764
  """
757
- username = username or getpass.getuser()
765
+ username = username or common_utils.get_current_user_name()
758
766
  username = username.lower()
759
767
  username = re.sub(r'[^a-z0-9-_]', '', username)
760
768
  username = re.sub(r'^[0-9-]+', '', username)
@@ -2,7 +2,6 @@
2
2
  import copy
3
3
  import dataclasses
4
4
  import enum
5
- import getpass
6
5
  import os
7
6
  import tempfile
8
7
  import typing
@@ -498,7 +497,7 @@ def shared_controller_vars_to_fill(
498
497
  env_vars.update({
499
498
  # Should not use $USER here, as that env var can be empty when
500
499
  # running in a container.
501
- constants.USER_ENV_VAR: getpass.getuser(),
500
+ constants.USER_ENV_VAR: common_utils.get_current_user_name(),
502
501
  constants.USER_ID_ENV_VAR: common_utils.get_user_hash(),
503
502
  # Skip cloud identity check to avoid the overhead.
504
503
  env_options.Options.SKIP_CLOUD_IDENTITY_CHECK.env_key: '1',
sky/utils/schemas.py CHANGED
@@ -672,18 +672,6 @@ def get_task_schema():
672
672
  'service': {
673
673
  'type': 'object',
674
674
  },
675
- 'job': {
676
- 'type': 'object',
677
- 'required': [],
678
- 'additionalProperties': False,
679
- 'properties': {
680
- 'priority': {
681
- 'type': 'integer',
682
- 'minimum': 0,
683
- 'maximum': 1000,
684
- },
685
- },
686
- },
687
675
  'setup': {
688
676
  'type': 'string',
689
677
  },
@@ -899,30 +887,41 @@ def get_config_schema():
899
887
  if k != '$schema'
900
888
  }
901
889
  resources_schema['properties'].pop('ports')
902
- controller_resources_schema = {
903
- 'type': 'object',
904
- 'required': [],
905
- 'additionalProperties': False,
906
- 'properties': {
907
- 'controller': {
908
- 'type': 'object',
909
- 'required': [],
910
- 'additionalProperties': False,
911
- 'properties': {
912
- 'resources': resources_schema,
913
- 'high_availability': {
914
- 'type': 'boolean',
915
- },
916
- 'autostop': _AUTOSTOP_SCHEMA,
917
- }
890
+
891
+ def _get_controller_schema(add_consolidation_mode: bool = False):
892
+ controller_properties = {
893
+ 'resources': resources_schema,
894
+ 'high_availability': {
895
+ 'type': 'boolean',
896
+ 'default': False,
918
897
  },
919
- 'bucket': {
920
- 'type': 'string',
921
- 'pattern': '^(https|s3|gs|r2|cos)://.+',
922
- 'required': [],
898
+ 'autostop': _AUTOSTOP_SCHEMA,
899
+ }
900
+ if add_consolidation_mode:
901
+ controller_properties['consolidation_mode'] = {
902
+ 'type': 'boolean',
903
+ 'default': False,
904
+ }
905
+
906
+ return {
907
+ 'type': 'object',
908
+ 'required': [],
909
+ 'additionalProperties': False,
910
+ 'properties': {
911
+ 'controller': {
912
+ 'type': 'object',
913
+ 'required': [],
914
+ 'additionalProperties': False,
915
+ 'properties': controller_properties,
916
+ },
917
+ 'bucket': {
918
+ 'type': 'string',
919
+ 'pattern': '^(https|s3|gs|r2|cos)://.+',
920
+ 'required': [],
921
+ }
923
922
  }
924
923
  }
925
- }
924
+
926
925
  cloud_configs = {
927
926
  'aws': {
928
927
  'type': 'object',
@@ -1440,8 +1439,8 @@ def get_config_schema():
1440
1439
  'db': {
1441
1440
  'type': 'string',
1442
1441
  },
1443
- 'jobs': controller_resources_schema,
1444
- 'serve': controller_resources_schema,
1442
+ 'jobs': _get_controller_schema(add_consolidation_mode=True),
1443
+ 'serve': _get_controller_schema(add_consolidation_mode=False),
1445
1444
  'allowed_clouds': allowed_clouds,
1446
1445
  'admin_policy': admin_policy_schema,
1447
1446
  'docker': docker_configs,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skypilot-nightly
3
- Version: 1.0.0.dev20250623
3
+ Version: 1.0.0.dev20250624
4
4
  Summary: SkyPilot: Run AI on Any Infra — Unified, Faster, Cheaper.
5
5
  Author: SkyPilot Team
6
6
  License: Apache 2.0