skypilot-nightly 1.0.0.dev20250905__py3-none-any.whl → 1.0.0.dev20251210__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 (429) hide show
  1. sky/__init__.py +12 -2
  2. sky/adaptors/aws.py +27 -22
  3. sky/adaptors/common.py +25 -2
  4. sky/adaptors/coreweave.py +278 -0
  5. sky/adaptors/do.py +8 -2
  6. sky/adaptors/gcp.py +11 -0
  7. sky/adaptors/ibm.py +5 -2
  8. sky/adaptors/kubernetes.py +64 -0
  9. sky/adaptors/nebius.py +3 -1
  10. sky/adaptors/primeintellect.py +1 -0
  11. sky/adaptors/seeweb.py +183 -0
  12. sky/adaptors/shadeform.py +89 -0
  13. sky/adaptors/slurm.py +478 -0
  14. sky/admin_policy.py +20 -0
  15. sky/authentication.py +157 -263
  16. sky/backends/__init__.py +3 -2
  17. sky/backends/backend.py +11 -3
  18. sky/backends/backend_utils.py +630 -185
  19. sky/backends/cloud_vm_ray_backend.py +1111 -928
  20. sky/backends/local_docker_backend.py +9 -5
  21. sky/backends/task_codegen.py +971 -0
  22. sky/backends/wheel_utils.py +18 -0
  23. sky/catalog/__init__.py +8 -3
  24. sky/catalog/aws_catalog.py +4 -0
  25. sky/catalog/common.py +19 -1
  26. sky/catalog/data_fetchers/fetch_aws.py +102 -80
  27. sky/catalog/data_fetchers/fetch_gcp.py +30 -3
  28. sky/catalog/data_fetchers/fetch_nebius.py +9 -6
  29. sky/catalog/data_fetchers/fetch_runpod.py +698 -0
  30. sky/catalog/data_fetchers/fetch_seeweb.py +329 -0
  31. sky/catalog/data_fetchers/fetch_shadeform.py +142 -0
  32. sky/catalog/kubernetes_catalog.py +36 -32
  33. sky/catalog/primeintellect_catalog.py +95 -0
  34. sky/catalog/runpod_catalog.py +5 -1
  35. sky/catalog/seeweb_catalog.py +184 -0
  36. sky/catalog/shadeform_catalog.py +165 -0
  37. sky/catalog/slurm_catalog.py +243 -0
  38. sky/check.py +87 -46
  39. sky/client/cli/command.py +1004 -434
  40. sky/client/cli/flags.py +4 -2
  41. sky/{volumes/utils.py → client/cli/table_utils.py} +111 -13
  42. sky/client/cli/utils.py +79 -0
  43. sky/client/common.py +12 -2
  44. sky/client/sdk.py +188 -65
  45. sky/client/sdk_async.py +34 -33
  46. sky/cloud_stores.py +82 -3
  47. sky/clouds/__init__.py +8 -0
  48. sky/clouds/aws.py +337 -129
  49. sky/clouds/azure.py +24 -18
  50. sky/clouds/cloud.py +47 -13
  51. sky/clouds/cudo.py +16 -13
  52. sky/clouds/do.py +9 -7
  53. sky/clouds/fluidstack.py +12 -5
  54. sky/clouds/gcp.py +14 -7
  55. sky/clouds/hyperbolic.py +12 -5
  56. sky/clouds/ibm.py +12 -5
  57. sky/clouds/kubernetes.py +80 -45
  58. sky/clouds/lambda_cloud.py +12 -5
  59. sky/clouds/nebius.py +23 -9
  60. sky/clouds/oci.py +19 -12
  61. sky/clouds/paperspace.py +4 -1
  62. sky/clouds/primeintellect.py +317 -0
  63. sky/clouds/runpod.py +85 -24
  64. sky/clouds/scp.py +12 -8
  65. sky/clouds/seeweb.py +477 -0
  66. sky/clouds/shadeform.py +400 -0
  67. sky/clouds/slurm.py +578 -0
  68. sky/clouds/ssh.py +6 -3
  69. sky/clouds/utils/scp_utils.py +61 -50
  70. sky/clouds/vast.py +43 -27
  71. sky/clouds/vsphere.py +14 -16
  72. sky/core.py +296 -195
  73. sky/dashboard/out/404.html +1 -1
  74. sky/dashboard/out/_next/static/KYAhEFa3FTfq4JyKVgo-s/_buildManifest.js +1 -0
  75. sky/dashboard/out/_next/static/chunks/1141-9c810f01ff4f398a.js +11 -0
  76. sky/dashboard/out/_next/static/chunks/1871-7e202677c42f43fe.js +6 -0
  77. sky/dashboard/out/_next/static/chunks/2260-7703229c33c5ebd5.js +1 -0
  78. sky/dashboard/out/_next/static/chunks/2369.fc20f0c2c8ed9fe7.js +15 -0
  79. sky/dashboard/out/_next/static/chunks/2755.edd818326d489a1d.js +26 -0
  80. sky/dashboard/out/_next/static/chunks/3294.ddda8c6c6f9f24dc.js +1 -0
  81. sky/dashboard/out/_next/static/chunks/3785.7e245f318f9d1121.js +1 -0
  82. sky/dashboard/out/_next/static/chunks/{6601-06114c982db410b6.js → 3800-b589397dc09c5b4e.js} +1 -1
  83. sky/dashboard/out/_next/static/chunks/3850-fd5696f3bbbaddae.js +1 -0
  84. sky/dashboard/out/_next/static/chunks/4725.172ede95d1b21022.js +1 -0
  85. sky/dashboard/out/_next/static/chunks/4937.a2baa2df5572a276.js +15 -0
  86. sky/dashboard/out/_next/static/chunks/6212-7bd06f60ba693125.js +13 -0
  87. sky/dashboard/out/_next/static/chunks/6856-da20c5fd999f319c.js +1 -0
  88. sky/dashboard/out/_next/static/chunks/6990-09cbf02d3cd518c3.js +1 -0
  89. sky/dashboard/out/_next/static/chunks/7359-c8d04e06886000b3.js +30 -0
  90. sky/dashboard/out/_next/static/chunks/7615-019513abc55b3b47.js +1 -0
  91. sky/dashboard/out/_next/static/chunks/8640.5b9475a2d18c5416.js +16 -0
  92. sky/dashboard/out/_next/static/chunks/8969-452f9d5cbdd2dc73.js +1 -0
  93. sky/dashboard/out/_next/static/chunks/9025.fa408f3242e9028d.js +6 -0
  94. sky/dashboard/out/_next/static/chunks/9353-8369df1cf105221c.js +1 -0
  95. sky/dashboard/out/_next/static/chunks/9360.a536cf6b1fa42355.js +31 -0
  96. sky/dashboard/out/_next/static/chunks/9847.3aaca6bb33455140.js +30 -0
  97. sky/dashboard/out/_next/static/chunks/pages/_app-68b647e26f9d2793.js +34 -0
  98. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33f525539665fdfd.js +16 -0
  99. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-a7565f586ef86467.js +1 -0
  100. sky/dashboard/out/_next/static/chunks/pages/clusters-9e5d47818b9bdadd.js +1 -0
  101. sky/dashboard/out/_next/static/chunks/pages/{config-dfb9bf07b13045f4.js → config-718cdc365de82689.js} +1 -1
  102. sky/dashboard/out/_next/static/chunks/pages/infra/{[context]-6563820e094f68ca.js → [context]-12c559ec4d81fdbd.js} +1 -1
  103. sky/dashboard/out/_next/static/chunks/pages/{infra-aabba60d57826e0f.js → infra-d187cd0413d72475.js} +1 -1
  104. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-895847b6cf200b04.js +16 -0
  105. sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-8d0f4655400b4eb9.js +21 -0
  106. sky/dashboard/out/_next/static/chunks/pages/jobs-e5a98f17f8513a96.js +1 -0
  107. sky/dashboard/out/_next/static/chunks/pages/plugins/[...slug]-4f46050ca065d8f8.js +1 -0
  108. sky/dashboard/out/_next/static/chunks/pages/users-2f7646eb77785a2c.js +1 -0
  109. sky/dashboard/out/_next/static/chunks/pages/volumes-ef19d49c6d0e8500.js +1 -0
  110. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-af76bb06dbb3954f.js → [name]-96e0f298308da7e2.js} +1 -1
  111. sky/dashboard/out/_next/static/chunks/pages/{workspaces-7598c33a746cdc91.js → workspaces-cb4da3abe08ebf19.js} +1 -1
  112. sky/dashboard/out/_next/static/chunks/webpack-fba3de387ff6bb08.js +1 -0
  113. sky/dashboard/out/_next/static/css/c5a4cfd2600fc715.css +3 -0
  114. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  115. sky/dashboard/out/clusters/[cluster].html +1 -1
  116. sky/dashboard/out/clusters.html +1 -1
  117. sky/dashboard/out/config.html +1 -1
  118. sky/dashboard/out/index.html +1 -1
  119. sky/dashboard/out/infra/[context].html +1 -1
  120. sky/dashboard/out/infra.html +1 -1
  121. sky/dashboard/out/jobs/[job].html +1 -1
  122. sky/dashboard/out/jobs/pools/[pool].html +1 -1
  123. sky/dashboard/out/jobs.html +1 -1
  124. sky/dashboard/out/plugins/[...slug].html +1 -0
  125. sky/dashboard/out/users.html +1 -1
  126. sky/dashboard/out/volumes.html +1 -1
  127. sky/dashboard/out/workspace/new.html +1 -1
  128. sky/dashboard/out/workspaces/[name].html +1 -1
  129. sky/dashboard/out/workspaces.html +1 -1
  130. sky/data/data_utils.py +92 -1
  131. sky/data/mounting_utils.py +177 -30
  132. sky/data/storage.py +200 -19
  133. sky/data/storage_utils.py +10 -45
  134. sky/exceptions.py +18 -7
  135. sky/execution.py +74 -31
  136. sky/global_user_state.py +605 -191
  137. sky/jobs/__init__.py +2 -0
  138. sky/jobs/client/sdk.py +101 -4
  139. sky/jobs/client/sdk_async.py +31 -5
  140. sky/jobs/constants.py +15 -8
  141. sky/jobs/controller.py +726 -284
  142. sky/jobs/file_content_utils.py +128 -0
  143. sky/jobs/log_gc.py +193 -0
  144. sky/jobs/recovery_strategy.py +250 -100
  145. sky/jobs/scheduler.py +271 -173
  146. sky/jobs/server/core.py +367 -114
  147. sky/jobs/server/server.py +81 -35
  148. sky/jobs/server/utils.py +89 -35
  149. sky/jobs/state.py +1498 -620
  150. sky/jobs/utils.py +771 -306
  151. sky/logs/agent.py +40 -5
  152. sky/logs/aws.py +9 -19
  153. sky/metrics/utils.py +282 -39
  154. sky/models.py +2 -0
  155. sky/optimizer.py +7 -6
  156. sky/provision/__init__.py +38 -1
  157. sky/provision/aws/config.py +34 -13
  158. sky/provision/aws/instance.py +5 -2
  159. sky/provision/azure/instance.py +5 -3
  160. sky/provision/common.py +22 -0
  161. sky/provision/cudo/instance.py +4 -3
  162. sky/provision/do/instance.py +4 -3
  163. sky/provision/docker_utils.py +112 -28
  164. sky/provision/fluidstack/instance.py +6 -5
  165. sky/provision/gcp/config.py +6 -1
  166. sky/provision/gcp/instance.py +4 -2
  167. sky/provision/hyperbolic/instance.py +4 -2
  168. sky/provision/instance_setup.py +66 -20
  169. sky/provision/kubernetes/__init__.py +2 -0
  170. sky/provision/kubernetes/config.py +7 -44
  171. sky/provision/kubernetes/constants.py +0 -1
  172. sky/provision/kubernetes/instance.py +609 -213
  173. sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml +1 -2
  174. sky/provision/kubernetes/network.py +12 -8
  175. sky/provision/kubernetes/network_utils.py +8 -25
  176. sky/provision/kubernetes/utils.py +422 -422
  177. sky/provision/kubernetes/volume.py +150 -18
  178. sky/provision/lambda_cloud/instance.py +16 -13
  179. sky/provision/nebius/instance.py +6 -2
  180. sky/provision/nebius/utils.py +103 -86
  181. sky/provision/oci/instance.py +4 -2
  182. sky/provision/paperspace/instance.py +4 -3
  183. sky/provision/primeintellect/__init__.py +10 -0
  184. sky/provision/primeintellect/config.py +11 -0
  185. sky/provision/primeintellect/instance.py +454 -0
  186. sky/provision/primeintellect/utils.py +398 -0
  187. sky/provision/provisioner.py +45 -15
  188. sky/provision/runpod/__init__.py +2 -0
  189. sky/provision/runpod/instance.py +4 -3
  190. sky/provision/runpod/volume.py +69 -13
  191. sky/provision/scp/instance.py +307 -130
  192. sky/provision/seeweb/__init__.py +11 -0
  193. sky/provision/seeweb/config.py +13 -0
  194. sky/provision/seeweb/instance.py +812 -0
  195. sky/provision/shadeform/__init__.py +11 -0
  196. sky/provision/shadeform/config.py +12 -0
  197. sky/provision/shadeform/instance.py +351 -0
  198. sky/provision/shadeform/shadeform_utils.py +83 -0
  199. sky/provision/slurm/__init__.py +12 -0
  200. sky/provision/slurm/config.py +13 -0
  201. sky/provision/slurm/instance.py +572 -0
  202. sky/provision/slurm/utils.py +583 -0
  203. sky/provision/vast/instance.py +9 -4
  204. sky/provision/vast/utils.py +10 -6
  205. sky/provision/volume.py +164 -0
  206. sky/provision/vsphere/common/ssl_helper.py +1 -1
  207. sky/provision/vsphere/common/vapiconnect.py +2 -1
  208. sky/provision/vsphere/common/vim_utils.py +3 -2
  209. sky/provision/vsphere/instance.py +8 -6
  210. sky/provision/vsphere/vsphere_utils.py +8 -1
  211. sky/resources.py +11 -3
  212. sky/schemas/api/responses.py +107 -6
  213. sky/schemas/db/global_user_state/008_skylet_ssh_tunnel_metadata.py +34 -0
  214. sky/schemas/db/global_user_state/009_last_activity_and_launched_at.py +89 -0
  215. sky/schemas/db/global_user_state/010_save_ssh_key.py +66 -0
  216. sky/schemas/db/global_user_state/011_is_ephemeral.py +34 -0
  217. sky/schemas/db/kv_cache/001_initial_schema.py +29 -0
  218. sky/schemas/db/serve_state/002_yaml_content.py +34 -0
  219. sky/schemas/db/skypilot_config/001_initial_schema.py +30 -0
  220. sky/schemas/db/spot_jobs/002_cluster_pool.py +3 -3
  221. sky/schemas/db/spot_jobs/004_job_file_contents.py +42 -0
  222. sky/schemas/db/spot_jobs/005_logs_gc.py +38 -0
  223. sky/schemas/db/spot_jobs/006_controller_pid_started_at.py +34 -0
  224. sky/schemas/db/spot_jobs/007_config_file_content.py +34 -0
  225. sky/schemas/generated/jobsv1_pb2.py +86 -0
  226. sky/schemas/generated/jobsv1_pb2.pyi +254 -0
  227. sky/schemas/generated/jobsv1_pb2_grpc.py +542 -0
  228. sky/schemas/generated/managed_jobsv1_pb2.py +76 -0
  229. sky/schemas/generated/managed_jobsv1_pb2.pyi +278 -0
  230. sky/schemas/generated/managed_jobsv1_pb2_grpc.py +278 -0
  231. sky/schemas/generated/servev1_pb2.py +58 -0
  232. sky/schemas/generated/servev1_pb2.pyi +115 -0
  233. sky/schemas/generated/servev1_pb2_grpc.py +322 -0
  234. sky/serve/autoscalers.py +2 -0
  235. sky/serve/client/impl.py +55 -21
  236. sky/serve/constants.py +4 -3
  237. sky/serve/controller.py +17 -11
  238. sky/serve/load_balancing_policies.py +1 -1
  239. sky/serve/replica_managers.py +219 -142
  240. sky/serve/serve_rpc_utils.py +179 -0
  241. sky/serve/serve_state.py +63 -54
  242. sky/serve/serve_utils.py +145 -109
  243. sky/serve/server/core.py +46 -25
  244. sky/serve/server/impl.py +311 -162
  245. sky/serve/server/server.py +21 -19
  246. sky/serve/service.py +84 -68
  247. sky/serve/service_spec.py +45 -7
  248. sky/server/auth/loopback.py +38 -0
  249. sky/server/auth/oauth2_proxy.py +12 -7
  250. sky/server/common.py +47 -24
  251. sky/server/config.py +62 -28
  252. sky/server/constants.py +9 -1
  253. sky/server/daemons.py +109 -38
  254. sky/server/metrics.py +76 -96
  255. sky/server/middleware_utils.py +166 -0
  256. sky/server/plugins.py +222 -0
  257. sky/server/requests/executor.py +384 -145
  258. sky/server/requests/payloads.py +83 -19
  259. sky/server/requests/preconditions.py +15 -13
  260. sky/server/requests/request_names.py +123 -0
  261. sky/server/requests/requests.py +511 -157
  262. sky/server/requests/serializers/decoders.py +48 -17
  263. sky/server/requests/serializers/encoders.py +102 -20
  264. sky/server/requests/serializers/return_value_serializers.py +60 -0
  265. sky/server/requests/threads.py +117 -0
  266. sky/server/rest.py +116 -24
  267. sky/server/server.py +497 -179
  268. sky/server/server_utils.py +30 -0
  269. sky/server/stream_utils.py +219 -45
  270. sky/server/uvicorn.py +30 -19
  271. sky/setup_files/MANIFEST.in +6 -1
  272. sky/setup_files/alembic.ini +8 -0
  273. sky/setup_files/dependencies.py +64 -19
  274. sky/setup_files/setup.py +44 -44
  275. sky/sky_logging.py +13 -5
  276. sky/skylet/attempt_skylet.py +116 -24
  277. sky/skylet/configs.py +3 -1
  278. sky/skylet/constants.py +139 -29
  279. sky/skylet/events.py +74 -14
  280. sky/skylet/executor/__init__.py +1 -0
  281. sky/skylet/executor/slurm.py +189 -0
  282. sky/skylet/job_lib.py +143 -105
  283. sky/skylet/log_lib.py +252 -8
  284. sky/skylet/log_lib.pyi +47 -7
  285. sky/skylet/providers/ibm/node_provider.py +12 -8
  286. sky/skylet/providers/ibm/vpc_provider.py +13 -12
  287. sky/skylet/runtime_utils.py +21 -0
  288. sky/skylet/services.py +524 -0
  289. sky/skylet/skylet.py +27 -2
  290. sky/skylet/subprocess_daemon.py +104 -28
  291. sky/skypilot_config.py +99 -79
  292. sky/ssh_node_pools/constants.py +12 -0
  293. sky/ssh_node_pools/core.py +40 -3
  294. sky/ssh_node_pools/deploy/__init__.py +4 -0
  295. sky/ssh_node_pools/deploy/deploy.py +952 -0
  296. sky/ssh_node_pools/deploy/tunnel_utils.py +199 -0
  297. sky/ssh_node_pools/deploy/utils.py +173 -0
  298. sky/ssh_node_pools/server.py +20 -21
  299. sky/{utils/kubernetes/ssh_utils.py → ssh_node_pools/utils.py} +9 -6
  300. sky/task.py +221 -104
  301. sky/templates/aws-ray.yml.j2 +1 -0
  302. sky/templates/azure-ray.yml.j2 +1 -0
  303. sky/templates/cudo-ray.yml.j2 +1 -0
  304. sky/templates/do-ray.yml.j2 +1 -0
  305. sky/templates/fluidstack-ray.yml.j2 +1 -0
  306. sky/templates/gcp-ray.yml.j2 +1 -0
  307. sky/templates/hyperbolic-ray.yml.j2 +1 -0
  308. sky/templates/ibm-ray.yml.j2 +2 -1
  309. sky/templates/jobs-controller.yaml.j2 +3 -0
  310. sky/templates/kubernetes-ray.yml.j2 +204 -55
  311. sky/templates/lambda-ray.yml.j2 +1 -0
  312. sky/templates/nebius-ray.yml.j2 +3 -0
  313. sky/templates/oci-ray.yml.j2 +1 -0
  314. sky/templates/paperspace-ray.yml.j2 +1 -0
  315. sky/templates/primeintellect-ray.yml.j2 +72 -0
  316. sky/templates/runpod-ray.yml.j2 +1 -0
  317. sky/templates/scp-ray.yml.j2 +1 -0
  318. sky/templates/seeweb-ray.yml.j2 +171 -0
  319. sky/templates/shadeform-ray.yml.j2 +73 -0
  320. sky/templates/slurm-ray.yml.j2 +85 -0
  321. sky/templates/vast-ray.yml.j2 +2 -0
  322. sky/templates/vsphere-ray.yml.j2 +1 -0
  323. sky/templates/websocket_proxy.py +188 -43
  324. sky/usage/usage_lib.py +16 -4
  325. sky/users/model.conf +1 -1
  326. sky/users/permission.py +84 -44
  327. sky/users/rbac.py +31 -3
  328. sky/utils/accelerator_registry.py +6 -3
  329. sky/utils/admin_policy_utils.py +18 -5
  330. sky/utils/annotations.py +128 -6
  331. sky/utils/asyncio_utils.py +78 -0
  332. sky/utils/atomic.py +1 -1
  333. sky/utils/auth_utils.py +153 -0
  334. sky/utils/cli_utils/status_utils.py +12 -7
  335. sky/utils/cluster_utils.py +28 -6
  336. sky/utils/command_runner.py +283 -30
  337. sky/utils/command_runner.pyi +63 -7
  338. sky/utils/common.py +3 -1
  339. sky/utils/common_utils.py +55 -7
  340. sky/utils/config_utils.py +1 -14
  341. sky/utils/context.py +127 -40
  342. sky/utils/context_utils.py +73 -18
  343. sky/utils/controller_utils.py +229 -70
  344. sky/utils/db/db_utils.py +95 -18
  345. sky/utils/db/kv_cache.py +149 -0
  346. sky/utils/db/migration_utils.py +24 -7
  347. sky/utils/env_options.py +4 -0
  348. sky/utils/git.py +559 -1
  349. sky/utils/kubernetes/create_cluster.sh +15 -30
  350. sky/utils/kubernetes/delete_cluster.sh +10 -7
  351. sky/utils/kubernetes/generate_kind_config.py +6 -66
  352. sky/utils/kubernetes/gpu_labeler.py +13 -3
  353. sky/utils/kubernetes/k8s_gpu_labeler_job.yaml +2 -1
  354. sky/utils/kubernetes/k8s_gpu_labeler_setup.yaml +16 -16
  355. sky/utils/kubernetes/kubernetes_deploy_utils.py +187 -260
  356. sky/utils/kubernetes/rsync_helper.sh +11 -3
  357. sky/utils/kubernetes/ssh-tunnel.sh +7 -376
  358. sky/utils/kubernetes_enums.py +7 -15
  359. sky/utils/lock_events.py +4 -4
  360. sky/utils/locks.py +128 -31
  361. sky/utils/log_utils.py +0 -319
  362. sky/utils/resource_checker.py +13 -10
  363. sky/utils/resources_utils.py +53 -29
  364. sky/utils/rich_utils.py +8 -4
  365. sky/utils/schemas.py +138 -52
  366. sky/utils/subprocess_utils.py +17 -4
  367. sky/utils/thread_utils.py +91 -0
  368. sky/utils/timeline.py +2 -1
  369. sky/utils/ux_utils.py +35 -1
  370. sky/utils/volume.py +88 -4
  371. sky/utils/yaml_utils.py +9 -0
  372. sky/volumes/client/sdk.py +48 -10
  373. sky/volumes/server/core.py +59 -22
  374. sky/volumes/server/server.py +46 -17
  375. sky/volumes/volume.py +54 -42
  376. sky/workspaces/core.py +57 -21
  377. sky/workspaces/server.py +13 -12
  378. sky_templates/README.md +3 -0
  379. sky_templates/__init__.py +3 -0
  380. sky_templates/ray/__init__.py +0 -0
  381. sky_templates/ray/start_cluster +183 -0
  382. sky_templates/ray/stop_cluster +75 -0
  383. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/METADATA +343 -65
  384. skypilot_nightly-1.0.0.dev20251210.dist-info/RECORD +629 -0
  385. skypilot_nightly-1.0.0.dev20251210.dist-info/top_level.txt +2 -0
  386. sky/client/cli/git.py +0 -549
  387. sky/dashboard/out/_next/static/chunks/1121-408ed10b2f9fce17.js +0 -1
  388. sky/dashboard/out/_next/static/chunks/1141-943efc7aff0f0c06.js +0 -1
  389. sky/dashboard/out/_next/static/chunks/1836-37fede578e2da5f8.js +0 -40
  390. sky/dashboard/out/_next/static/chunks/3015-86cabed5d4669ad0.js +0 -1
  391. sky/dashboard/out/_next/static/chunks/3294.c80326aec9bfed40.js +0 -6
  392. sky/dashboard/out/_next/static/chunks/3785.4872a2f3aa489880.js +0 -1
  393. sky/dashboard/out/_next/static/chunks/3850-ff4a9a69d978632b.js +0 -1
  394. sky/dashboard/out/_next/static/chunks/4045.b30465273dc5e468.js +0 -21
  395. sky/dashboard/out/_next/static/chunks/4676-9da7fdbde90b5549.js +0 -10
  396. sky/dashboard/out/_next/static/chunks/4725.10f7a9a5d3ea8208.js +0 -1
  397. sky/dashboard/out/_next/static/chunks/5339.3fda4a4010ff4e06.js +0 -51
  398. sky/dashboard/out/_next/static/chunks/6135-4b4d5e824b7f9d3c.js +0 -1
  399. sky/dashboard/out/_next/static/chunks/649.b9d7f7d10c1b8c53.js +0 -45
  400. sky/dashboard/out/_next/static/chunks/6856-dca7962af4814e1b.js +0 -1
  401. sky/dashboard/out/_next/static/chunks/6990-08b2a1cae076a943.js +0 -1
  402. sky/dashboard/out/_next/static/chunks/7325.b4bc99ce0892dcd5.js +0 -6
  403. sky/dashboard/out/_next/static/chunks/754-d0da8ab45f9509e9.js +0 -18
  404. sky/dashboard/out/_next/static/chunks/7669.1f5d9a402bf5cc42.js +0 -36
  405. sky/dashboard/out/_next/static/chunks/8969-0be3036bf86f8256.js +0 -1
  406. sky/dashboard/out/_next/static/chunks/9025.c12318fb6a1a9093.js +0 -6
  407. sky/dashboard/out/_next/static/chunks/9037-fa1737818d0a0969.js +0 -6
  408. sky/dashboard/out/_next/static/chunks/pages/_app-ce361c6959bc2001.js +0 -34
  409. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-1cbba24bd1bd35f8.js +0 -16
  410. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-0b4b35dc1dfe046c.js +0 -16
  411. sky/dashboard/out/_next/static/chunks/pages/clusters-469814d711d63b1b.js +0 -1
  412. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-dd64309c3fe67ed2.js +0 -11
  413. sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-07349868f7905d37.js +0 -16
  414. sky/dashboard/out/_next/static/chunks/pages/jobs-1f70d9faa564804f.js +0 -1
  415. sky/dashboard/out/_next/static/chunks/pages/users-018bf31cda52e11b.js +0 -1
  416. sky/dashboard/out/_next/static/chunks/pages/volumes-739726d6b823f532.js +0 -1
  417. sky/dashboard/out/_next/static/chunks/webpack-4fe903277b57b523.js +0 -1
  418. sky/dashboard/out/_next/static/css/4614e06482d7309e.css +0 -3
  419. sky/dashboard/out/_next/static/mS-4qZPSkRuA1u-g2wQhg/_buildManifest.js +0 -1
  420. sky/templates/kubernetes-ssh-jump.yml.j2 +0 -94
  421. sky/utils/kubernetes/cleanup-tunnel.sh +0 -62
  422. sky/utils/kubernetes/deploy_remote_cluster.py +0 -1299
  423. sky/utils/kubernetes/ssh_jump_lifecycle_manager.py +0 -191
  424. skypilot_nightly-1.0.0.dev20250905.dist-info/RECORD +0 -547
  425. skypilot_nightly-1.0.0.dev20250905.dist-info/top_level.txt +0 -1
  426. /sky/dashboard/out/_next/static/{mS-4qZPSkRuA1u-g2wQhg → KYAhEFa3FTfq4JyKVgo-s}/_ssgManifest.js +0 -0
  427. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/WHEEL +0 -0
  428. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/entry_points.txt +0 -0
  429. {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/licenses/LICENSE +0 -0
sky/adaptors/slurm.py ADDED
@@ -0,0 +1,478 @@
1
+ """Slurm adaptor for SkyPilot."""
2
+
3
+ import logging
4
+ import re
5
+ import time
6
+ from typing import Dict, List, NamedTuple, Optional, Tuple
7
+
8
+ from sky.utils import command_runner
9
+ from sky.utils import subprocess_utils
10
+ from sky.utils import timeline
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # ASCII Unit Separator (\x1f) to handle values with spaces
15
+ # and other special characters.
16
+ SEP = r'\x1f'
17
+
18
+ # Regex pattern to extract partition names from scontrol output
19
+ # Matches PartitionName=<name> and captures until the next field
20
+ _PARTITION_NAME_REGEX = re.compile(r'PartitionName=(.+?)(?:\s+\w+=|$)')
21
+
22
+ # Default timeout for waiting for job nodes to be allocated, in seconds.
23
+ _SLURM_DEFAULT_PROVISION_TIMEOUT = 10
24
+
25
+
26
+ class SlurmPartition(NamedTuple):
27
+ """Information about the Slurm partitions."""
28
+ name: str
29
+ is_default: bool
30
+
31
+
32
+ # TODO(kevin): Add more API types for other client functions.
33
+ class NodeInfo(NamedTuple):
34
+ """Information about a Slurm node from sinfo."""
35
+ node: str
36
+ state: str
37
+ gres: str
38
+ cpus: int
39
+ memory_gb: float
40
+ # The default partition contains a '*' at the end of the name.
41
+ # It is the caller's responsibility to strip the '*' if needed.
42
+ partition: str
43
+
44
+
45
+ class SlurmClient:
46
+ """Client for Slurm control plane operations."""
47
+
48
+ def __init__(
49
+ self,
50
+ ssh_host: str,
51
+ ssh_port: int,
52
+ ssh_user: str,
53
+ ssh_key: Optional[str],
54
+ ssh_proxy_command: Optional[str] = None,
55
+ ):
56
+ """Initialize SlurmClient.
57
+
58
+ Args:
59
+ ssh_host: Hostname of the Slurm controller.
60
+ ssh_port: SSH port on the controller.
61
+ ssh_user: SSH username.
62
+ ssh_key: Path to SSH private key, or None for keyless SSH.
63
+ ssh_proxy_command: Optional SSH proxy command.
64
+ """
65
+ self.ssh_host = ssh_host
66
+ self.ssh_port = ssh_port
67
+ self.ssh_user = ssh_user
68
+ self.ssh_key = ssh_key
69
+ self.ssh_proxy_command = ssh_proxy_command
70
+
71
+ # Internal runner for executing Slurm CLI commands
72
+ # on the controller node.
73
+ self._runner = command_runner.SSHCommandRunner(
74
+ (ssh_host, ssh_port),
75
+ ssh_user,
76
+ ssh_key,
77
+ ssh_proxy_command=ssh_proxy_command,
78
+ )
79
+
80
+ def query_jobs(
81
+ self,
82
+ job_name: Optional[str] = None,
83
+ state_filters: Optional[List[str]] = None,
84
+ ) -> List[str]:
85
+ """Query Slurm jobs by state and optional name.
86
+
87
+ Args:
88
+ job_name: Optional job name to filter by.
89
+ state_filters: List of job states to filter by
90
+ (e.g., ['running', 'pending']). If None, returns all jobs.
91
+
92
+ Returns:
93
+ List of job IDs matching the filters.
94
+ """
95
+ cmd = 'squeue --me -h -o "%i"'
96
+ if state_filters is not None:
97
+ state_filters_str = ','.join(state_filters)
98
+ cmd += f' --states {state_filters_str}'
99
+ if job_name is not None:
100
+ cmd += f' --name {job_name}'
101
+
102
+ rc, stdout, stderr = self._runner.run(cmd,
103
+ require_outputs=True,
104
+ stream_logs=False)
105
+ subprocess_utils.handle_returncode(rc,
106
+ cmd,
107
+ 'Failed to query Slurm jobs.',
108
+ stderr=stderr)
109
+
110
+ job_ids = stdout.strip().splitlines()
111
+ return job_ids
112
+
113
+ def cancel_jobs_by_name(self,
114
+ job_name: str,
115
+ signal: Optional[str] = None,
116
+ full: bool = False) -> None:
117
+ """Cancel Slurm job(s) by name.
118
+
119
+ Args:
120
+ job_name: Name of the job(s) to cancel.
121
+ signal: Optional signal to send to the job(s).
122
+ full: If True, signals the batch script and its children processes.
123
+ By default, signals other than SIGKILL are not sent to the
124
+ batch step (the shell script).
125
+ """
126
+ cmd = f'scancel --name {job_name}'
127
+ if signal is not None:
128
+ cmd += f' --signal {signal}'
129
+ if full:
130
+ cmd += ' --full'
131
+ rc, stdout, stderr = self._runner.run(cmd,
132
+ require_outputs=True,
133
+ stream_logs=False)
134
+ subprocess_utils.handle_returncode(rc,
135
+ cmd,
136
+ f'Failed to cancel job {job_name}.',
137
+ stderr=stderr)
138
+ logger.debug(f'Successfully cancelled job {job_name}: {stdout}')
139
+
140
+ def info(self) -> str:
141
+ """Get Slurm cluster information.
142
+
143
+ This is useful for checking if the cluster is accessible and
144
+ retrieving node information.
145
+
146
+ Returns:
147
+ The stdout output from sinfo.
148
+ """
149
+ cmd = 'sinfo'
150
+ rc, stdout, stderr = self._runner.run(cmd,
151
+ require_outputs=True,
152
+ stream_logs=False)
153
+ subprocess_utils.handle_returncode(
154
+ rc, cmd, 'Failed to get Slurm cluster information.', stderr=stderr)
155
+ return stdout
156
+
157
+ def info_nodes(self) -> List[NodeInfo]:
158
+ """Get Slurm node information.
159
+
160
+ Returns node names, states, GRES (generic resources like GPUs),
161
+ CPUs, memory (MB), and partitions.
162
+ """
163
+ cmd = (f'sinfo -h --Node -o '
164
+ f'"%N{SEP}%t{SEP}%G{SEP}%c{SEP}%m{SEP}%P"')
165
+ rc, stdout, stderr = self._runner.run(cmd,
166
+ require_outputs=True,
167
+ stream_logs=False)
168
+ subprocess_utils.handle_returncode(
169
+ rc, cmd, 'Failed to get Slurm node information.', stderr=stderr)
170
+
171
+ nodes = []
172
+ for line in stdout.splitlines():
173
+ parts = line.split(SEP)
174
+ if len(parts) != 6:
175
+ raise RuntimeError(
176
+ f'Unexpected output format from sinfo: {line!r}')
177
+ try:
178
+ node_info = NodeInfo(node=parts[0],
179
+ state=parts[1],
180
+ gres=parts[2],
181
+ cpus=int(parts[3]),
182
+ memory_gb=int(parts[4]) / 1024.0,
183
+ partition=parts[5])
184
+ nodes.append(node_info)
185
+ except ValueError as e:
186
+ raise RuntimeError(
187
+ f'Failed to parse node info from line: {line!r}. '
188
+ f'Error: {e}') from e
189
+
190
+ return nodes
191
+
192
+ def node_details(self, node_name: str) -> Dict[str, str]:
193
+ """Get detailed Slurm node information.
194
+
195
+ Returns:
196
+ A dictionary of node attributes.
197
+ """
198
+
199
+ def _parse_scontrol_node_output(output: str) -> Dict[str, str]:
200
+ """Parses the key=value output of 'scontrol show node'."""
201
+ node_info = {}
202
+ # Split by space, handling values that might have spaces
203
+ # if quoted. This is simplified; scontrol can be complex.
204
+ parts = output.split()
205
+ for part in parts:
206
+ if '=' in part:
207
+ key, value = part.split('=', 1)
208
+ # Simple quote removal, might need refinement
209
+ value = value.strip('\'"')
210
+ node_info[key] = value
211
+ return node_info
212
+
213
+ cmd = f'scontrol show node {node_name}'
214
+ rc, node_details, _ = self._runner.run(cmd,
215
+ require_outputs=True,
216
+ stream_logs=False)
217
+ subprocess_utils.handle_returncode(
218
+ rc,
219
+ cmd,
220
+ f'Failed to get detailed node information for {node_name}.',
221
+ stderr=node_details)
222
+ node_info = _parse_scontrol_node_output(node_details)
223
+ return node_info
224
+
225
+ def get_node_jobs(self, node_name: str) -> List[str]:
226
+ """Get the list of jobs for a given node name.
227
+
228
+ Returns:
229
+ A list of job names for the current user on the node.
230
+ """
231
+ cmd = f'squeue --me -h --nodelist {node_name} -o "%b"'
232
+ rc, stdout, stderr = self._runner.run(cmd,
233
+ require_outputs=True,
234
+ stream_logs=False)
235
+ subprocess_utils.handle_returncode(
236
+ rc, cmd, f'Failed to get jobs for node {node_name}.', stderr=stderr)
237
+ return stdout.splitlines()
238
+
239
+ def get_job_state(self, job_id: str) -> Optional[str]:
240
+ """Get the state of a Slurm job.
241
+
242
+ Args:
243
+ job_id: The Slurm job ID.
244
+
245
+ Returns:
246
+ The job state (e.g., 'PENDING', 'RUNNING', 'COMPLETED', etc.),
247
+ or None if the job is not found.
248
+ """
249
+ # Use --only-job-state since we only need the job state.
250
+ # This reduces the work required by slurmctld.
251
+ cmd = f'squeue -h --only-job-state --jobs {job_id} -o "%T"'
252
+ rc, stdout, stderr = self._runner.run(cmd,
253
+ require_outputs=True,
254
+ stream_logs=False)
255
+ if rc != 0:
256
+ # Job may not exist
257
+ logger.debug(f'Failed to get job state for job {job_id}: {stderr}')
258
+ return None
259
+
260
+ state = stdout.strip()
261
+ return state if state else None
262
+
263
+ @timeline.event
264
+ def get_job_reason(self, job_id: str) -> Optional[str]:
265
+ """Get the reason a job is in its current state
266
+
267
+ Args:
268
+ job_id: The Slurm job ID.
269
+ """
270
+ # Without --states all, squeue omits terminated jobs.
271
+ cmd = f'squeue -h --jobs {job_id} --states all -o "%r"'
272
+ rc, stdout, stderr = self._runner.run(cmd,
273
+ require_outputs=True,
274
+ stream_logs=False)
275
+ if rc != 0:
276
+ logger.debug(f'Failed to get job info for job {job_id}: {stderr}')
277
+ return None
278
+
279
+ output = stdout.strip()
280
+ if not output:
281
+ return None
282
+
283
+ return output if output != 'None' else None
284
+
285
+ @timeline.event
286
+ def wait_for_job_nodes(self, job_id: str, timeout: int) -> None:
287
+ """Wait for a Slurm job to have nodes allocated.
288
+
289
+ Args:
290
+ job_id: The Slurm job ID.
291
+ timeout: Maximum time to wait in seconds.
292
+ """
293
+ start_time = time.time()
294
+ last_state = None
295
+
296
+ while time.time() - start_time < timeout:
297
+ state = self.get_job_state(job_id)
298
+
299
+ if state != last_state:
300
+ logger.debug(f'Job {job_id} state: {state}')
301
+ last_state = state
302
+
303
+ if state is None:
304
+ raise RuntimeError(f'Job {job_id} not found. It may have been '
305
+ 'cancelled or failed.')
306
+
307
+ if state in ('COMPLETED', 'CANCELLED', 'FAILED', 'TIMEOUT'):
308
+ raise RuntimeError(
309
+ f'Job {job_id} terminated with state {state} '
310
+ 'before nodes were allocated.')
311
+ # TODO(kevin): Log reason for pending.
312
+
313
+ # Check if nodes are allocated by trying to get node list
314
+ cmd = f'squeue -h --jobs {job_id} -o "%N"'
315
+ rc, stdout, stderr = self._runner.run(cmd,
316
+ require_outputs=True,
317
+ stream_logs=False)
318
+
319
+ if rc == 0 and stdout.strip():
320
+ # Nodes are allocated
321
+ logger.debug(
322
+ f'Job {job_id} has nodes allocated: {stdout.strip()}')
323
+ return
324
+ elif rc != 0:
325
+ logger.debug(f'Failed to get nodes for job {job_id}: {stderr}')
326
+
327
+ # Wait before checking again
328
+ time.sleep(2)
329
+
330
+ raise TimeoutError(f'Job {job_id} did not get nodes allocated within '
331
+ f'{timeout} seconds. Last state: {last_state}')
332
+
333
+ @timeline.event
334
+ def get_job_nodes(
335
+ self,
336
+ job_id: str,
337
+ wait: bool = True,
338
+ timeout: Optional[int] = None) -> Tuple[List[str], List[str]]:
339
+ """Get the list of nodes and their IPs for a given job ID.
340
+
341
+ The ordering is guaranteed to be stable for the lifetime of the job.
342
+
343
+ Args:
344
+ job_id: The Slurm job ID.
345
+ wait: If True, wait for nodes to be allocated before returning.
346
+ timeout: Maximum time to wait in seconds. Only used when wait=True.
347
+
348
+ Returns:
349
+ A tuple of (nodes, node_ips) where nodes is a list of node names
350
+ and node_ips is a list of corresponding IP addresses.
351
+ """
352
+ # Wait for nodes to be allocated if requested
353
+ if wait:
354
+ if timeout is None:
355
+ timeout = _SLURM_DEFAULT_PROVISION_TIMEOUT
356
+ self.wait_for_job_nodes(job_id, timeout=timeout)
357
+
358
+ cmd = (
359
+ f'squeue -h --jobs {job_id} -o "%N" | tr \',\' \'\\n\' | '
360
+ f'while read node; do '
361
+ # TODO(kevin): Use json output for more robust parsing.
362
+ f'ip=$(scontrol show node=$node | grep NodeAddr= | '
363
+ f'awk -F= \'{{print $2}}\' | awk \'{{print $1}}\'); '
364
+ f'echo "$node $ip"; '
365
+ f'done')
366
+ rc, stdout, stderr = self._runner.run(cmd,
367
+ require_outputs=True,
368
+ stream_logs=False)
369
+ subprocess_utils.handle_returncode(
370
+ rc, cmd, f'Failed to get nodes for job {job_id}.', stderr=stderr)
371
+ logger.debug(f'Successfully got nodes for job {job_id}: {stdout}')
372
+
373
+ node_info = {}
374
+ for line in stdout.strip().splitlines():
375
+ line = line.strip()
376
+ if line:
377
+ parts = line.split()
378
+ if len(parts) >= 2:
379
+ node_name = parts[0]
380
+ node_ip = parts[1]
381
+ node_info[node_name] = node_ip
382
+
383
+ nodes = list(node_info.keys())
384
+ node_ips = [node_info[node] for node in nodes]
385
+ if not nodes:
386
+ raise RuntimeError(
387
+ f'No nodes found for job {job_id}. '
388
+ f'The job may have terminated or the output was empty.')
389
+ assert (len(nodes) == len(node_ips)
390
+ ), f'Number of nodes and IPs do not match: {nodes} != {node_ips}'
391
+
392
+ return nodes, node_ips
393
+
394
+ def submit_job(
395
+ self,
396
+ partition: str,
397
+ job_name: str,
398
+ script_path: str,
399
+ ) -> str:
400
+ """Submit a Slurm job script.
401
+
402
+ Args:
403
+ partition: Slurm partition to submit to.
404
+ job_name: Name to give the job.
405
+ script_path: Remote path where the script will be stored.
406
+
407
+ Returns:
408
+ The job ID of the submitted job.
409
+ """
410
+ cmd = f'sbatch --partition={partition} {script_path}'
411
+ rc, stdout, stderr = self._runner.run(cmd,
412
+ require_outputs=True,
413
+ stream_logs=False)
414
+ subprocess_utils.handle_returncode(rc,
415
+ cmd,
416
+ 'Failed to submit Slurm job.',
417
+ stderr=f'{stdout}\n{stderr}')
418
+
419
+ # Parse job ID from sbatch output (format: "Submitted batch job 12345")
420
+ job_id_match = re.search(r'Submitted batch job (\d+)', stdout)
421
+ if not job_id_match:
422
+ raise RuntimeError(
423
+ f'Failed to parse job ID from sbatch output: {stdout}')
424
+
425
+ job_id = job_id_match.group(1).strip()
426
+ logger.debug(f'Successfully submitted Slurm job {job_id} with name '
427
+ f'{job_name}: {stdout}')
428
+
429
+ return job_id
430
+
431
+ def get_partitions_info(self) -> List[SlurmPartition]:
432
+ """Get the partitions information for the Slurm cluster.
433
+
434
+ Returns:
435
+ List of SlurmPartition objects.
436
+ """
437
+ cmd = 'scontrol show partitions -o'
438
+ rc, stdout, stderr = self._runner.run(cmd,
439
+ require_outputs=True,
440
+ stream_logs=False)
441
+ subprocess_utils.handle_returncode(rc,
442
+ cmd,
443
+ 'Failed to get Slurm partitions.',
444
+ stderr=stderr)
445
+
446
+ partitions = []
447
+ for line in stdout.strip().splitlines():
448
+ is_default = False
449
+ match = _PARTITION_NAME_REGEX.search(line)
450
+ if 'Default=YES' in line:
451
+ is_default = True
452
+ if match:
453
+ partition = match.group(1).strip()
454
+ if partition:
455
+ partitions.append(
456
+ SlurmPartition(name=partition, is_default=is_default))
457
+ return partitions
458
+
459
+ def get_default_partition(self) -> Optional[str]:
460
+ """Get the default partition name for the Slurm cluster.
461
+
462
+ Returns:
463
+ The default partition name, or None if it cannot be determined.
464
+ """
465
+ partitions = self.get_partitions_info()
466
+ for partition in partitions:
467
+ if partition.is_default:
468
+ return partition.name
469
+ return None
470
+
471
+ def get_partitions(self) -> List[str]:
472
+ """Get unique partition names in the Slurm cluster.
473
+
474
+ Returns:
475
+ List of partition names. The default partition will not have a '*'
476
+ at the end of the name.
477
+ """
478
+ return [partition.name for partition in self.get_partitions_info()]
sky/admin_policy.py CHANGED
@@ -9,7 +9,9 @@ import pydantic
9
9
 
10
10
  import sky
11
11
  from sky import exceptions
12
+ from sky import models
12
13
  from sky.adaptors import common as adaptors_common
14
+ from sky.server.requests import request_names
13
15
  from sky.utils import common_utils
14
16
  from sky.utils import config_utils
15
17
  from sky.utils import ux_utils
@@ -49,8 +51,10 @@ class _UserRequestBody(pydantic.BaseModel):
49
51
  # will be converted to JSON string, which will lose the None key.
50
52
  task: str
51
53
  skypilot_config: str
54
+ request_name: str
52
55
  request_options: Optional[RequestOptions] = None
53
56
  at_client_side: bool = False
57
+ user: str
54
58
 
55
59
 
56
60
  @dataclasses.dataclass
@@ -73,32 +77,48 @@ class UserRequest:
73
77
  skypilot_config: Global skypilot config to be used in this request.
74
78
  request_options: Request options. It is None for jobs and services.
75
79
  at_client_side: Is the request intercepted by the policy at client-side?
80
+ user: User who made the request.
81
+ Only available on the server side.
82
+ This value is None if at_client_side is True.
76
83
  """
77
84
  task: 'sky.Task'
78
85
  skypilot_config: 'sky.Config'
86
+ request_name: request_names.AdminPolicyRequestName
79
87
  request_options: Optional['RequestOptions'] = None
80
88
  at_client_side: bool = False
89
+ user: Optional['models.User'] = None
81
90
 
82
91
  def encode(self) -> str:
83
92
  return _UserRequestBody(
84
93
  task=yaml_utils.dump_yaml_str(self.task.to_yaml_config()),
85
94
  skypilot_config=yaml_utils.dump_yaml_str(dict(
86
95
  self.skypilot_config)),
96
+ request_name=self.request_name.value,
87
97
  request_options=self.request_options,
88
98
  at_client_side=self.at_client_side,
99
+ user=(yaml_utils.dump_yaml_str(self.user.to_dict())
100
+ if self.user is not None else ''),
89
101
  ).model_dump_json()
90
102
 
91
103
  @classmethod
92
104
  def decode(cls, body: str) -> 'UserRequest':
93
105
  user_request_body = _UserRequestBody.model_validate_json(body)
106
+ user_dict = yaml_utils.read_yaml_str(
107
+ user_request_body.user) if user_request_body.user != '' else None
108
+ user = models.User(
109
+ id=user_dict['id'],
110
+ name=user_dict['name']) if user_dict is not None else None
94
111
  return cls(
95
112
  task=sky.Task.from_yaml_config(
96
113
  yaml_utils.read_yaml_all_str(user_request_body.task)[0]),
97
114
  skypilot_config=config_utils.Config.from_dict(
98
115
  yaml_utils.read_yaml_all_str(
99
116
  user_request_body.skypilot_config)[0]),
117
+ request_name=request_names.AdminPolicyRequestName(
118
+ user_request_body.request_name),
100
119
  request_options=user_request_body.request_options,
101
120
  at_client_side=user_request_body.at_client_side,
121
+ user=user,
102
122
  )
103
123
 
104
124