skypilot-nightly 1.0.0.dev20250720__py3-none-any.whl → 1.0.0.dev20250723__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 (103) hide show
  1. sky/__init__.py +2 -2
  2. sky/admin_policy.py +11 -4
  3. sky/backends/backend_utils.py +27 -11
  4. sky/backends/cloud_vm_ray_backend.py +22 -27
  5. sky/client/cli/command.py +38 -23
  6. sky/client/sdk.py +52 -7
  7. sky/clouds/nebius.py +2 -5
  8. sky/clouds/vast.py +2 -1
  9. sky/dashboard/out/404.html +1 -1
  10. sky/dashboard/out/_next/static/chunks/{1141-d8c6404a7c6fffe6.js → 1141-e49a159c30a6c4a7.js} +1 -1
  11. sky/dashboard/out/_next/static/chunks/1559-18717d96ef2fcbe9.js +30 -0
  12. sky/dashboard/out/_next/static/chunks/{1871-a821dcaaae2a3823.js → 1871-ea0e7283886407ca.js} +2 -2
  13. sky/dashboard/out/_next/static/chunks/2003.b82e6db40ec4c463.js +1 -0
  14. sky/dashboard/out/_next/static/chunks/2350.23778a2b19aabd33.js +1 -0
  15. sky/dashboard/out/_next/static/chunks/2369.2d6e4757f8dfc2b7.js +15 -0
  16. sky/dashboard/out/_next/static/chunks/{2641.5233e938f14e31a7.js → 2641.74c19c4d45a2c034.js} +1 -1
  17. sky/dashboard/out/_next/static/chunks/3785.59705416215ff08b.js +1 -0
  18. sky/dashboard/out/_next/static/chunks/4869.da729a7db3a31f43.js +16 -0
  19. sky/dashboard/out/_next/static/chunks/4937.d75809403fc264ac.js +15 -0
  20. sky/dashboard/out/_next/static/chunks/6135-2abbd0352f8ee061.js +1 -0
  21. sky/dashboard/out/_next/static/chunks/691.488b4aef97c28727.js +55 -0
  22. sky/dashboard/out/_next/static/chunks/6990-f64e03df359e04f7.js +1 -0
  23. sky/dashboard/out/_next/static/chunks/7411-2cc31dc0fdf2a9ad.js +41 -0
  24. sky/dashboard/out/_next/static/chunks/9025.4a9099bdf3ed4875.js +6 -0
  25. sky/dashboard/out/_next/static/chunks/{938-63fc419cb82ad9b3.js → 938-7ee806653aef0609.js} +1 -1
  26. sky/dashboard/out/_next/static/chunks/9847.387abf8a14d722db.js +30 -0
  27. sky/dashboard/out/_next/static/chunks/{9984.2b5e3fa69171bff9.js → 9984.0460de9d3adf5582.js} +1 -1
  28. sky/dashboard/out/_next/static/chunks/pages/_app-da491665d4289aae.js +34 -0
  29. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/{[job]-fa406155b4223d0d.js → [job]-2186770cc2de1623.js} +2 -2
  30. sky/dashboard/out/_next/static/chunks/pages/clusters/{[cluster]-0c37ee1ac5f3474d.js → [cluster]-95afb019ab85801c.js} +1 -1
  31. sky/dashboard/out/_next/static/chunks/pages/clusters-3d4be4961e1c94eb.js +1 -0
  32. sky/dashboard/out/_next/static/chunks/pages/index-89e7daf7b7df02e0.js +1 -0
  33. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-a90b4fe4616dc501.js +1 -0
  34. sky/dashboard/out/_next/static/chunks/pages/infra-0d3d1f890c5d188a.js +1 -0
  35. sky/dashboard/out/_next/static/chunks/pages/jobs/{[job]-c5b357bfd9502fbe.js → [job]-dc0299ffefebcdbe.js} +2 -2
  36. sky/dashboard/out/_next/static/chunks/pages/jobs-49f790d12a85027c.js +1 -0
  37. sky/dashboard/out/_next/static/chunks/pages/{users-19e98664bdd61643.js → users-6790fcefd5487b13.js} +1 -1
  38. sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-6bcd4b20914d76c9.js +1 -0
  39. sky/dashboard/out/_next/static/chunks/pages/workspaces-5f7fe4b7d55b8612.js +1 -0
  40. sky/dashboard/out/_next/static/chunks/webpack-a305898dc479711e.js +1 -0
  41. sky/dashboard/out/_next/static/css/b3227360726f12eb.css +3 -0
  42. sky/dashboard/out/_next/static/mym3Ciwp-zqU7ZpOLGnrW/_buildManifest.js +1 -0
  43. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  44. sky/dashboard/out/clusters/[cluster].html +1 -1
  45. sky/dashboard/out/clusters.html +1 -1
  46. sky/dashboard/out/config.html +1 -1
  47. sky/dashboard/out/index.html +1 -1
  48. sky/dashboard/out/infra/[context].html +1 -1
  49. sky/dashboard/out/infra.html +1 -1
  50. sky/dashboard/out/jobs/[job].html +1 -1
  51. sky/dashboard/out/jobs.html +1 -1
  52. sky/dashboard/out/users.html +1 -1
  53. sky/dashboard/out/volumes.html +1 -1
  54. sky/dashboard/out/workspace/new.html +1 -1
  55. sky/dashboard/out/workspaces/[name].html +1 -1
  56. sky/dashboard/out/workspaces.html +1 -1
  57. sky/data/mounting_utils.py +93 -32
  58. sky/global_user_state.py +2 -3
  59. sky/jobs/state.py +2 -2
  60. sky/provision/nebius/utils.py +3 -6
  61. sky/server/common.py +4 -3
  62. sky/setup_files/MANIFEST.in +1 -1
  63. sky/setup_files/alembic.ini +0 -4
  64. sky/skylet/constants.py +4 -0
  65. sky/skypilot_config.py +5 -31
  66. sky/utils/common_utils.py +8 -3
  67. sky/utils/config_utils.py +15 -0
  68. sky/utils/db/migration_utils.py +44 -4
  69. sky/utils/locks.py +319 -0
  70. sky/utils/schemas.py +38 -34
  71. sky/utils/timeline.py +41 -0
  72. {skypilot_nightly-1.0.0.dev20250720.dist-info → skypilot_nightly-1.0.0.dev20250723.dist-info}/METADATA +1 -1
  73. {skypilot_nightly-1.0.0.dev20250720.dist-info → skypilot_nightly-1.0.0.dev20250723.dist-info}/RECORD +78 -78
  74. sky/dashboard/out/_next/static/chunks/1746.27d40aedc22bd2d6.js +0 -60
  75. sky/dashboard/out/_next/static/chunks/2544.27f70672535675ed.js +0 -1
  76. sky/dashboard/out/_next/static/chunks/2875.c24c6d57dc82e436.js +0 -25
  77. sky/dashboard/out/_next/static/chunks/3785.95b94f18aaec7233.js +0 -1
  78. sky/dashboard/out/_next/static/chunks/3947-b059261d6fa88a1f.js +0 -35
  79. sky/dashboard/out/_next/static/chunks/430.ed51037d1a4a438b.js +0 -1
  80. sky/dashboard/out/_next/static/chunks/4869.c7c055a5c2814f33.js +0 -16
  81. sky/dashboard/out/_next/static/chunks/5491.918ffed0ba7a5294.js +0 -20
  82. sky/dashboard/out/_next/static/chunks/6990-dcb411b566e64cde.js +0 -1
  83. sky/dashboard/out/_next/static/chunks/804-9f5e98ce84d46bdd.js +0 -21
  84. sky/dashboard/out/_next/static/chunks/9025.133e9ba5c780afeb.js +0 -6
  85. sky/dashboard/out/_next/static/chunks/9470-8178183f3bae198f.js +0 -1
  86. sky/dashboard/out/_next/static/chunks/9847.46e613d000c55859.js +0 -30
  87. sky/dashboard/out/_next/static/chunks/pages/_app-507712f30cd3cec3.js +0 -20
  88. sky/dashboard/out/_next/static/chunks/pages/clusters-102d169e87913ba1.js +0 -1
  89. sky/dashboard/out/_next/static/chunks/pages/index-927ddeebe57a8ac3.js +0 -1
  90. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-8b0809f59034d509.js +0 -1
  91. sky/dashboard/out/_next/static/chunks/pages/infra-ae9d2f705ce582c9.js +0 -1
  92. sky/dashboard/out/_next/static/chunks/pages/jobs-5bbdc71878f0a068.js +0 -1
  93. sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-7c0187f43757a548.js +0 -1
  94. sky/dashboard/out/_next/static/chunks/pages/workspaces-a1e43d9ef51a9cea.js +0 -1
  95. sky/dashboard/out/_next/static/chunks/webpack-26cdc782eed15a7d.js +0 -1
  96. sky/dashboard/out/_next/static/css/5122cb0a08486fd3.css +0 -3
  97. sky/dashboard/out/_next/static/pTQKG61ng32Zc7gsAROFJ/_buildManifest.js +0 -1
  98. sky/schemas/db/skypilot_config/001_initial_schema.py +0 -30
  99. /sky/dashboard/out/_next/static/{pTQKG61ng32Zc7gsAROFJ → mym3Ciwp-zqU7ZpOLGnrW}/_ssgManifest.js +0 -0
  100. {skypilot_nightly-1.0.0.dev20250720.dist-info → skypilot_nightly-1.0.0.dev20250723.dist-info}/WHEEL +0 -0
  101. {skypilot_nightly-1.0.0.dev20250720.dist-info → skypilot_nightly-1.0.0.dev20250723.dist-info}/entry_points.txt +0 -0
  102. {skypilot_nightly-1.0.0.dev20250720.dist-info → skypilot_nightly-1.0.0.dev20250723.dist-info}/licenses/LICENSE +0 -0
  103. {skypilot_nightly-1.0.0.dev20250720.dist-info → skypilot_nightly-1.0.0.dev20250723.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/dashboard/_next/static/css/5122cb0a08486fd3.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/5122cb0a08486fd3.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/dashboard/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script><script src="/dashboard/_next/static/chunks/webpack-26cdc782eed15a7d.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-507712f30cd3cec3.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces-a1e43d9ef51a9cea.js" defer=""></script><script src="/dashboard/_next/static/pTQKG61ng32Zc7gsAROFJ/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/pTQKG61ng32Zc7gsAROFJ/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces","query":{},"buildId":"pTQKG61ng32Zc7gsAROFJ","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/dashboard/_next/static/css/b3227360726f12eb.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/b3227360726f12eb.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/dashboard/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script><script src="/dashboard/_next/static/chunks/webpack-a305898dc479711e.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-da491665d4289aae.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces-5f7fe4b7d55b8612.js" defer=""></script><script src="/dashboard/_next/static/mym3Ciwp-zqU7ZpOLGnrW/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/mym3Ciwp-zqU7ZpOLGnrW/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces","query":{},"buildId":"mym3Ciwp-zqU7ZpOLGnrW","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
@@ -39,19 +39,32 @@ _GOOFYS_WRAPPER = ('$(if [ -S /dev/log ] ; then '
39
39
 
40
40
 
41
41
  def get_s3_mount_install_cmd() -> str:
42
- """Returns a command to install S3 mount utility goofys."""
42
+ """Returns command for basic S3 mounting (goofys by default, rclone for
43
+ ARM64)."""
43
44
  # TODO(aylei): maintain our goofys fork under skypilot-org
44
- install_cmd = ('ARCH=$(uname -m) && '
45
- 'if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then '
46
- ' echo "goofys is not supported on $ARCH" && '
47
- f' exit {exceptions.ARCH_NOT_SUPPORTED_EXIT_CODE}; '
48
- 'else '
49
- ' ARCH_SUFFIX="amd64"; '
50
- 'fi && '
51
- 'sudo wget -nc https://github.com/aylei/goofys/'
52
- 'releases/download/0.24.0-aylei-upstream/goofys '
53
- '-O /usr/local/bin/goofys && '
54
- 'sudo chmod 755 /usr/local/bin/goofys')
45
+ install_cmd = (
46
+ 'ARCH=$(uname -m) && '
47
+ 'if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then '
48
+ # Use rclone for ARM64 since goofys doesn't support it
49
+ # Extract core rclone installation logic without redundant ARCH check
50
+ ' ARCH_SUFFIX="arm" && '
51
+ f' (which dpkg > /dev/null 2>&1 && (which rclone > /dev/null || '
52
+ f'(cd ~ > /dev/null && curl -O https://downloads.rclone.org/'
53
+ f'{RCLONE_VERSION}/rclone-{RCLONE_VERSION}-linux-${{ARCH_SUFFIX}}.deb '
54
+ f'&& sudo dpkg -i rclone-{RCLONE_VERSION}-linux-${{ARCH_SUFFIX}}.deb '
55
+ f'&& rm -f rclone-{RCLONE_VERSION}-linux-${{ARCH_SUFFIX}}.deb))) || '
56
+ f'(which rclone > /dev/null || (cd ~ > /dev/null && curl -O '
57
+ f'https://downloads.rclone.org/{RCLONE_VERSION}/'
58
+ f'rclone-{RCLONE_VERSION}-linux-${{ARCH_SUFFIX}}.rpm && '
59
+ f'sudo yum --nogpgcheck install '
60
+ f'rclone-{RCLONE_VERSION}-linux-${{ARCH_SUFFIX}}.rpm -y && '
61
+ f'rm -f rclone-{RCLONE_VERSION}-linux-${{ARCH_SUFFIX}}.rpm)); '
62
+ 'else '
63
+ ' sudo wget -nc https://github.com/aylei/goofys/'
64
+ 'releases/download/0.24.0-aylei-upstream/goofys '
65
+ '-O /usr/local/bin/goofys && '
66
+ 'sudo chmod 755 /usr/local/bin/goofys; '
67
+ 'fi')
55
68
  return install_cmd
56
69
 
57
70
 
@@ -59,15 +72,30 @@ def get_s3_mount_install_cmd() -> str:
59
72
  def get_s3_mount_cmd(bucket_name: str,
60
73
  mount_path: str,
61
74
  _bucket_sub_path: Optional[str] = None) -> str:
62
- """Returns a command to mount an S3 bucket using goofys."""
75
+ """Returns a command to mount an S3 bucket (goofys by default, rclone for
76
+ ARM64)"""
63
77
  if _bucket_sub_path is None:
64
78
  _bucket_sub_path = ''
65
79
  else:
66
80
  _bucket_sub_path = f':{_bucket_sub_path}'
67
- mount_cmd = (f'{_GOOFYS_WRAPPER} -o allow_other '
68
- f'--stat-cache-ttl {_STAT_CACHE_TTL} '
69
- f'--type-cache-ttl {_TYPE_CACHE_TTL} '
70
- f'{bucket_name}{_bucket_sub_path} {mount_path}')
81
+
82
+ # Use rclone for ARM64 architectures since goofys doesn't support them
83
+ arch_check = 'ARCH=$(uname -m) && '
84
+ rclone_mount = (
85
+ f'{FUSERMOUNT3_SOFT_LINK_CMD} && '
86
+ f'rclone mount :s3:{bucket_name}{_bucket_sub_path} {mount_path} '
87
+ '--daemon --allow-other')
88
+ goofys_mount = (f'{_GOOFYS_WRAPPER} -o allow_other '
89
+ f'--stat-cache-ttl {_STAT_CACHE_TTL} '
90
+ f'--type-cache-ttl {_TYPE_CACHE_TTL} '
91
+ f'{bucket_name}{_bucket_sub_path} {mount_path}')
92
+
93
+ mount_cmd = (f'{arch_check}'
94
+ f'if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then '
95
+ f' {rclone_mount}; '
96
+ f'else '
97
+ f' {goofys_mount}; '
98
+ f'fi')
71
99
  return mount_cmd
72
100
 
73
101
 
@@ -76,17 +104,33 @@ def get_nebius_mount_cmd(nebius_profile_name: str,
76
104
  endpoint_url: str,
77
105
  mount_path: str,
78
106
  _bucket_sub_path: Optional[str] = None) -> str:
79
- """Returns a command to install Nebius mount utility goofys."""
107
+ """Returns a command to mount Nebius bucket (goofys by default, rclone for
108
+ ARM64)."""
80
109
  if _bucket_sub_path is None:
81
110
  _bucket_sub_path = ''
82
111
  else:
83
112
  _bucket_sub_path = f':{_bucket_sub_path}'
84
- mount_cmd = (f'AWS_PROFILE={nebius_profile_name} {_GOOFYS_WRAPPER} '
85
- '-o allow_other '
86
- f'--stat-cache-ttl {_STAT_CACHE_TTL} '
87
- f'--type-cache-ttl {_TYPE_CACHE_TTL} '
88
- f'--endpoint {endpoint_url} '
89
- f'{bucket_name}{_bucket_sub_path} {mount_path}')
113
+
114
+ # Use rclone for ARM64 architectures since goofys doesn't support them
115
+ arch_check = 'ARCH=$(uname -m) && '
116
+ rclone_mount = (
117
+ f'{FUSERMOUNT3_SOFT_LINK_CMD} && '
118
+ f'AWS_PROFILE={nebius_profile_name} '
119
+ f'rclone mount :s3:{bucket_name}{_bucket_sub_path} {mount_path} '
120
+ f'--s3-endpoint {endpoint_url} --daemon --allow-other')
121
+ goofys_mount = (f'AWS_PROFILE={nebius_profile_name} {_GOOFYS_WRAPPER} '
122
+ '-o allow_other '
123
+ f'--stat-cache-ttl {_STAT_CACHE_TTL} '
124
+ f'--type-cache-ttl {_TYPE_CACHE_TTL} '
125
+ f'--endpoint {endpoint_url} '
126
+ f'{bucket_name}{_bucket_sub_path} {mount_path}')
127
+
128
+ mount_cmd = (f'{arch_check}'
129
+ f'if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then '
130
+ f' {rclone_mount}; '
131
+ f'else '
132
+ f' {goofys_mount}; '
133
+ f'fi')
90
134
  return mount_cmd
91
135
 
92
136
 
@@ -236,18 +280,35 @@ def get_r2_mount_cmd(r2_credentials_path: str,
236
280
  bucket_name: str,
237
281
  mount_path: str,
238
282
  _bucket_sub_path: Optional[str] = None) -> str:
239
- """Returns a command to install R2 mount utility goofys."""
283
+ """Returns a command to mount R2 bucket (goofys by default, rclone for
284
+ ARM64)."""
240
285
  if _bucket_sub_path is None:
241
286
  _bucket_sub_path = ''
242
287
  else:
243
288
  _bucket_sub_path = f':{_bucket_sub_path}'
244
- mount_cmd = (f'AWS_SHARED_CREDENTIALS_FILE={r2_credentials_path} '
245
- f'AWS_PROFILE={r2_profile_name} {_GOOFYS_WRAPPER} '
246
- '-o allow_other '
247
- f'--stat-cache-ttl {_STAT_CACHE_TTL} '
248
- f'--type-cache-ttl {_TYPE_CACHE_TTL} '
249
- f'--endpoint {endpoint_url} '
250
- f'{bucket_name}{_bucket_sub_path} {mount_path}')
289
+
290
+ # Use rclone for ARM64 architectures since goofys doesn't support them
291
+ arch_check = 'ARCH=$(uname -m) && '
292
+ rclone_mount = (
293
+ f'{FUSERMOUNT3_SOFT_LINK_CMD} && '
294
+ f'AWS_SHARED_CREDENTIALS_FILE={r2_credentials_path} '
295
+ f'AWS_PROFILE={r2_profile_name} '
296
+ f'rclone mount :s3:{bucket_name}{_bucket_sub_path} {mount_path} '
297
+ f'--s3-endpoint {endpoint_url} --daemon --allow-other')
298
+ goofys_mount = (f'AWS_SHARED_CREDENTIALS_FILE={r2_credentials_path} '
299
+ f'AWS_PROFILE={r2_profile_name} {_GOOFYS_WRAPPER} '
300
+ '-o allow_other '
301
+ f'--stat-cache-ttl {_STAT_CACHE_TTL} '
302
+ f'--type-cache-ttl {_TYPE_CACHE_TTL} '
303
+ f'--endpoint {endpoint_url} '
304
+ f'{bucket_name}{_bucket_sub_path} {mount_path}')
305
+
306
+ mount_cmd = (f'{arch_check}'
307
+ f'if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then '
308
+ f' {rclone_mount}; '
309
+ f'else '
310
+ f' {goofys_mount}; '
311
+ f'fi')
251
312
  return mount_cmd
252
313
 
253
314
 
sky/global_user_state.py CHANGED
@@ -17,7 +17,6 @@ import typing
17
17
  from typing import Any, Dict, List, Optional, Set, Tuple
18
18
  import uuid
19
19
 
20
- from alembic import command as alembic_command
21
20
  import sqlalchemy
22
21
  from sqlalchemy import exc as sqlalchemy_exc
23
22
  from sqlalchemy import orm
@@ -243,8 +242,8 @@ def create_table(engine: sqlalchemy.engine.Engine):
243
242
  engine, migration_utils.GLOBAL_USER_STATE_DB_NAME)
244
243
  # pylint: disable=line-too-long
245
244
  alembic_config.config_ini_section = migration_utils.GLOBAL_USER_STATE_DB_NAME
246
- alembic_command.upgrade(alembic_config,
247
- migration_utils.GLOBAL_USER_STATE_VERSION)
245
+ migration_utils.safe_alembic_upgrade(
246
+ engine, alembic_config, migration_utils.GLOBAL_USER_STATE_VERSION)
248
247
 
249
248
 
250
249
  def initialize_and_get_db() -> sqlalchemy.engine.Engine:
sky/jobs/state.py CHANGED
@@ -10,7 +10,6 @@ import time
10
10
  import typing
11
11
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
12
12
 
13
- from alembic import command as alembic_command
14
13
  import colorama
15
14
  import sqlalchemy
16
15
  from sqlalchemy import exc as sqlalchemy_exc
@@ -134,7 +133,8 @@ def create_table(engine: sqlalchemy.engine.Engine):
134
133
  alembic_config = migration_utils.get_alembic_config(
135
134
  engine, migration_utils.SPOT_JOBS_DB_NAME)
136
135
  alembic_config.config_ini_section = migration_utils.SPOT_JOBS_DB_NAME
137
- alembic_command.upgrade(alembic_config, migration_utils.SPOT_JOBS_VERSION)
136
+ migration_utils.safe_alembic_upgrade(engine, alembic_config,
137
+ migration_utils.SPOT_JOBS_VERSION)
138
138
 
139
139
 
140
140
  def initialize_and_get_db() -> sqlalchemy.engine.Engine:
@@ -41,10 +41,7 @@ def get_project_by_region(region: str) -> str:
41
41
 
42
42
  # Check is there project if in config
43
43
  project_id = skypilot_config.get_effective_region_config(
44
- cloud='nebius',
45
- region=None,
46
- keys=(region, 'project_id'),
47
- default_value=None)
44
+ cloud='nebius', region=region, keys=('project_id',), default_value=None)
48
45
  if project_id is not None:
49
46
  return project_id
50
47
  for project in projects.items:
@@ -189,8 +186,8 @@ def launch(cluster_name_on_cloud: str,
189
186
  if preset == '8gpu-128vcpu-1600gb':
190
187
  fabric = skypilot_config.get_effective_region_config(
191
188
  cloud='nebius',
192
- region=None,
193
- keys=(region, 'fabric'),
189
+ region=region,
190
+ keys=('fabric',),
194
191
  default_value=None)
195
192
 
196
193
  # Auto-select fabric if network_tier=best and no fabric configured
sky/server/common.py CHANGED
@@ -252,8 +252,9 @@ def get_dashboard_url(server_url: str,
252
252
 
253
253
 
254
254
  @annotations.lru_cache(scope='global')
255
- def is_api_server_local():
256
- return get_server_url() in AVAILABLE_LOCAL_API_SERVER_URLS
255
+ def is_api_server_local(endpoint: Optional[str] = None):
256
+ server_url = endpoint if endpoint is not None else get_server_url()
257
+ return server_url in AVAILABLE_LOCAL_API_SERVER_URLS
257
258
 
258
259
 
259
260
  def _handle_non_200_server_status(
@@ -566,7 +567,7 @@ def check_server_healthy(
566
567
  api_server_status = api_server_info.status
567
568
  if api_server_status == ApiServerStatus.VERSION_MISMATCH:
568
569
  msg = api_server_info.error
569
- if is_api_server_local():
570
+ if is_api_server_local(endpoint):
570
571
  # For local server, just hint user to restart the server to get
571
572
  # a consistent version.
572
573
  msg = _LOCAL_API_SERVER_RESTART_HINT
@@ -18,5 +18,5 @@ include sky/server/html/*
18
18
  recursive-include sky/dashboard/out *
19
19
  include sky/users/*.conf
20
20
  include sky/utils/*.sh
21
- include alembic.ini
21
+ include sky/setup_files/alembic.ini
22
22
  recursive-include sky/schemas/db *
@@ -94,10 +94,6 @@ version_table = alembic_version_state_db
94
94
  version_locations = %(here)s/../schemas/db/spot_jobs
95
95
  version_table = alembic_version_spot_jobs_db
96
96
 
97
- [sky_config_db]
98
- version_locations = %(here)s/../schemas/db/skypilot_config
99
- version_table = alembic_version_sky_config_db
100
-
101
97
  [post_write_hooks]
102
98
  # post_write_hooks defines scripts or Python functions that are run
103
99
  # on newly generated revision scripts. See the documentation for further
sky/skylet/constants.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Constants for SkyPilot."""
2
+ import os
2
3
  from typing import List, Tuple
3
4
 
4
5
  from packaging import version
@@ -491,3 +492,6 @@ DEFAULT_PRIORITY = 0
491
492
 
492
493
  GRACE_PERIOD_SECONDS_ENV_VAR = SKYPILOT_ENV_VAR_PREFIX + 'GRACE_PERIOD_SECONDS'
493
494
  COST_REPORT_DEFAULT_DAYS = 30
495
+
496
+ # The directory for file locks.
497
+ SKY_LOCKS_DIR = os.path.expanduser('~/.sky/locks')
sky/skypilot_config.py CHANGED
@@ -58,10 +58,8 @@ import threading
58
58
  import typing
59
59
  from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
60
60
 
61
- from alembic import command as alembic_command
62
61
  import filelock
63
62
  import sqlalchemy
64
- from sqlalchemy import exc as sqlalchemy_exc
65
63
  from sqlalchemy import orm
66
64
  from sqlalchemy.dialects import postgresql
67
65
  from sqlalchemy.dialects import sqlite
@@ -78,7 +76,6 @@ from sky.utils import context
78
76
  from sky.utils import schemas
79
77
  from sky.utils import ux_utils
80
78
  from sky.utils.db import db_utils
81
- from sky.utils.db import migration_utils
82
79
  from sky.utils.kubernetes import config_map_utils
83
80
 
84
81
  if typing.TYPE_CHECKING:
@@ -574,17 +571,11 @@ def _reload_config_as_server() -> None:
574
571
  'if db config is specified, no other config is allowed')
575
572
 
576
573
  if db_url:
577
- with migration_utils.db_lock(migration_utils.SKYPILOT_CONFIG_DB_NAME):
574
+ with _DB_USE_LOCK:
578
575
  sqlalchemy_engine = sqlalchemy.create_engine(db_url,
579
576
  poolclass=NullPool)
580
-
581
- # Get alembic config for sky config db and run migrations
582
- alembic_config = migration_utils.get_alembic_config(
583
- sqlalchemy_engine, migration_utils.SKYPILOT_CONFIG_DB_NAME)
584
- # pylint: disable=line-too-long
585
- alembic_config.config_ini_section = migration_utils.SKYPILOT_CONFIG_DB_NAME
586
- alembic_command.upgrade(alembic_config,
587
- migration_utils.SKYPILOT_CONFIG_VERSION)
577
+ db_utils.add_tables_to_db_sqlalchemy(Base.metadata,
578
+ sqlalchemy_engine)
588
579
 
589
580
  def _get_config_yaml_from_db(
590
581
  key: str) -> Optional[config_utils.Config]:
@@ -872,25 +863,8 @@ def update_api_server_config_no_lock(config: config_utils.Config) -> None:
872
863
  with _DB_USE_LOCK:
873
864
  sqlalchemy_engine = sqlalchemy.create_engine(existing_db_url,
874
865
  poolclass=NullPool)
875
-
876
- # Get alembic config for sky config db and run migrations
877
- alembic_config = migration_utils.get_alembic_config(
878
- sqlalchemy_engine, 'sky_config_db')
879
- alembic_config.config_ini_section = 'sky_config_db'
880
- try:
881
- alembic_command.upgrade(alembic_config, '001')
882
- except (sqlalchemy_exc.IntegrityError,
883
- sqlalchemy_exc.OperationalError) as e:
884
- # If the version already exists (due to concurrent
885
- # initialization), we can safely ignore this error
886
- if ('UNIQUE constraint failed: '
887
- 'alembic_version_sky_config_db.version_num'
888
- in str(e) or
889
- 'table alembic_version_sky_config_db already exists'
890
- in str(e)):
891
- pass
892
- else:
893
- raise
866
+ db_utils.add_tables_to_db_sqlalchemy(Base.metadata,
867
+ sqlalchemy_engine)
894
868
 
895
869
  def _set_config_yaml_to_db(key: str,
896
870
  config: config_utils.Config):
sky/utils/common_utils.py CHANGED
@@ -562,8 +562,9 @@ def read_yaml_all(path: str) -> List[Dict[str, Any]]:
562
562
  return read_yaml_all_str(f.read())
563
563
 
564
564
 
565
- def dump_yaml(path: str, config: Union[List[Dict[str, Any]],
566
- Dict[str, Any]]) -> None:
565
+ def dump_yaml(path: str,
566
+ config: Union[List[Dict[str, Any]], Dict[str, Any]],
567
+ blank: bool = False) -> None:
567
568
  """Dumps a YAML file.
568
569
 
569
570
  Args:
@@ -571,7 +572,11 @@ def dump_yaml(path: str, config: Union[List[Dict[str, Any]],
571
572
  config: the configuration to dump.
572
573
  """
573
574
  with open(path, 'w', encoding='utf-8') as f:
574
- f.write(dump_yaml_str(config))
575
+ contents = dump_yaml_str(config)
576
+ if blank and isinstance(config, dict) and len(config) == 0:
577
+ # when dumping to yaml, an empty dict will go in as {}.
578
+ contents = ''
579
+ f.write(contents)
575
580
 
576
581
 
577
582
  def dump_yaml_str(config: Union[List[Dict[str, Any]], Dict[str, Any]]) -> str:
sky/utils/config_utils.py CHANGED
@@ -248,6 +248,8 @@ def get_cloud_config_value_from_dict(
248
248
  region_key = None
249
249
  if cloud == 'kubernetes':
250
250
  region_key = 'context_configs'
251
+ if cloud == 'nebius':
252
+ region_key = 'region_configs'
251
253
 
252
254
  per_context_config = None
253
255
  if region is not None and region_key is not None:
@@ -255,6 +257,19 @@ def get_cloud_config_value_from_dict(
255
257
  keys=(cloud, region_key, region) + keys,
256
258
  default_value=None,
257
259
  override_configs=override_configs)
260
+ if not per_context_config and cloud == 'nebius':
261
+ # TODO (kyuds): Backward compatibility, remove after 0.11.0.
262
+ per_context_config = input_config.get_nested(
263
+ keys=(cloud, region) + keys,
264
+ default_value=None,
265
+ override_configs=override_configs)
266
+ if per_context_config is not None:
267
+ logger.info(
268
+ 'Nebius configuration is using the legacy format. \n'
269
+ 'This format will be deprecated after 0.11.0, refer to '
270
+ '`https://docs.skypilot.co/en/latest/reference/config.html#nebius` ' # pylint: disable=line-too-long
271
+ 'for the new format. Please use `region_configs` to specify region specific configuration.'
272
+ )
258
273
  # if no override found for specified region
259
274
  general_config = input_config.get_nested(keys=(cloud,) + keys,
260
275
  default_value=default_value,
@@ -1,9 +1,12 @@
1
1
  """Constants for the database schemas."""
2
2
 
3
3
  import contextlib
4
+ import logging
4
5
  import os
5
6
 
7
+ from alembic import command as alembic_command
6
8
  from alembic.config import Config
9
+ from alembic.runtime import migration
7
10
  import filelock
8
11
  import sqlalchemy
9
12
 
@@ -13,10 +16,6 @@ GLOBAL_USER_STATE_DB_NAME = 'state_db'
13
16
  GLOBAL_USER_STATE_VERSION = '001'
14
17
  GLOBAL_USER_STATE_LOCK_PATH = '~/.sky/locks/.state_db.lock'
15
18
 
16
- SKYPILOT_CONFIG_DB_NAME = 'skypilot_config_db'
17
- SKYPILOT_CONFIG_VERSION = '001'
18
- SKYPILOT_CONFIG_LOCK_PATH = '~/.sky/locks/.skypilot_config_db.lock'
19
-
20
19
  SPOT_JOBS_DB_NAME = 'spot_jobs_db'
21
20
  SPOT_JOBS_VERSION = '001'
22
21
  SPOT_JOBS_LOCK_PATH = '~/.sky/locks/.spot_jobs_db.lock'
@@ -51,3 +50,44 @@ def get_alembic_config(engine: sqlalchemy.engine.Engine, section: str):
51
50
  alembic_cfg.set_section_option(section, 'sqlalchemy.url', url)
52
51
 
53
52
  return alembic_cfg
53
+
54
+
55
+ def safe_alembic_upgrade(engine: sqlalchemy.engine.Engine,
56
+ alembic_config: Config, target_revision: str):
57
+ """Only upgrade if current version is older than target.
58
+
59
+ This handles the case where a database was created with a newer version of
60
+ the code and we're now running older code. Since our migrations are purely
61
+ additive, it's safe to run a newer database with older code.
62
+
63
+ Args:
64
+ engine: SQLAlchemy engine for the database
65
+ alembic_config: Alembic configuration object
66
+ target_revision: Target revision to upgrade to (e.g., '001')
67
+ """
68
+ # set alembic logger to warning level
69
+ alembic_logger = logging.getLogger('alembic')
70
+ alembic_logger.setLevel(logging.WARNING)
71
+
72
+ current_rev = None
73
+
74
+ # Get the current revision from the database
75
+ version_table = alembic_config.get_section_option(
76
+ alembic_config.config_ini_section, 'version_table', 'alembic_version')
77
+
78
+ with engine.connect() as connection:
79
+ context = migration.MigrationContext.configure(
80
+ connection, opts={'version_table': version_table})
81
+ current_rev = context.get_current_revision()
82
+
83
+ if current_rev is None:
84
+ alembic_command.upgrade(alembic_config, target_revision)
85
+ return
86
+
87
+ # Compare revisions - assuming they are numeric strings like '001', '002'
88
+ current_rev_num = int(current_rev)
89
+ target_rev_num = int(target_revision)
90
+
91
+ # only upgrade if current revision is older than target revision
92
+ if current_rev_num < target_rev_num:
93
+ alembic_command.upgrade(alembic_config, target_revision)