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
@@ -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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/users-018bf31cda52e11b.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/users","query":{},"buildId":"TPMkEeuj85tHTmIW7Gu3S","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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/users-018bf31cda52e11b.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/users","query":{},"buildId":"-eL7Ky3bxVivzeLHNB9U6","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
@@ -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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/volumes-739726d6b823f532.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/volumes","query":{},"buildId":"TPMkEeuj85tHTmIW7Gu3S","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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/volumes-739726d6b823f532.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/volumes","query":{},"buildId":"-eL7Ky3bxVivzeLHNB9U6","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
@@ -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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspace/new","query":{},"buildId":"TPMkEeuj85tHTmIW7Gu3S","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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspace/new","query":{},"buildId":"-eL7Ky3bxVivzeLHNB9U6","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
@@ -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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/616-3d59f75e2ccf9321.js" defer=""></script><script src="/dashboard/_next/static/chunks/6130-2be46d70a38f1e82.js" defer=""></script><script src="/dashboard/_next/static/chunks/5739-d67458fcb1386c92.js" defer=""></script><script src="/dashboard/_next/static/chunks/7411-b15471acd2cba716.js" defer=""></script><script src="/dashboard/_next/static/chunks/1272-1ef0bf0237faccdb.js" defer=""></script><script src="/dashboard/_next/static/chunks/7205-88191679e7988c57.js" defer=""></script><script src="/dashboard/_next/static/chunks/6989-01359c57e018caa4.js" defer=""></script><script src="/dashboard/_next/static/chunks/3850-ff4a9a69d978632b.js" defer=""></script><script src="/dashboard/_next/static/chunks/8969-4a6f1a928fb6d370.js" defer=""></script><script src="/dashboard/_next/static/chunks/6990-08b2a1cae076a943.js" defer=""></script><script src="/dashboard/_next/static/chunks/6135-4b4d5e824b7f9d3c.js" defer=""></script><script src="/dashboard/_next/static/chunks/1121-8afcf719ea87debc.js" defer=""></script><script src="/dashboard/_next/static/chunks/6601-06114c982db410b6.js" defer=""></script><script src="/dashboard/_next/static/chunks/3015-6c9c09593b1e67b6.js" defer=""></script><script src="/dashboard/_next/static/chunks/1141-943efc7aff0f0c06.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces/%5Bname%5D-de06e613e20bc977.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces/[name]","query":{},"buildId":"TPMkEeuj85tHTmIW7Gu3S","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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/616-3d59f75e2ccf9321.js" defer=""></script><script src="/dashboard/_next/static/chunks/6130-2be46d70a38f1e82.js" defer=""></script><script src="/dashboard/_next/static/chunks/5739-d67458fcb1386c92.js" defer=""></script><script src="/dashboard/_next/static/chunks/7411-b15471acd2cba716.js" defer=""></script><script src="/dashboard/_next/static/chunks/1272-1ef0bf0237faccdb.js" defer=""></script><script src="/dashboard/_next/static/chunks/7205-88191679e7988c57.js" defer=""></script><script src="/dashboard/_next/static/chunks/6989-01359c57e018caa4.js" defer=""></script><script src="/dashboard/_next/static/chunks/3850-ff4a9a69d978632b.js" defer=""></script><script src="/dashboard/_next/static/chunks/8969-4a6f1a928fb6d370.js" defer=""></script><script src="/dashboard/_next/static/chunks/6990-08b2a1cae076a943.js" defer=""></script><script src="/dashboard/_next/static/chunks/6135-4b4d5e824b7f9d3c.js" defer=""></script><script src="/dashboard/_next/static/chunks/1121-8afcf719ea87debc.js" defer=""></script><script src="/dashboard/_next/static/chunks/6601-06114c982db410b6.js" defer=""></script><script src="/dashboard/_next/static/chunks/3015-6c9c09593b1e67b6.js" defer=""></script><script src="/dashboard/_next/static/chunks/1141-943efc7aff0f0c06.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces/%5Bname%5D-de06e613e20bc977.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces/[name]","query":{},"buildId":"-eL7Ky3bxVivzeLHNB9U6","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
@@ -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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces-be35b22e2046564c.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/TPMkEeuj85tHTmIW7Gu3S/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces","query":{},"buildId":"TPMkEeuj85tHTmIW7Gu3S","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/4614e06482d7309e.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/4614e06482d7309e.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-6e76f636a048e145.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-cf60a09ccd051a10.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-f15ccb73239a3bf1.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-ce361c6959bc2001.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces-be35b22e2046564c.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/-eL7Ky3bxVivzeLHNB9U6/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces","query":{},"buildId":"-eL7Ky3bxVivzeLHNB9U6","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
sky/global_user_state.py CHANGED
@@ -25,7 +25,6 @@ from sqlalchemy import orm
25
25
  from sqlalchemy.dialects import postgresql
26
26
  from sqlalchemy.dialects import sqlite
27
27
  from sqlalchemy.ext import declarative
28
- import yaml
29
28
 
30
29
  from sky import models
31
30
  from sky import sky_logging
@@ -35,6 +34,7 @@ from sky.utils import common_utils
35
34
  from sky.utils import context_utils
36
35
  from sky.utils import registry
37
36
  from sky.utils import status_lib
37
+ from sky.utils import yaml_utils
38
38
  from sky.utils.db import db_utils
39
39
  from sky.utils.db import migration_utils
40
40
 
@@ -433,6 +433,20 @@ def get_user(user_id: str) -> Optional[models.User]:
433
433
  created_at=row.created_at)
434
434
 
435
435
 
436
+ @_init_db
437
+ def _get_users(user_ids: Set[str]) -> Dict[str, models.User]:
438
+ assert _SQLALCHEMY_ENGINE is not None
439
+ with orm.Session(_SQLALCHEMY_ENGINE) as session:
440
+ rows = session.query(user_table).filter(
441
+ user_table.c.id.in_(user_ids)).all()
442
+ return {
443
+ row.id: models.User(id=row.id,
444
+ name=row.name,
445
+ password=row.password,
446
+ created_at=row.created_at) for row in rows
447
+ }
448
+
449
+
436
450
  @_init_db
437
451
  def get_user_by_name(username: str) -> List[models.User]:
438
452
  with orm.Session(_SQLALCHEMY_ENGINE) as session:
@@ -767,6 +781,32 @@ def get_last_cluster_event(cluster_hash: str,
767
781
  return row.reason
768
782
 
769
783
 
784
+ def _get_last_cluster_event_multiple(
785
+ cluster_hashes: Set[str],
786
+ event_type: ClusterEventType) -> Dict[str, str]:
787
+ assert _SQLALCHEMY_ENGINE is not None
788
+ with orm.Session(_SQLALCHEMY_ENGINE) as session:
789
+ # Use a subquery to get the latest event for each cluster_hash
790
+ latest_events = session.query(
791
+ cluster_event_table.c.cluster_hash,
792
+ sqlalchemy.func.max(cluster_event_table.c.transitioned_at).label(
793
+ 'max_time')).filter(
794
+ cluster_event_table.c.cluster_hash.in_(cluster_hashes),
795
+ cluster_event_table.c.type == event_type.value).group_by(
796
+ cluster_event_table.c.cluster_hash).subquery()
797
+
798
+ # Join with original table to get the full event details
799
+ rows = session.query(cluster_event_table).join(
800
+ latest_events,
801
+ sqlalchemy.and_(
802
+ cluster_event_table.c.cluster_hash ==
803
+ latest_events.c.cluster_hash,
804
+ cluster_event_table.c.transitioned_at ==
805
+ latest_events.c.max_time)).all()
806
+
807
+ return {row.cluster_hash: row.reason for row in rows}
808
+
809
+
770
810
  def cleanup_cluster_events_with_retention(retention_hours: float) -> None:
771
811
  assert _SQLALCHEMY_ENGINE is not None
772
812
  with orm.Session(_SQLALCHEMY_ENGINE) as session:
@@ -1266,18 +1306,70 @@ def get_cluster_from_name(
1266
1306
 
1267
1307
 
1268
1308
  @_init_db
1269
- def get_clusters() -> List[Dict[str, Any]]:
1270
- assert _SQLALCHEMY_ENGINE is not None
1271
- with orm.Session(_SQLALCHEMY_ENGINE) as session:
1272
- rows = session.query(cluster_table).order_by(
1273
- sqlalchemy.desc(cluster_table.c.launched_at)).all()
1309
+ def get_clusters(
1310
+ *, # keyword only separator
1311
+ exclude_managed_clusters: bool = False,
1312
+ workspaces_filter: Optional[Set[str]] = None,
1313
+ user_hashes_filter: Optional[Set[str]] = None,
1314
+ ) -> List[Dict[str, Any]]:
1315
+ """Get clusters from the database.
1316
+
1317
+ Args:
1318
+ exclude_managed_clusters: If True, exclude clusters that have
1319
+ is_managed field set to True.
1320
+ workspaces_filter: If specified, only include clusters
1321
+ that has workspace field set to one of the values.
1322
+ user_hashes_filter: If specified, only include clusters
1323
+ that has user_hash field set to one of the values.
1324
+ """
1325
+ # is a cluster has a null user_hash,
1326
+ # we treat it as belonging to the current user.
1327
+ current_user_hash = common_utils.get_user_hash()
1328
+ assert _SQLALCHEMY_ENGINE is not None
1329
+ with orm.Session(_SQLALCHEMY_ENGINE) as session:
1330
+ query = session.query(cluster_table)
1331
+ if exclude_managed_clusters:
1332
+ query = query.filter(cluster_table.c.is_managed == int(False))
1333
+ if workspaces_filter is not None:
1334
+ query = query.filter(
1335
+ cluster_table.c.workspace.in_(workspaces_filter))
1336
+ if user_hashes_filter is not None:
1337
+ if current_user_hash in user_hashes_filter:
1338
+ # backwards compatibility for old clusters.
1339
+ # If current_user_hash is in user_hashes_filter, we include
1340
+ # clusters that have a null user_hash.
1341
+ query = query.filter(
1342
+ cluster_table.c.user_hash.in_(user_hashes_filter) |
1343
+ (cluster_table.c.user_hash is None))
1344
+ else:
1345
+ query = query.filter(
1346
+ cluster_table.c.user_hash.in_(user_hashes_filter))
1347
+ query = query.order_by(sqlalchemy.desc(cluster_table.c.launched_at))
1348
+ rows = query.all()
1274
1349
  records = []
1350
+
1351
+ # get user hash for each row
1352
+ row_to_user_hash = {}
1275
1353
  for row in rows:
1276
- user_hash = _get_user_hash_or_current_user(row.user_hash)
1277
- user = get_user(user_hash)
1354
+ user_hash = (row.user_hash
1355
+ if row.user_hash is not None else current_user_hash)
1356
+ row_to_user_hash[row.cluster_hash] = user_hash
1357
+
1358
+ # get all users needed for the rows at once
1359
+ user_hashes = set(row_to_user_hash.values())
1360
+ user_hash_to_user = _get_users(user_hashes)
1361
+
1362
+ # get last cluster event for each row
1363
+ cluster_hashes = set(row_to_user_hash.keys())
1364
+ last_cluster_event_dict = _get_last_cluster_event_multiple(
1365
+ cluster_hashes, ClusterEventType.STATUS_CHANGE)
1366
+
1367
+ # get user for each row
1368
+ for row in rows:
1369
+ user_hash = row_to_user_hash[row.cluster_hash]
1370
+ user = user_hash_to_user.get(user_hash, None)
1278
1371
  user_name = user.name if user is not None else None
1279
- last_event = get_last_cluster_event(
1280
- row.cluster_hash, event_type=ClusterEventType.STATUS_CHANGE)
1372
+ last_event = last_cluster_event_dict.get(row.cluster_hash, None)
1281
1373
  # TODO: use namedtuple instead of dict
1282
1374
  record = {
1283
1375
  'name': row.name,
@@ -1999,7 +2091,7 @@ def get_cluster_yaml_dict(cluster_yaml_path: Optional[str]) -> Dict[str, Any]:
1999
2091
  yaml_str = get_cluster_yaml_str(cluster_yaml_path)
2000
2092
  if yaml_str is None:
2001
2093
  raise ValueError(f'Cluster yaml {cluster_yaml_path} not found.')
2002
- return yaml.safe_load(yaml_str)
2094
+ return yaml_utils.safe_load(yaml_str)
2003
2095
 
2004
2096
 
2005
2097
  @_init_db
@@ -3,20 +3,14 @@ import copy
3
3
  import logging
4
4
  import math
5
5
  import os
6
- import typing
7
6
  from typing import Any, Dict, Optional, Union
8
7
 
9
- from sky.adaptors import common as adaptors_common
10
8
  from sky.adaptors import kubernetes
11
9
  from sky.provision import common
12
10
  from sky.provision.kubernetes import network_utils
13
11
  from sky.provision.kubernetes import utils as kubernetes_utils
14
12
  from sky.utils import kubernetes_enums
15
-
16
- if typing.TYPE_CHECKING:
17
- import yaml
18
- else:
19
- yaml = adaptors_common.LazyImport('yaml')
13
+ from sky.utils import yaml_utils
20
14
 
21
15
  logger = logging.getLogger(__name__)
22
16
 
@@ -592,7 +586,7 @@ def _configure_fuse_mounting(provider_config: Dict[str, Any]) -> None:
592
586
  daemonset_path = os.path.join(
593
587
  root_dir, 'kubernetes/manifests/fusermount-server-daemonset.yaml')
594
588
  with open(daemonset_path, 'r', encoding='utf-8') as file:
595
- daemonset = yaml.safe_load(file)
589
+ daemonset = yaml_utils.safe_load(file)
596
590
  kubernetes_utils.merge_custom_metadata(daemonset['metadata'])
597
591
  try:
598
592
  kubernetes.apps_api(context).create_namespaced_daemon_set(
@@ -1441,6 +1441,12 @@ def query_instances(
1441
1441
  phase = pod.status.phase
1442
1442
  pod_status = status_map[phase]
1443
1443
  if non_terminated_only and pod_status is None:
1444
+ logger.debug(f'Pod {pod.metadata.name} is terminated, but '
1445
+ 'query_instances is called with '
1446
+ f'non_terminated_only=True. Phase: {phase}')
1447
+ if phase == 'Failed':
1448
+ reason_for_debug = _get_pod_termination_reason(pod)
1449
+ logger.debug(f'Termination reason: {reason_for_debug}')
1444
1450
  continue
1445
1451
  reason = None
1446
1452
  if phase == 'Failed':
@@ -13,13 +13,12 @@ from sky.provision.kubernetes import utils as kubernetes_utils
13
13
  from sky.utils import directory_utils
14
14
  from sky.utils import kubernetes_enums
15
15
  from sky.utils import ux_utils
16
+ from sky.utils import yaml_utils
16
17
 
17
18
  if typing.TYPE_CHECKING:
18
19
  import jinja2
19
- import yaml
20
20
  else:
21
21
  jinja2 = adaptors_common.LazyImport('jinja2')
22
- yaml = adaptors_common.LazyImport('yaml')
23
22
 
24
23
  logger = sky_logging.init_logger(__name__)
25
24
 
@@ -108,7 +107,7 @@ def fill_loadbalancer_template(namespace: str, context: Optional[str],
108
107
  annotations=annotations,
109
108
  labels=labels,
110
109
  )
111
- content = yaml.safe_load(cont)
110
+ content = yaml_utils.safe_load(cont)
112
111
  return content
113
112
 
114
113
 
@@ -147,7 +146,7 @@ def fill_ingress_template(namespace: str, context: Optional[str],
147
146
  annotations=annotations,
148
147
  labels=labels,
149
148
  )
150
- content = yaml.safe_load(cont)
149
+ content = yaml_utils.safe_load(cont)
151
150
 
152
151
  # Return a dictionary containing both specs
153
152
  return {
@@ -38,6 +38,7 @@ from sky.utils import schemas
38
38
  from sky.utils import status_lib
39
39
  from sky.utils import timeline
40
40
  from sky.utils import ux_utils
41
+ from sky.utils import yaml_utils
41
42
 
42
43
  if typing.TYPE_CHECKING:
43
44
  import jinja2
@@ -1898,7 +1899,7 @@ def is_kubeconfig_exec_auth(
1898
1899
 
1899
1900
  # Load the kubeconfig for the context
1900
1901
  kubeconfig_text = _get_kubeconfig_text_for_context(context)
1901
- kubeconfig = yaml.safe_load(kubeconfig_text)
1902
+ kubeconfig = yaml_utils.safe_load(kubeconfig_text)
1902
1903
 
1903
1904
  # Get the user details
1904
1905
  user_details = kubeconfig['users']
@@ -2601,7 +2602,7 @@ def fill_ssh_jump_template(ssh_key_secret: str, ssh_jump_image: str,
2601
2602
  image=ssh_jump_image,
2602
2603
  secret=ssh_key_secret,
2603
2604
  service_type=service_type)
2604
- content = yaml.safe_load(cont)
2605
+ content = yaml_utils.safe_load(cont)
2605
2606
  return content
2606
2607
 
2607
2608
 
@@ -2750,7 +2751,7 @@ def combine_pod_config_fields(
2750
2751
  """
2751
2752
  with open(cluster_yaml_path, 'r', encoding='utf-8') as f:
2752
2753
  yaml_content = f.read()
2753
- yaml_obj = yaml.safe_load(yaml_content)
2754
+ yaml_obj = yaml_utils.safe_load(yaml_content)
2754
2755
  # We don't use override_configs in `get_effective_region_config`, as merging
2755
2756
  # the pod config requires special handling.
2756
2757
  if isinstance(cloud, clouds.SSH):
@@ -2795,7 +2796,7 @@ def combine_metadata_fields(cluster_yaml_path: str,
2795
2796
 
2796
2797
  with open(cluster_yaml_path, 'r', encoding='utf-8') as f:
2797
2798
  yaml_content = f.read()
2798
- yaml_obj = yaml.safe_load(yaml_content)
2799
+ yaml_obj = yaml_utils.safe_load(yaml_content)
2799
2800
 
2800
2801
  # Get custom_metadata from global config
2801
2802
  custom_metadata = skypilot_config.get_effective_region_config(
@@ -3689,7 +3690,7 @@ def format_kubeconfig_exec_auth_with_cache(kubeconfig_path: str) -> str:
3689
3690
  """
3690
3691
  # TODO(kyuds): GC cache files
3691
3692
  with open(kubeconfig_path, 'r', encoding='utf-8') as file:
3692
- config = yaml.safe_load(file)
3693
+ config = yaml_utils.safe_load(file)
3693
3694
  normalized = yaml.dump(config, sort_keys=True)
3694
3695
  hashed = hashlib.sha1(normalized.encode('utf-8')).hexdigest()
3695
3696
  path = os.path.expanduser(
@@ -100,15 +100,23 @@ def delete_cluster(name: str, region: str) -> None:
100
100
  def list_instances(project_id: str) -> Dict[str, Dict[str, Any]]:
101
101
  """Lists instances associated with API key."""
102
102
  service = nebius.compute().InstanceServiceClient(nebius.sdk())
103
- result = nebius.sync_call(
104
- service.list(
105
- nebius.compute().ListInstancesRequest(parent_id=project_id),
106
- timeout=nebius.READ_TIMEOUT))
107
-
108
- instances = result
103
+ page_token = ''
104
+ instances = []
105
+ while True:
106
+ result = nebius.sync_call(
107
+ service.list(nebius.compute().ListInstancesRequest(
108
+ parent_id=project_id,
109
+ page_size=100,
110
+ page_token=page_token,
111
+ ),
112
+ timeout=nebius.READ_TIMEOUT))
113
+ instances.extend(result.items)
114
+ if not result.next_page_token: # "" means no more pages
115
+ break
116
+ page_token = result.next_page_token
109
117
 
110
118
  instance_dict: Dict[str, Dict[str, Any]] = {}
111
- for instance in instances.items:
119
+ for instance in instances:
112
120
  info = {}
113
121
  info['status'] = instance.status.state.name
114
122
  info['name'] = instance.metadata.name
@@ -3,12 +3,10 @@
3
3
  import http.cookies as http_cookies
4
4
  import os
5
5
  import ssl
6
- import typing
7
6
  from typing import Any, Dict, List, Optional
8
7
 
9
8
  from sky import exceptions
10
9
  from sky import sky_logging
11
- from sky.adaptors import common as adaptors_common
12
10
  from sky.adaptors import vsphere as vsphere_adaptor
13
11
  from sky.catalog import vsphere_catalog
14
12
  from sky.catalog.common import get_catalog_path
@@ -28,11 +26,7 @@ from sky.provision.vsphere.common.vim_utils import create_spec_with_script
28
26
  from sky.provision.vsphere.common.vim_utils import poweron_vm
29
27
  from sky.provision.vsphere.common.vim_utils import wait_for_tasks
30
28
  from sky.provision.vsphere.common.vim_utils import wait_internal_ip_ready
31
-
32
- if typing.TYPE_CHECKING:
33
- import yaml
34
- else:
35
- yaml = adaptors_common.LazyImport('yaml')
29
+ from sky.utils import yaml_utils
36
30
 
37
31
  logger = sky_logging.init_logger(__name__)
38
32
 
@@ -323,7 +317,7 @@ def get_vsphere_credentials(name=None):
323
317
  assert os.path.exists(
324
318
  credential_path), f'Missing credential file at {credential_path}.'
325
319
  with open(credential_path, 'r', encoding='utf-8') as file:
326
- credential = yaml.safe_load(file)
320
+ credential = yaml_utils.safe_load(file)
327
321
  vcenters = credential['vcenters']
328
322
  if name is None:
329
323
  return vcenters
@@ -1,5 +1,6 @@
1
1
  """Responses for the API server."""
2
2
 
3
+ import enum
3
4
  from typing import Any, Dict, List, Optional
4
5
 
5
6
  import pydantic
@@ -117,3 +118,9 @@ class StatusResponse(ResponseBaseModel):
117
118
  cpus: Optional[str] = None
118
119
  memory: Optional[str] = None
119
120
  accelerators: Optional[str] = None
121
+
122
+
123
+ class UploadStatus(enum.Enum):
124
+ """Status of the upload."""
125
+ UPLOADING = 'uploading'
126
+ COMPLETED = 'completed'
sky/serve/serve_utils.py CHANGED
@@ -20,7 +20,6 @@ import uuid
20
20
 
21
21
  import colorama
22
22
  import filelock
23
- import yaml
24
23
 
25
24
  from sky import backends
26
25
  from sky import exceptions
@@ -43,6 +42,7 @@ from sky.utils import message_utils
43
42
  from sky.utils import resources_utils
44
43
  from sky.utils import status_lib
45
44
  from sky.utils import ux_utils
45
+ from sky.utils import yaml_utils
46
46
 
47
47
  if typing.TYPE_CHECKING:
48
48
  import fastapi
@@ -710,7 +710,7 @@ def _get_service_status(
710
710
  svc.pop('pool', None) # Remove pool from service config
711
711
  original_config['pool'] = svc # Add pool to root config
712
712
  else:
713
- original_config = yaml.safe_load(original_config)
713
+ original_config = yaml_utils.safe_load(original_config)
714
714
  record['pool_yaml'] = common_utils.dump_yaml_str(original_config)
715
715
 
716
716
  record['target_num_replicas'] = 0
sky/serve/service_spec.py CHANGED
@@ -2,11 +2,9 @@
2
2
  import json
3
3
  import os
4
4
  import textwrap
5
- import typing
6
5
  from typing import Any, Dict, List, Optional, Union
7
6
 
8
7
  from sky import serve
9
- from sky.adaptors import common as adaptors_common
10
8
  from sky.serve import constants
11
9
  from sky.serve import load_balancing_policies as lb_policies
12
10
  from sky.serve import serve_utils
@@ -14,11 +12,7 @@ from sky.serve import spot_placer as spot_placer_lib
14
12
  from sky.utils import common_utils
15
13
  from sky.utils import schemas
16
14
  from sky.utils import ux_utils
17
-
18
- if typing.TYPE_CHECKING:
19
- import yaml
20
- else:
21
- yaml = adaptors_common.LazyImport('yaml')
15
+ from sky.utils import yaml_utils
22
16
 
23
17
 
24
18
  class SkyServiceSpec:
@@ -274,7 +268,7 @@ class SkyServiceSpec:
274
268
  @staticmethod
275
269
  def from_yaml(yaml_path: str) -> 'SkyServiceSpec':
276
270
  with open(os.path.expanduser(yaml_path), 'r', encoding='utf-8') as f:
277
- config = yaml.safe_load(f)
271
+ config = yaml_utils.safe_load(f)
278
272
 
279
273
  if isinstance(config, str):
280
274
  with ux_utils.print_exception_no_traceback():
sky/server/auth/authn.py CHANGED
@@ -14,6 +14,10 @@ logger = sky_logging.init_logger(__name__)
14
14
  # TODO(hailong): Remove this function and use request.state.auth_user instead.
15
15
  async def override_user_info_in_request_body(request: fastapi.Request,
16
16
  auth_user: Optional[models.User]):
17
+ # Skip for upload requests to avoid consuming the body prematurely, which
18
+ # will break the streaming upload.
19
+ if request.url.path.startswith('/upload'):
20
+ return
17
21
  if auth_user is None:
18
22
  return
19
23
 
sky/server/common.py CHANGED
@@ -23,6 +23,7 @@ import uuid
23
23
  import cachetools
24
24
  import colorama
25
25
  import filelock
26
+ from passlib import context as passlib_context
26
27
  from typing_extensions import ParamSpec
27
28
 
28
29
  from sky import exceptions
@@ -61,7 +62,7 @@ AVAILABLE_LOCAL_API_SERVER_URLS = [
61
62
 
62
63
  API_SERVER_CMD = '-m sky.server.server'
63
64
  # The client dir on the API server for storing user-specific data, such as file
64
- # mounts, logs, etc. This dir is empheral and will be cleaned up when the API
65
+ # mounts, logs, etc. This dir is ephemeral and will be cleaned up when the API
65
66
  # server is restarted.
66
67
  API_SERVER_CLIENT_DIR = pathlib.Path('~/.sky/api_server/clients')
67
68
  RETRY_COUNT_ON_TIMEOUT = 3
@@ -102,6 +103,11 @@ logger = sky_logging.init_logger(__name__)
102
103
 
103
104
  hinted_for_server_install_version_mismatch = False
104
105
 
106
+ crypt_ctx = passlib_context.CryptContext([
107
+ 'bcrypt', 'sha256_crypt', 'sha512_crypt', 'des_crypt', 'apr_md5_crypt',
108
+ 'ldap_sha1'
109
+ ])
110
+
105
111
 
106
112
  class ApiServerStatus(enum.Enum):
107
113
  HEALTHY = 'healthy'
@@ -275,6 +275,10 @@ def override_request_env_and_config(
275
275
  request_id: str) -> Generator[None, None, None]:
276
276
  """Override the environment and SkyPilot config for a request."""
277
277
  original_env = os.environ.copy()
278
+ # Unset SKYPILOT_DEBUG by default, to avoid the value set on the API server
279
+ # affecting client requests. If set on the client side, it will be
280
+ # overridden by the request body.
281
+ os.environ.pop('SKYPILOT_DEBUG', None)
278
282
  os.environ.update(request_body.env_vars)
279
283
  # Note: may be overridden by AuthProxyMiddleware.
280
284
  # TODO(zhwu): we need to make the entire request a context available to the