skypilot-nightly 1.0.0.dev20250522__py3-none-any.whl → 1.0.0.dev20250524__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 (120) hide show
  1. sky/__init__.py +2 -2
  2. sky/adaptors/kubernetes.py +46 -16
  3. sky/backends/backend_utils.py +62 -45
  4. sky/backends/cloud_vm_ray_backend.py +19 -5
  5. sky/check.py +398 -171
  6. sky/cli.py +302 -98
  7. sky/client/cli.py +302 -98
  8. sky/client/sdk.py +104 -12
  9. sky/clouds/__init__.py +3 -0
  10. sky/clouds/aws.py +4 -2
  11. sky/clouds/azure.py +4 -2
  12. sky/clouds/cloud.py +24 -6
  13. sky/clouds/cudo.py +2 -1
  14. sky/clouds/do.py +2 -1
  15. sky/clouds/fluidstack.py +2 -1
  16. sky/clouds/gcp.py +23 -5
  17. sky/clouds/ibm.py +4 -2
  18. sky/clouds/kubernetes.py +66 -22
  19. sky/clouds/lambda_cloud.py +2 -1
  20. sky/clouds/nebius.py +18 -2
  21. sky/clouds/oci.py +4 -2
  22. sky/clouds/paperspace.py +2 -1
  23. sky/clouds/runpod.py +2 -1
  24. sky/clouds/scp.py +2 -1
  25. sky/clouds/service_catalog/constants.py +1 -1
  26. sky/clouds/service_catalog/ssh_catalog.py +167 -0
  27. sky/clouds/ssh.py +203 -0
  28. sky/clouds/vast.py +2 -1
  29. sky/clouds/vsphere.py +2 -1
  30. sky/core.py +58 -11
  31. sky/dashboard/out/404.html +1 -1
  32. sky/dashboard/out/_next/static/aHej19bZyl4hoHgrzPCn7/_buildManifest.js +1 -0
  33. sky/dashboard/out/_next/static/chunks/480-ee58038f1a4afd5c.js +1 -0
  34. sky/dashboard/out/_next/static/chunks/488-50d843fdb5396d32.js +15 -0
  35. sky/dashboard/out/_next/static/chunks/498-d7722313e5e5b4e6.js +21 -0
  36. sky/dashboard/out/_next/static/chunks/573-f17bd89d9f9118b3.js +66 -0
  37. sky/dashboard/out/_next/static/chunks/578-7a4795009a56430c.js +6 -0
  38. sky/dashboard/out/_next/static/chunks/734-5f5ce8f347b7f417.js +1 -0
  39. sky/dashboard/out/_next/static/chunks/937.f97f83652028e944.js +1 -0
  40. sky/dashboard/out/_next/static/chunks/938-f347f6144075b0c8.js +1 -0
  41. sky/dashboard/out/_next/static/chunks/9f96d65d-5a3e4af68c26849e.js +1 -0
  42. sky/dashboard/out/_next/static/chunks/pages/_app-dec800f9ef1b10f4.js +1 -0
  43. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-37c042a356f8e608.js +1 -0
  44. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-9529d9e882a0e75c.js +16 -0
  45. sky/dashboard/out/_next/static/chunks/pages/clusters-9e6d1ec6e1ac5b29.js +1 -0
  46. sky/dashboard/out/_next/static/chunks/pages/infra-e690d864aa00e2ea.js +1 -0
  47. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-db6558a5ec687011.js +1 -0
  48. sky/dashboard/out/_next/static/chunks/pages/jobs-73d5e0c369d00346.js +16 -0
  49. sky/dashboard/out/_next/static/chunks/pages/users-2d319455c3f1c3e2.js +1 -0
  50. sky/dashboard/out/_next/static/chunks/pages/workspaces-02a7b60f2ead275f.js +1 -0
  51. sky/dashboard/out/_next/static/chunks/webpack-deda68c926e8d0bc.js +1 -0
  52. sky/dashboard/out/_next/static/css/d2cdba64c9202dd7.css +3 -0
  53. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  54. sky/dashboard/out/clusters/[cluster].html +1 -1
  55. sky/dashboard/out/clusters.html +1 -1
  56. sky/dashboard/out/index.html +1 -1
  57. sky/dashboard/out/infra.html +1 -1
  58. sky/dashboard/out/jobs/[job].html +1 -1
  59. sky/dashboard/out/jobs.html +1 -1
  60. sky/dashboard/out/users.html +1 -0
  61. sky/dashboard/out/workspaces.html +1 -0
  62. sky/data/storage.py +1 -1
  63. sky/global_user_state.py +42 -19
  64. sky/jobs/constants.py +1 -1
  65. sky/jobs/server/core.py +72 -56
  66. sky/jobs/state.py +26 -5
  67. sky/jobs/utils.py +65 -13
  68. sky/optimizer.py +29 -7
  69. sky/provision/__init__.py +1 -0
  70. sky/provision/aws/instance.py +17 -1
  71. sky/provision/fluidstack/instance.py +1 -0
  72. sky/provision/kubernetes/instance.py +16 -5
  73. sky/provision/kubernetes/utils.py +37 -19
  74. sky/provision/nebius/instance.py +3 -1
  75. sky/provision/nebius/utils.py +14 -2
  76. sky/provision/ssh/__init__.py +18 -0
  77. sky/resources.py +4 -1
  78. sky/serve/server/core.py +9 -6
  79. sky/server/html/token_page.html +6 -1
  80. sky/server/requests/executor.py +1 -0
  81. sky/server/requests/payloads.py +18 -0
  82. sky/server/server.py +108 -5
  83. sky/setup_files/dependencies.py +1 -0
  84. sky/skylet/constants.py +4 -1
  85. sky/skypilot_config.py +83 -9
  86. sky/templates/nebius-ray.yml.j2 +12 -0
  87. sky/utils/cli_utils/status_utils.py +18 -8
  88. sky/utils/infra_utils.py +21 -1
  89. sky/utils/kubernetes/cleanup-tunnel.sh +62 -0
  90. sky/utils/kubernetes/create_cluster.sh +1 -0
  91. sky/utils/kubernetes/deploy_remote_cluster.py +1440 -0
  92. sky/utils/kubernetes/kubernetes_deploy_utils.py +117 -10
  93. sky/utils/kubernetes/ssh-tunnel.sh +387 -0
  94. sky/utils/log_utils.py +218 -1
  95. sky/utils/schemas.py +75 -0
  96. sky/utils/ux_utils.py +2 -1
  97. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/METADATA +6 -1
  98. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/RECORD +103 -91
  99. sky/dashboard/out/_next/static/CzOVV6JpRQBRt5GhZuhyK/_buildManifest.js +0 -1
  100. sky/dashboard/out/_next/static/chunks/236-1a3a9440417720eb.js +0 -6
  101. sky/dashboard/out/_next/static/chunks/312-c3c8845990db8ffc.js +0 -15
  102. sky/dashboard/out/_next/static/chunks/37-d584022b0da4ac3b.js +0 -6
  103. sky/dashboard/out/_next/static/chunks/393-e1eaa440481337ec.js +0 -1
  104. sky/dashboard/out/_next/static/chunks/480-f28cd152a98997de.js +0 -1
  105. sky/dashboard/out/_next/static/chunks/582-683f4f27b81996dc.js +0 -59
  106. sky/dashboard/out/_next/static/chunks/pages/_app-8cfab319f9fb3ae8.js +0 -1
  107. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33bc2bec322249b1.js +0 -1
  108. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-e2fc2dd1955e6c36.js +0 -1
  109. sky/dashboard/out/_next/static/chunks/pages/clusters-3a748bd76e5c2984.js +0 -1
  110. sky/dashboard/out/_next/static/chunks/pages/infra-9180cd91cee64b96.js +0 -1
  111. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-70756c2dad850a7e.js +0 -1
  112. sky/dashboard/out/_next/static/chunks/pages/jobs-ecd804b9272f4a7c.js +0 -1
  113. sky/dashboard/out/_next/static/chunks/webpack-830f59b8404e96b8.js +0 -1
  114. sky/dashboard/out/_next/static/css/7e7ce4ff31d3977b.css +0 -3
  115. sky/utils/kubernetes/deploy_remote_cluster.sh +0 -308
  116. /sky/dashboard/out/_next/static/{CzOVV6JpRQBRt5GhZuhyK → aHej19bZyl4hoHgrzPCn7}/_ssgManifest.js +0 -0
  117. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/WHEEL +0 -0
  118. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/entry_points.txt +0 -0
  119. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/licenses/LICENSE +0 -0
  120. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/top_level.txt +0 -0
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>Users | SkyPilot Dashboard</title><link rel="preload" href="/dashboard/skypilot.svg" as="image" fetchpriority="high"/><meta name="next-head-count" content="4"/><link rel="preload" href="/dashboard/_next/static/css/d2cdba64c9202dd7.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/d2cdba64c9202dd7.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-deda68c926e8d0bc.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-87d061ee6ed71b28.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-e0e2335212e72357.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-dec800f9ef1b10f4.js" defer=""></script><script src="/dashboard/_next/static/chunks/573-f17bd89d9f9118b3.js" defer=""></script><script src="/dashboard/_next/static/chunks/480-ee58038f1a4afd5c.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/users-2d319455c3f1c3e2.js" defer=""></script><script src="/dashboard/_next/static/aHej19bZyl4hoHgrzPCn7/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/aHej19bZyl4hoHgrzPCn7/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div class="min-h-screen bg-gray-50"><div class="fixed top-0 left-0 right-0 z-50 shadow-sm"><div class="fixed top-0 left-0 right-0 bg-white z-30 h-14 px-4 border-b border-gray-200 shadow-sm"><div class="flex items-center h-full"><div class="flex items-center space-x-4 mr-6"><a class="flex items-center px-1 pt-1 h-full" href="/dashboard"><div class="h-20 w-20 flex items-center justify-center"><img alt="SkyPilot Logo" fetchpriority="high" width="80" height="80" decoding="async" data-nimg="1" class="w-full h-full object-contain" style="color:transparent" src="/dashboard/skypilot.svg"/></div></a></div><div class="flex items-center space-x-2 md:space-x-4 mr-6"><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/clusters"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"></rect><rect width="20" height="8" x="2" y="14" rx="2" ry="2"></rect><line x1="6" x2="6.01" y1="6" y2="6"></line><line x1="6" x2="6.01" y1="18" y2="18"></line></svg><span>Clusters</span></a><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/jobs"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path><rect width="20" height="14" x="2" y="6" rx="2"></rect></svg><span>Jobs</span></a><div class="border-l border-gray-200 h-6 mx-1"></div><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/infra"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect><rect x="9" y="9" width="6" height="6"></rect><line x1="9" y1="1" x2="9" y2="4"></line><line x1="15" y1="1" x2="15" y2="4"></line><line x1="9" y1="20" x2="9" y2="23"></line><line x1="15" y1="20" x2="15" y2="23"></line><line x1="20" y1="9" x2="23" y2="9"></line><line x1="20" y1="14" x2="23" y2="14"></line><line x1="1" y1="9" x2="4" y2="9"></line><line x1="1" y1="14" x2="4" y2="14"></line></svg><span>Infra</span></a><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/workspaces"><svg class="w-4 h-4" stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g><path fill="none" d="M0 0h24v24H0z"></path><path d="M3 18.5V5a3 3 0 0 1 3-3h14a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5A3.5 3.5 0 0 1 3 18.5zM19 20v-3H6.5a1.5 1.5 0 0 0 0 3H19zM10 4H6a1 1 0 0 0-1 1v10.337A3.486 3.486 0 0 1 6.5 15H19V4h-2v8l-3.5-2-3.5 2V4z"></path></g></svg><span>Workspaces</span></a><a class="inline-flex items-center border-b-2 border-transparent text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/users"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users w-4 h-4"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg><span>Users</span></a></div><div class="flex items-center space-x-1 ml-auto"><a href="https://skypilot.readthedocs.io/en/latest/" target="_blank" rel="noopener noreferrer" class="inline-flex items-center px-2 py-1 text-gray-600 hover:text-blue-600 transition-colors duration-150 cursor-pointer" title="Docs" tabindex="0"><span class="mr-1">Docs</span><svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg></a><div class="border-l border-gray-200 h-6 mx-1"></div><a href="https://github.com/skypilot-org/skypilot" target="_blank" rel="noopener noreferrer" class="inline-flex items-center justify-center p-2 rounded-full text-gray-600 hover:bg-gray-100 transition-colors duration-150 cursor-pointer" title="GitHub" tabindex="0"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"></path></svg></a><a href="https://slack.skypilot.co/" target="_blank" rel="noopener noreferrer" class="inline-flex items-center justify-center p-2 rounded-full text-gray-600 hover:bg-gray-100 transition-colors duration-150 cursor-pointer" title="Slack" tabindex="0"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path transform="scale(0.85) translate(1.8, 1.8)" d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z"></path></svg></a><a href="https://github.com/skypilot-org/skypilot/issues/new" target="_blank" rel="noopener noreferrer" class="inline-flex items-center justify-center p-2 rounded-full text-gray-600 hover:bg-gray-100 transition-colors duration-150 cursor-pointer" title="Leave Feedback" tabindex="0"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-square w-5 h-5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg></a></div></div></div></div><div class="transition-all duration-200 ease-in-out min-h-screen" style="padding-top:56px"><main class="p-6"><div class="flex items-center justify-between mb-4 h-5"><div class="text-base"><a class="text-sky-blue hover:underline leading-none" href="/dashboard/users">Users</a></div><div class="flex items-center"><button class="justify-center whitespace-nowrap rounded-md font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent h-10 text-sky-blue hover:text-sky-blue-bright flex items-center px-3 py-1.5 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-cw h-4 w-4 mr-1.5"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path><path d="M21 3v5h-5"></path></svg><span>Refresh</span></button></div></div><div class="flex justify-center items-center h-64"><style data-emotion="css z01bqi animation-61bdi0">.css-z01bqi{display:inline-block;color:#1976d2;-webkit-animation:animation-61bdi0 1.4s linear infinite;animation:animation-61bdi0 1.4s linear infinite;}@-webkit-keyframes animation-61bdi0{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation-61bdi0{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}</style><span class="MuiCircularProgress-root MuiCircularProgress-indeterminate MuiCircularProgress-colorPrimary css-z01bqi" style="width:40px;height:40px" role="progressbar"><style data-emotion="css 13o7eu2">.css-13o7eu2{display:block;}</style><svg class="MuiCircularProgress-svg css-13o7eu2" viewBox="22 22 44 44"><style data-emotion="css 14891ef animation-1p2h4ri">.css-14891ef{stroke:currentColor;stroke-dasharray:80px,200px;stroke-dashoffset:0;-webkit-animation:animation-1p2h4ri 1.4s ease-in-out infinite;animation:animation-1p2h4ri 1.4s ease-in-out infinite;}@-webkit-keyframes animation-1p2h4ri{0%{stroke-dasharray:1px,200px;stroke-dashoffset:0;}50%{stroke-dasharray:100px,200px;stroke-dashoffset:-15px;}100%{stroke-dasharray:100px,200px;stroke-dashoffset:-125px;}}@keyframes animation-1p2h4ri{0%{stroke-dasharray:1px,200px;stroke-dashoffset:0;}50%{stroke-dasharray:100px,200px;stroke-dashoffset:-15px;}100%{stroke-dasharray:100px,200px;stroke-dashoffset:-125px;}}</style><circle class="MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate css-14891ef" cx="44" cy="44" r="20.2" fill="none" stroke-width="3.6"></circle></svg></span></div></main></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/users","query":{},"buildId":"aHej19bZyl4hoHgrzPCn7","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><link rel="preload" href="/dashboard/skypilot.svg" as="image" fetchpriority="high"/><meta name="next-head-count" content="3"/><link rel="preload" href="/dashboard/_next/static/css/d2cdba64c9202dd7.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/d2cdba64c9202dd7.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-deda68c926e8d0bc.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-87d061ee6ed71b28.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-e0e2335212e72357.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-dec800f9ef1b10f4.js" defer=""></script><script src="/dashboard/_next/static/chunks/9f96d65d-5a3e4af68c26849e.js" defer=""></script><script src="/dashboard/_next/static/chunks/573-f17bd89d9f9118b3.js" defer=""></script><script src="/dashboard/_next/static/chunks/488-50d843fdb5396d32.js" defer=""></script><script src="/dashboard/_next/static/chunks/498-d7722313e5e5b4e6.js" defer=""></script><script src="/dashboard/_next/static/chunks/480-ee58038f1a4afd5c.js" defer=""></script><script src="/dashboard/_next/static/chunks/734-5f5ce8f347b7f417.js" defer=""></script><script src="/dashboard/_next/static/chunks/938-f347f6144075b0c8.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces-02a7b60f2ead275f.js" defer=""></script><script src="/dashboard/_next/static/aHej19bZyl4hoHgrzPCn7/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/aHej19bZyl4hoHgrzPCn7/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div class="min-h-screen bg-gray-50"><div class="fixed top-0 left-0 right-0 z-50 shadow-sm"><div class="fixed top-0 left-0 right-0 bg-white z-30 h-14 px-4 border-b border-gray-200 shadow-sm"><div class="flex items-center h-full"><div class="flex items-center space-x-4 mr-6"><a class="flex items-center px-1 pt-1 h-full" href="/dashboard"><div class="h-20 w-20 flex items-center justify-center"><img alt="SkyPilot Logo" fetchpriority="high" width="80" height="80" decoding="async" data-nimg="1" class="w-full h-full object-contain" style="color:transparent" src="/dashboard/skypilot.svg"/></div></a></div><div class="flex items-center space-x-2 md:space-x-4 mr-6"><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/clusters"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"></rect><rect width="20" height="8" x="2" y="14" rx="2" ry="2"></rect><line x1="6" x2="6.01" y1="6" y2="6"></line><line x1="6" x2="6.01" y1="18" y2="18"></line></svg><span>Clusters</span></a><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/jobs"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path><rect width="20" height="14" x="2" y="6" rx="2"></rect></svg><span>Jobs</span></a><div class="border-l border-gray-200 h-6 mx-1"></div><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/infra"><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect><rect x="9" y="9" width="6" height="6"></rect><line x1="9" y1="1" x2="9" y2="4"></line><line x1="15" y1="1" x2="15" y2="4"></line><line x1="9" y1="20" x2="9" y2="23"></line><line x1="15" y1="20" x2="15" y2="23"></line><line x1="20" y1="9" x2="23" y2="9"></line><line x1="20" y1="14" x2="23" y2="14"></line><line x1="1" y1="9" x2="4" y2="9"></line><line x1="1" y1="14" x2="4" y2="14"></line></svg><span>Infra</span></a><a class="inline-flex items-center border-b-2 border-transparent text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/workspaces"><svg class="w-4 h-4" stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g><path fill="none" d="M0 0h24v24H0z"></path><path d="M3 18.5V5a3 3 0 0 1 3-3h14a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5A3.5 3.5 0 0 1 3 18.5zM19 20v-3H6.5a1.5 1.5 0 0 0 0 3H19zM10 4H6a1 1 0 0 0-1 1v10.337A3.486 3.486 0 0 1 6.5 15H19V4h-2v8l-3.5-2-3.5 2V4z"></path></g></svg><span>Workspaces</span></a><a class="inline-flex items-center border-b-2 border-transparent hover:text-blue-600 px-1 pt-1 space-x-2" href="/dashboard/users"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users w-4 h-4"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg><span>Users</span></a></div><div class="flex items-center space-x-1 ml-auto"><a href="https://skypilot.readthedocs.io/en/latest/" target="_blank" rel="noopener noreferrer" class="inline-flex items-center px-2 py-1 text-gray-600 hover:text-blue-600 transition-colors duration-150 cursor-pointer" title="Docs" tabindex="0"><span class="mr-1">Docs</span><svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg></a><div class="border-l border-gray-200 h-6 mx-1"></div><a href="https://github.com/skypilot-org/skypilot" target="_blank" rel="noopener noreferrer" class="inline-flex items-center justify-center p-2 rounded-full text-gray-600 hover:bg-gray-100 transition-colors duration-150 cursor-pointer" title="GitHub" tabindex="0"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"></path></svg></a><a href="https://slack.skypilot.co/" target="_blank" rel="noopener noreferrer" class="inline-flex items-center justify-center p-2 rounded-full text-gray-600 hover:bg-gray-100 transition-colors duration-150 cursor-pointer" title="Slack" tabindex="0"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path transform="scale(0.85) translate(1.8, 1.8)" d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z"></path></svg></a><a href="https://github.com/skypilot-org/skypilot/issues/new" target="_blank" rel="noopener noreferrer" class="inline-flex items-center justify-center p-2 rounded-full text-gray-600 hover:bg-gray-100 transition-colors duration-150 cursor-pointer" title="Leave Feedback" tabindex="0"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-square w-5 h-5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg></a></div></div></div></div><div class="transition-all duration-200 ease-in-out min-h-screen" style="padding-top:56px"><main class="p-6"><div class="flex justify-center items-center h-64"><style data-emotion="css z01bqi animation-61bdi0">.css-z01bqi{display:inline-block;color:#1976d2;-webkit-animation:animation-61bdi0 1.4s linear infinite;animation:animation-61bdi0 1.4s linear infinite;}@-webkit-keyframes animation-61bdi0{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation-61bdi0{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}</style><span class="MuiCircularProgress-root MuiCircularProgress-indeterminate MuiCircularProgress-colorPrimary css-z01bqi" style="width:40px;height:40px" role="progressbar"><style data-emotion="css 13o7eu2">.css-13o7eu2{display:block;}</style><svg class="MuiCircularProgress-svg css-13o7eu2" viewBox="22 22 44 44"><style data-emotion="css 14891ef animation-1p2h4ri">.css-14891ef{stroke:currentColor;stroke-dasharray:80px,200px;stroke-dashoffset:0;-webkit-animation:animation-1p2h4ri 1.4s ease-in-out infinite;animation:animation-1p2h4ri 1.4s ease-in-out infinite;}@-webkit-keyframes animation-1p2h4ri{0%{stroke-dasharray:1px,200px;stroke-dashoffset:0;}50%{stroke-dasharray:100px,200px;stroke-dashoffset:-15px;}100%{stroke-dasharray:100px,200px;stroke-dashoffset:-125px;}}@keyframes animation-1p2h4ri{0%{stroke-dasharray:1px,200px;stroke-dashoffset:0;}50%{stroke-dasharray:100px,200px;stroke-dashoffset:-15px;}100%{stroke-dasharray:100px,200px;stroke-dashoffset:-125px;}}</style><circle class="MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate css-14891ef" cx="44" cy="44" r="20.2" fill="none" stroke-width="3.6"></circle></svg></span><span class="ml-2 text-gray-500">Loading workspace data...</span></div></main></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces","query":{},"buildId":"aHej19bZyl4hoHgrzPCn7","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
sky/data/storage.py CHANGED
@@ -109,7 +109,7 @@ def _is_storage_cloud_enabled(cloud_name: str,
109
109
  sky_check.check_capability(
110
110
  sky_cloud.CloudCapability.STORAGE,
111
111
  quiet=True,
112
- )
112
+ workspace=skypilot_config.get_active_workspace())
113
113
  return _is_storage_cloud_enabled(cloud_name,
114
114
  try_fix_with_sky_check=False)
115
115
  return False
sky/global_user_state.py CHANGED
@@ -18,6 +18,7 @@ import uuid
18
18
 
19
19
  from sky import models
20
20
  from sky import sky_logging
21
+ from sky.skylet import constants
21
22
  from sky.utils import common_utils
22
23
  from sky.utils import context_utils
23
24
  from sky.utils import db_utils
@@ -70,7 +71,8 @@ def create_table(cursor, conn):
70
71
  cluster_ever_up INTEGER DEFAULT 0,
71
72
  status_updated_at INTEGER DEFAULT null,
72
73
  config_hash TEXT DEFAULT null,
73
- user_hash TEXT DEFAULT null)""")
74
+ user_hash TEXT DEFAULT null,
75
+ workspace TEXT DEFAULT 'default')""")
74
76
 
75
77
  # Table for Cluster History
76
78
  # usage_intervals: List[Tuple[int, int]]
@@ -164,6 +166,14 @@ def create_table(cursor, conn):
164
166
 
165
167
  db_utils.add_column_to_table(cursor, conn, 'cluster_history', 'user_hash',
166
168
  'TEXT DEFAULT null')
169
+
170
+ db_utils.add_column_to_table(
171
+ cursor,
172
+ conn,
173
+ 'clusters',
174
+ 'workspace',
175
+ 'TEXT DEFAULT \'default\'',
176
+ value_to_replace_existing_entries=constants.SKYPILOT_DEFAULT_WORKSPACE)
167
177
  conn.commit()
168
178
 
169
179
 
@@ -209,6 +219,9 @@ def add_or_update_cluster(cluster_name: str,
209
219
  is_launch: if the cluster is firstly launched. If True, the launched_at
210
220
  and last_use will be updated. Otherwise, use the old value.
211
221
  """
222
+ # TODO(zhwu): have to be imported here to avoid circular import.
223
+ from sky import skypilot_config # pylint: disable=import-outside-toplevel
224
+
212
225
  # FIXME: launched_at will be changed when `sky launch -c` is called.
213
226
  handle = pickle.dumps(cluster_handle)
214
227
  cluster_launched_at = int(time.time()) if is_launch else None
@@ -242,6 +255,7 @@ def add_or_update_cluster(cluster_name: str,
242
255
  usage_intervals.append((cluster_launched_at, None))
243
256
 
244
257
  user_hash = common_utils.get_user_hash()
258
+ active_workspace = skypilot_config.get_active_workspace()
245
259
 
246
260
  _DB.cursor.execute(
247
261
  'INSERT or REPLACE INTO clusters'
@@ -252,7 +266,7 @@ def add_or_update_cluster(cluster_name: str,
252
266
  '(name, launched_at, handle, last_use, status, '
253
267
  'autostop, to_down, metadata, owner, cluster_hash, '
254
268
  'storage_mounts_metadata, cluster_ever_up, status_updated_at, '
255
- 'config_hash, user_hash) '
269
+ 'config_hash, user_hash, workspace) '
256
270
  'VALUES ('
257
271
  # name
258
272
  '?, '
@@ -295,8 +309,9 @@ def add_or_update_cluster(cluster_name: str,
295
309
  # config_hash
296
310
  'COALESCE(?, (SELECT config_hash FROM clusters WHERE name=?)),'
297
311
  # user_hash: keep original user_hash if it exists
298
- 'COALESCE('
299
- '(SELECT user_hash FROM clusters WHERE name=?), ?)'
312
+ 'COALESCE((SELECT user_hash FROM clusters WHERE name=?), ?),'
313
+ # keep original workspace if it exists
314
+ 'COALESCE((SELECT workspace FROM clusters WHERE name=?), ?)'
300
315
  ')',
301
316
  (
302
317
  # name
@@ -336,6 +351,9 @@ def add_or_update_cluster(cluster_name: str,
336
351
  # user_hash
337
352
  cluster_name,
338
353
  user_hash,
354
+ # workspace
355
+ cluster_name,
356
+ active_workspace,
339
357
  ))
340
358
 
341
359
  launched_nodes = getattr(cluster_handle, 'launched_nodes', None)
@@ -678,7 +696,7 @@ def get_cluster_from_name(
678
696
  rows = _DB.cursor.execute(
679
697
  'SELECT name, launched_at, handle, last_use, status, autostop, '
680
698
  'metadata, to_down, owner, cluster_hash, storage_mounts_metadata, '
681
- 'cluster_ever_up, status_updated_at, config_hash, user_hash '
699
+ 'cluster_ever_up, status_updated_at, config_hash, user_hash, workspace '
682
700
  'FROM clusters WHERE name=(?)', (cluster_name,)).fetchall()
683
701
  for row in rows:
684
702
  # Explicitly specify the number of fields to unpack, so that
@@ -686,7 +704,7 @@ def get_cluster_from_name(
686
704
  # breaking the previous code.
687
705
  (name, launched_at, handle, last_use, status, autostop, metadata,
688
706
  to_down, owner, cluster_hash, storage_mounts_metadata, cluster_ever_up,
689
- status_updated_at, config_hash, user_hash) = row
707
+ status_updated_at, config_hash, user_hash, workspace) = row
690
708
  user_hash = _get_user_hash_or_current_user(user_hash)
691
709
  # TODO: use namedtuple instead of dict
692
710
  record = {
@@ -707,6 +725,7 @@ def get_cluster_from_name(
707
725
  'user_hash': user_hash,
708
726
  'user_name': get_user(user_hash).name,
709
727
  'config_hash': config_hash,
728
+ 'workspace': workspace,
710
729
  }
711
730
  return record
712
731
  return None
@@ -716,13 +735,13 @@ def get_clusters() -> List[Dict[str, Any]]:
716
735
  rows = _DB.cursor.execute(
717
736
  'select name, launched_at, handle, last_use, status, autostop, '
718
737
  'metadata, to_down, owner, cluster_hash, storage_mounts_metadata, '
719
- 'cluster_ever_up, status_updated_at, config_hash, user_hash '
738
+ 'cluster_ever_up, status_updated_at, config_hash, user_hash, workspace '
720
739
  'from clusters order by launched_at desc').fetchall()
721
740
  records = []
722
741
  for row in rows:
723
742
  (name, launched_at, handle, last_use, status, autostop, metadata,
724
743
  to_down, owner, cluster_hash, storage_mounts_metadata, cluster_ever_up,
725
- status_updated_at, config_hash, user_hash) = row
744
+ status_updated_at, config_hash, user_hash, workspace) = row
726
745
  user_hash = _get_user_hash_or_current_user(user_hash)
727
746
  # TODO: use namedtuple instead of dict
728
747
  record = {
@@ -743,6 +762,7 @@ def get_clusters() -> List[Dict[str, Any]]:
743
762
  'user_hash': user_hash,
744
763
  'user_name': get_user(user_hash).name,
745
764
  'config_hash': config_hash,
765
+ 'workspace': workspace,
746
766
  }
747
767
 
748
768
  records.append(record)
@@ -804,11 +824,12 @@ def get_cluster_names_start_with(starts_with: str) -> List[str]:
804
824
  return [row[0] for row in rows]
805
825
 
806
826
 
807
- def get_cached_enabled_clouds(
808
- cloud_capability: 'cloud.CloudCapability') -> List['clouds.Cloud']:
809
-
810
- rows = _DB.cursor.execute('SELECT value FROM config WHERE key = ?',
811
- (_get_capability_key(cloud_capability),))
827
+ def get_cached_enabled_clouds(cloud_capability: 'cloud.CloudCapability',
828
+ workspace: str) -> List['clouds.Cloud']:
829
+ # The table contains the cached enabled clouds for each workspace.
830
+ rows = _DB.cursor.execute(
831
+ 'SELECT value FROM config WHERE key = ?',
832
+ (_get_enabled_clouds_key(cloud_capability, workspace),))
812
833
  ret = []
813
834
  for (value,) in rows:
814
835
  ret = json.loads(value)
@@ -829,15 +850,17 @@ def get_cached_enabled_clouds(
829
850
 
830
851
 
831
852
  def set_enabled_clouds(enabled_clouds: List[str],
832
- cloud_capability: 'cloud.CloudCapability') -> None:
833
- _DB.cursor.execute(
834
- 'INSERT OR REPLACE INTO config VALUES (?, ?)',
835
- (_get_capability_key(cloud_capability), json.dumps(enabled_clouds)))
853
+ cloud_capability: 'cloud.CloudCapability',
854
+ workspace: str) -> None:
855
+ _DB.cursor.execute('INSERT OR REPLACE INTO config VALUES (?, ?)',
856
+ (_get_enabled_clouds_key(cloud_capability, workspace),
857
+ json.dumps(enabled_clouds)))
836
858
  _DB.conn.commit()
837
859
 
838
860
 
839
- def _get_capability_key(cloud_capability: 'cloud.CloudCapability') -> str:
840
- return _ENABLED_CLOUDS_KEY_PREFIX + cloud_capability.value
861
+ def _get_enabled_clouds_key(cloud_capability: 'cloud.CloudCapability',
862
+ workspace: str) -> str:
863
+ return _ENABLED_CLOUDS_KEY_PREFIX + workspace + '_' + cloud_capability.value
841
864
 
842
865
 
843
866
  def add_or_update_storage(storage_name: str,
sky/jobs/constants.py CHANGED
@@ -47,7 +47,7 @@ JOBS_CLUSTER_NAME_PREFIX_LENGTH = 25
47
47
  # The version of the lib files that jobs/utils use. Whenever there is an API
48
48
  # change for the jobs/utils, we need to bump this version and update
49
49
  # job.utils.ManagedJobCodeGen to handle the version update.
50
- MANAGED_JOBS_VERSION = 3
50
+ MANAGED_JOBS_VERSION = 4
51
51
 
52
52
  # The command for setting up the jobs dashboard on the controller. It firstly
53
53
  # checks if the systemd services are available, and if not (e.g., Kubernetes
sky/jobs/server/core.py CHANGED
@@ -17,6 +17,7 @@ from sky import execution
17
17
  from sky import global_user_state
18
18
  from sky import provision as provision_lib
19
19
  from sky import sky_logging
20
+ from sky import skypilot_config
20
21
  from sky import task as task_lib
21
22
  from sky.backends import backend_utils
22
23
  from sky.clouds.service_catalog import common as service_catalog_common
@@ -207,7 +208,7 @@ def launch(
207
208
 
208
209
  controller_task.managed_job_dag = dag
209
210
 
210
- sky_logging.print(
211
+ logger.info(
211
212
  f'{colorama.Fore.YELLOW}'
212
213
  f'Launching managed job {dag.name!r} from jobs controller...'
213
214
  f'{colorama.Style.RESET_ALL}')
@@ -215,12 +216,20 @@ def launch(
215
216
  # Launch with the api server's user hash, so that sky status does not
216
217
  # show the owner of the controller as whatever user launched it first.
217
218
  with common.with_server_user_hash():
218
- return execution.launch(task=controller_task,
219
- cluster_name=controller_name,
220
- stream_logs=stream_logs,
221
- retry_until_up=True,
222
- fast=True,
223
- _disable_controller_check=True)
219
+ # Always launch the controller in the default workspace.
220
+ with skypilot_config.local_active_workspace_ctx(
221
+ skylet_constants.SKYPILOT_DEFAULT_WORKSPACE):
222
+ # TODO(zhwu): the buckets need to be correctly handled for
223
+ # a specific workspace. For example, if a job is launched in
224
+ # workspace A, but the controller is in workspace B, the
225
+ # intermediate bucket and newly created bucket should be in
226
+ # workspace A.
227
+ return execution.launch(task=controller_task,
228
+ cluster_name=controller_name,
229
+ stream_logs=stream_logs,
230
+ retry_until_up=True,
231
+ fast=True,
232
+ _disable_controller_check=True)
224
233
 
225
234
 
226
235
  def queue_from_kubernetes_pod(
@@ -318,14 +327,17 @@ def _maybe_restart_controller(
318
327
  if handle is not None:
319
328
  return handle
320
329
 
321
- sky_logging.print(f'{colorama.Fore.YELLOW}'
322
- f'Restarting {jobs_controller_type.value.name}...'
323
- f'{colorama.Style.RESET_ALL}')
330
+ logger.info(f'{colorama.Fore.YELLOW}'
331
+ f'Restarting {jobs_controller_type.value.name}...'
332
+ f'{colorama.Style.RESET_ALL}')
324
333
 
325
334
  rich_utils.force_update_status(
326
335
  ux_utils.spinner_message(f'{spinner_message} - restarting '
327
336
  'controller'))
328
- handle = core.start(cluster_name=jobs_controller_type.value.cluster_name)
337
+ with skypilot_config.local_active_workspace_ctx(
338
+ skylet_constants.SKYPILOT_DEFAULT_WORKSPACE):
339
+ handle = core.start(
340
+ cluster_name=jobs_controller_type.value.cluster_name)
329
341
  # Make sure the dashboard is running when the controller is restarted.
330
342
  # We should not directly use execution.launch() and have the dashboard cmd
331
343
  # in the task setup because since we are using detached_setup, it will
@@ -440,52 +452,56 @@ def cancel(name: Optional[str] = None,
440
452
  sky.exceptions.ClusterNotUpError: the jobs controller is not up.
441
453
  RuntimeError: failed to cancel the job.
442
454
  """
443
- job_ids = [] if job_ids is None else job_ids
444
- handle = backend_utils.is_controller_accessible(
445
- controller=controller_utils.Controllers.JOBS_CONTROLLER,
446
- stopped_message='All managed jobs should have finished.')
447
-
448
- job_id_str = ','.join(map(str, job_ids))
449
- if sum([bool(job_ids), name is not None, all or all_users]) != 1:
450
- arguments = []
451
- arguments += [f'job_ids={job_id_str}'] if job_ids else []
452
- arguments += [f'name={name}'] if name is not None else []
453
- arguments += ['all'] if all else []
454
- arguments += ['all_users'] if all_users else []
455
- with ux_utils.print_exception_no_traceback():
456
- raise ValueError('Can only specify one of JOB_IDS, name, or all/'
457
- f'all_users. Provided {" ".join(arguments)!r}.')
458
-
459
- backend = backend_utils.get_backend_from_handle(handle)
460
- assert isinstance(backend, backends.CloudVmRayBackend)
461
- if all_users:
462
- code = managed_job_utils.ManagedJobCodeGen.cancel_jobs_by_id(
463
- None, all_users=True)
464
- elif all:
465
- code = managed_job_utils.ManagedJobCodeGen.cancel_jobs_by_id(None)
466
- elif job_ids:
467
- code = managed_job_utils.ManagedJobCodeGen.cancel_jobs_by_id(job_ids)
468
- else:
469
- assert name is not None, (job_ids, name, all)
470
- code = managed_job_utils.ManagedJobCodeGen.cancel_job_by_name(name)
471
- # The stderr is redirected to stdout
472
- returncode, stdout, _ = backend.run_on_head(handle,
473
- code,
474
- require_outputs=True,
475
- stream_logs=False)
476
- try:
477
- subprocess_utils.handle_returncode(returncode, code,
478
- 'Failed to cancel managed job',
479
- stdout)
480
- except exceptions.CommandError as e:
481
- with ux_utils.print_exception_no_traceback():
482
- raise RuntimeError(e.error_msg) from e
455
+ with rich_utils.safe_status(
456
+ ux_utils.spinner_message('Cancelling managed jobs')):
457
+ job_ids = [] if job_ids is None else job_ids
458
+ handle = backend_utils.is_controller_accessible(
459
+ controller=controller_utils.Controllers.JOBS_CONTROLLER,
460
+ stopped_message='All managed jobs should have finished.')
461
+
462
+ job_id_str = ','.join(map(str, job_ids))
463
+ if sum([bool(job_ids), name is not None, all or all_users]) != 1:
464
+ arguments = []
465
+ arguments += [f'job_ids={job_id_str}'] if job_ids else []
466
+ arguments += [f'name={name}'] if name is not None else []
467
+ arguments += ['all'] if all else []
468
+ arguments += ['all_users'] if all_users else []
469
+ with ux_utils.print_exception_no_traceback():
470
+ raise ValueError(
471
+ 'Can only specify one of JOB_IDS, name, or all/'
472
+ f'all_users. Provided {" ".join(arguments)!r}.')
473
+
474
+ backend = backend_utils.get_backend_from_handle(handle)
475
+ assert isinstance(backend, backends.CloudVmRayBackend)
476
+ if all_users:
477
+ code = managed_job_utils.ManagedJobCodeGen.cancel_jobs_by_id(
478
+ None, all_users=True)
479
+ elif all:
480
+ code = managed_job_utils.ManagedJobCodeGen.cancel_jobs_by_id(None)
481
+ elif job_ids:
482
+ code = managed_job_utils.ManagedJobCodeGen.cancel_jobs_by_id(
483
+ job_ids)
484
+ else:
485
+ assert name is not None, (job_ids, name, all)
486
+ code = managed_job_utils.ManagedJobCodeGen.cancel_job_by_name(name)
487
+ # The stderr is redirected to stdout
488
+ returncode, stdout, stderr = backend.run_on_head(handle,
489
+ code,
490
+ require_outputs=True,
491
+ stream_logs=False)
492
+ try:
493
+ subprocess_utils.handle_returncode(returncode, code,
494
+ 'Failed to cancel managed job',
495
+ stdout + stderr)
496
+ except exceptions.CommandError as e:
497
+ with ux_utils.print_exception_no_traceback():
498
+ raise RuntimeError(e.error_msg) from e
483
499
 
484
- sky_logging.print(stdout)
485
- if 'Multiple jobs found with name' in stdout:
486
- with ux_utils.print_exception_no_traceback():
487
- raise RuntimeError(
488
- 'Please specify the job ID instead of the job name.')
500
+ logger.info(stdout)
501
+ if 'Multiple jobs found with name' in stdout:
502
+ with ux_utils.print_exception_no_traceback():
503
+ raise RuntimeError(
504
+ 'Please specify the job ID instead of the job name.')
489
505
 
490
506
 
491
507
  @usage_lib.entrypoint
sky/jobs/state.py CHANGED
@@ -13,6 +13,7 @@ import colorama
13
13
 
14
14
  from sky import exceptions
15
15
  from sky import sky_logging
16
+ from sky.skylet import constants
16
17
  from sky.utils import common_utils
17
18
  from sky.utils import db_utils
18
19
 
@@ -118,7 +119,8 @@ def create_table(cursor, conn):
118
119
  controller_pid INTEGER DEFAULT NULL,
119
120
  dag_yaml_path TEXT,
120
121
  env_file_path TEXT,
121
- user_hash TEXT)""")
122
+ user_hash TEXT,
123
+ workspace TEXT DEFAULT NULL)""")
122
124
 
123
125
  db_utils.add_column_to_table(cursor, conn, 'job_info', 'schedule_state',
124
126
  'TEXT')
@@ -134,6 +136,12 @@ def create_table(cursor, conn):
134
136
 
135
137
  db_utils.add_column_to_table(cursor, conn, 'job_info', 'user_hash', 'TEXT')
136
138
 
139
+ db_utils.add_column_to_table(cursor,
140
+ conn,
141
+ 'job_info',
142
+ 'workspace',
143
+ 'TEXT DEFAULT NULL',
144
+ value_to_replace_existing_entries='default')
137
145
  conn.commit()
138
146
 
139
147
 
@@ -190,6 +198,7 @@ columns = [
190
198
  'dag_yaml_path',
191
199
  'env_file_path',
192
200
  'user_hash',
201
+ 'workspace',
193
202
  ]
194
203
 
195
204
 
@@ -380,14 +389,14 @@ class ManagedJobScheduleState(enum.Enum):
380
389
 
381
390
 
382
391
  # === Status transition functions ===
383
- def set_job_info(job_id: int, name: str):
392
+ def set_job_info(job_id: int, name: str, workspace: str):
384
393
  with db_utils.safe_cursor(_DB_PATH) as cursor:
385
394
  cursor.execute(
386
395
  """\
387
396
  INSERT INTO job_info
388
- (spot_job_id, name, schedule_state)
389
- VALUES (?, ?, ?)""",
390
- (job_id, name, ManagedJobScheduleState.INACTIVE.value))
397
+ (spot_job_id, name, schedule_state, workspace)
398
+ VALUES (?, ?, ?, ?)""",
399
+ (job_id, name, ManagedJobScheduleState.INACTIVE.value, workspace))
391
400
 
392
401
 
393
402
  def set_pending(job_id: int, task_id: int, task_name: str, resources_str: str):
@@ -1116,3 +1125,15 @@ def get_waiting_job() -> Optional[Dict[str, Any]]:
1116
1125
  'dag_yaml_path': row[2],
1117
1126
  'env_file_path': row[3],
1118
1127
  } if row is not None else None
1128
+
1129
+
1130
+ def get_workspace(job_id: int) -> str:
1131
+ """Get the workspace of a job."""
1132
+ with db_utils.safe_cursor(_DB_PATH) as cursor:
1133
+ workspace = cursor.execute(
1134
+ 'SELECT workspace FROM job_info WHERE spot_job_id = (?)',
1135
+ (job_id,)).fetchone()
1136
+ job_workspace = workspace[0] if workspace else None
1137
+ if job_workspace is None:
1138
+ return constants.SKYPILOT_DEFAULT_WORKSPACE
1139
+ return job_workspace