skypilot-nightly 1.0.0.dev20250627__py3-none-any.whl → 1.0.0.dev20250630__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.
- sky/__init__.py +2 -2
- sky/adaptors/kubernetes.py +14 -0
- sky/adaptors/nebius.py +2 -2
- sky/authentication.py +12 -5
- sky/backends/backend_utils.py +92 -26
- sky/check.py +5 -2
- sky/client/cli/command.py +39 -8
- sky/client/sdk.py +217 -167
- sky/client/service_account_auth.py +47 -0
- sky/clouds/aws.py +10 -4
- sky/clouds/azure.py +5 -2
- sky/clouds/cloud.py +5 -2
- sky/clouds/gcp.py +31 -18
- sky/clouds/kubernetes.py +54 -34
- sky/clouds/nebius.py +8 -2
- sky/clouds/ssh.py +5 -2
- sky/clouds/utils/aws_utils.py +10 -4
- sky/clouds/utils/gcp_utils.py +22 -7
- sky/clouds/utils/oci_utils.py +62 -14
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/1043-1b39779691bb4030.js +1 -0
- sky/dashboard/out/_next/static/chunks/{141-fa5a20cbf401b351.js → 1141-726e5a3f00b67185.js} +2 -2
- sky/dashboard/out/_next/static/chunks/1272-1ef0bf0237faccdb.js +1 -0
- sky/dashboard/out/_next/static/chunks/1664-d65361e92b85e786.js +1 -0
- sky/dashboard/out/_next/static/chunks/1691.44e378727a41f3b5.js +21 -0
- sky/dashboard/out/_next/static/chunks/1871-80dea41717729fa5.js +6 -0
- sky/dashboard/out/_next/static/chunks/2544.27f70672535675ed.js +1 -0
- sky/dashboard/out/_next/static/chunks/{875.52c962183328b3f2.js → 2875.c24c6d57dc82e436.js} +1 -1
- sky/dashboard/out/_next/static/chunks/3256.7257acd01b481bed.js +11 -0
- sky/dashboard/out/_next/static/chunks/3698-52ad1ca228faa776.js +1 -0
- sky/dashboard/out/_next/static/chunks/3785.b3cc2bc1d49d2c3c.js +1 -0
- sky/dashboard/out/_next/static/chunks/3937.d7f1c55d1916c7f2.js +1 -0
- sky/dashboard/out/_next/static/chunks/{947-6620842ef80ae879.js → 3947-b059261d6fa88a1f.js} +1 -1
- sky/dashboard/out/_next/static/chunks/{697.6460bf72e760addd.js → 4697.f5421144224da9fc.js} +1 -1
- sky/dashboard/out/_next/static/chunks/4725.4c849b1e05c8e9ad.js +1 -0
- sky/dashboard/out/_next/static/chunks/5230-df791914b54d91d9.js +1 -0
- sky/dashboard/out/_next/static/chunks/{491.b3d264269613fe09.js → 5491.918ffed0ba7a5294.js} +1 -1
- sky/dashboard/out/_next/static/chunks/5739-5ea3ffa10fc884f2.js +8 -0
- sky/dashboard/out/_next/static/chunks/616-162f3033ffcd3d31.js +39 -0
- sky/dashboard/out/_next/static/chunks/6601-fcfad0ddf92ec7ab.js +1 -0
- sky/dashboard/out/_next/static/chunks/6989-6ff4e45dfb49d11d.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-d0dc765474fa0eca.js +1 -0
- sky/dashboard/out/_next/static/chunks/8969-909d53833da080cb.js +1 -0
- sky/dashboard/out/_next/static/chunks/8982.a2e214068f30a857.js +1 -0
- sky/dashboard/out/_next/static/chunks/{25.76c246239df93d50.js → 9025.a7c44babfe56ce09.js} +2 -2
- sky/dashboard/out/_next/static/chunks/938-044ad21de8b4626b.js +1 -0
- sky/dashboard/out/_next/static/chunks/9470-21d059a1dfa03f61.js +1 -0
- sky/dashboard/out/_next/static/chunks/9984.739ae958a066298d.js +1 -0
- sky/dashboard/out/_next/static/chunks/fd9d1056-61f2257a9cd8b32b.js +1 -0
- sky/dashboard/out/_next/static/chunks/{framework-87d061ee6ed71b28.js → framework-efc06c2733009cd3.js} +1 -1
- sky/dashboard/out/_next/static/chunks/main-app-68c028b1bc5e1b72.js +1 -0
- sky/dashboard/out/_next/static/chunks/{main-e0e2335212e72357.js → main-c0a4f1ea606d48d2.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/{_app-9a3ce3170d2edcec.js → _app-a37b06ddb64521fd.js} +2 -2
- sky/dashboard/out/_next/static/chunks/pages/_error-c72a1f77a3c0be1b.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-8135aba0712bda37.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-b8e1114e6d38218c.js +6 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-9744c271a1642f76.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/config-a2673b256b6d416f.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/index-927ddeebe57a8ac3.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-8b0809f59034d509.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-ae9d2f705ce582c9.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-c4d5cfac7fbc0668.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-5bbdc71878f0a068.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-cd43fb3c122eedde.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/volumes-4ebf6484f7216387.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-5629d4e551dba1ee.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-7c0187f43757a548.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-06bde99155fa6292.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-d427db53e54de9ce.js +1 -0
- sky/dashboard/out/_next/static/css/0da6afe66176678a.css +3 -0
- sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
- sky/dashboard/out/clusters/[cluster].html +1 -1
- sky/dashboard/out/clusters.html +1 -1
- sky/dashboard/out/config.html +1 -1
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra/[context].html +1 -1
- sky/dashboard/out/infra.html +1 -1
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -1
- sky/dashboard/out/volumes.html +1 -1
- sky/dashboard/out/workspace/new.html +1 -1
- sky/dashboard/out/workspaces/[name].html +1 -1
- sky/dashboard/out/workspaces.html +1 -1
- sky/data/storage.py +8 -3
- sky/global_user_state.py +257 -9
- sky/jobs/client/sdk.py +20 -25
- sky/models.py +16 -0
- sky/optimizer.py +46 -0
- sky/provision/__init__.py +14 -6
- sky/provision/kubernetes/config.py +1 -1
- sky/provision/kubernetes/constants.py +9 -0
- sky/provision/kubernetes/instance.py +24 -18
- sky/provision/kubernetes/network.py +15 -9
- sky/provision/kubernetes/network_utils.py +42 -23
- sky/provision/kubernetes/utils.py +73 -35
- sky/provision/kubernetes/volume.py +77 -15
- sky/provision/nebius/utils.py +10 -4
- sky/resources.py +10 -4
- sky/serve/client/sdk.py +28 -34
- sky/server/common.py +51 -3
- sky/server/constants.py +3 -0
- sky/server/requests/executor.py +4 -0
- sky/server/requests/payloads.py +33 -0
- sky/server/requests/requests.py +19 -0
- sky/server/rest.py +6 -15
- sky/server/server.py +121 -6
- sky/skylet/constants.py +7 -0
- sky/skypilot_config.py +32 -4
- sky/task.py +12 -0
- sky/users/permission.py +29 -0
- sky/users/server.py +384 -5
- sky/users/token_service.py +196 -0
- sky/utils/common_utils.py +4 -5
- sky/utils/config_utils.py +41 -0
- sky/utils/controller_utils.py +5 -1
- sky/utils/log_utils.py +68 -0
- sky/utils/resource_checker.py +153 -0
- sky/utils/resources_utils.py +12 -4
- sky/utils/schemas.py +87 -60
- sky/utils/subprocess_utils.py +2 -6
- sky/volumes/server/core.py +103 -78
- sky/volumes/utils.py +22 -5
- sky/workspaces/core.py +9 -117
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/METADATA +1 -1
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/RECORD +133 -128
- sky/dashboard/out/_next/static/HudU4f4Xsy-cP51JvXSZ-/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/230-d6e363362017ff3a.js +0 -1
- sky/dashboard/out/_next/static/chunks/43-36177d00f6956ab2.js +0 -1
- sky/dashboard/out/_next/static/chunks/470-92dd1614396389be.js +0 -1
- sky/dashboard/out/_next/static/chunks/544.110e53813fb98e2e.js +0 -1
- sky/dashboard/out/_next/static/chunks/616-d6128fa9e7cae6e6.js +0 -39
- sky/dashboard/out/_next/static/chunks/645.961f08e39b8ce447.js +0 -1
- sky/dashboard/out/_next/static/chunks/664-047bc03493fda379.js +0 -1
- sky/dashboard/out/_next/static/chunks/690.55f9eed3be903f56.js +0 -16
- sky/dashboard/out/_next/static/chunks/785.dc2686c3c1235554.js +0 -1
- sky/dashboard/out/_next/static/chunks/798-c0525dc3f21e488d.js +0 -1
- sky/dashboard/out/_next/static/chunks/799-3625946b2ec2eb30.js +0 -8
- sky/dashboard/out/_next/static/chunks/871-3db673be3ee3750b.js +0 -6
- sky/dashboard/out/_next/static/chunks/937.3759f538f11a0953.js +0 -1
- sky/dashboard/out/_next/static/chunks/938-068520cc11738deb.js +0 -1
- sky/dashboard/out/_next/static/chunks/969-d3a0b53f728d280a.js +0 -1
- sky/dashboard/out/_next/static/chunks/973-81b2d057178adb76.js +0 -1
- sky/dashboard/out/_next/static/chunks/982.1b61658204416b0f.js +0 -1
- sky/dashboard/out/_next/static/chunks/984.e8bac186a24e5178.js +0 -1
- sky/dashboard/out/_next/static/chunks/989-db34c16ad7ea6155.js +0 -1
- sky/dashboard/out/_next/static/chunks/990-0ad5ea1699e03ee8.js +0 -1
- sky/dashboard/out/_next/static/chunks/fd9d1056-2821b0f0cabcd8bd.js +0 -1
- sky/dashboard/out/_next/static/chunks/main-app-241eb28595532291.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/_error-1be831200e60c5c0.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-aff040d7bc5d0086.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-8040f2483897ed0c.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/clusters-f119a5630a1efd61.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/config-6b255eae088da6a3.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/index-6b0d9e5031b70c58.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-b302aea4d65766bf.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/infra-ee8cc4d449945d19.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-e4b23128db0774cd.js +0 -16
- sky/dashboard/out/_next/static/chunks/pages/jobs-0a5695ff3075d94a.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/users-4978cbb093e141e7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/volumes-476b670ef33d1ecd.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-5b59bce9eb208d84.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-cb7e720b739de53a.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/workspaces-50e230828730cfb3.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-08fdb9e6070127fc.js +0 -1
- sky/dashboard/out/_next/static/css/52082cf558ec9705.css +0 -3
- /sky/dashboard/out/_next/static/{HudU4f4Xsy-cP51JvXSZ- → NdypbqMxaYucRGfopkKXa}/_ssgManifest.js +0 -0
- /sky/dashboard/out/_next/static/chunks/{804-4c9fc53aa74bc191.js → 804-9f5e98ce84d46bdd.js} +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250627.dist-info → skypilot_nightly-1.0.0.dev20250630.dist-info}/top_level.txt +0 -0
sky/dashboard/out/jobs.html
CHANGED
@@ -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/
|
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/0da6afe66176678a.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/0da6afe66176678a.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-d427db53e54de9ce.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-a37b06ddb64521fd.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/jobs-5bbdc71878f0a068.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/jobs","query":{},"buildId":"NdypbqMxaYucRGfopkKXa","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
sky/dashboard/out/users.html
CHANGED
@@ -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/
|
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/0da6afe66176678a.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/0da6afe66176678a.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-d427db53e54de9ce.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-a37b06ddb64521fd.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/users-cd43fb3c122eedde.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/users","query":{},"buildId":"NdypbqMxaYucRGfopkKXa","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
sky/dashboard/out/volumes.html
CHANGED
@@ -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/
|
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/0da6afe66176678a.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/0da6afe66176678a.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-d427db53e54de9ce.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-a37b06ddb64521fd.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/volumes-4ebf6484f7216387.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/volumes","query":{},"buildId":"NdypbqMxaYucRGfopkKXa","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/
|
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/0da6afe66176678a.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/0da6afe66176678a.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-d427db53e54de9ce.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-a37b06ddb64521fd.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspace/new-5629d4e551dba1ee.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspace/new","query":{},"buildId":"NdypbqMxaYucRGfopkKXa","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/
|
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/0da6afe66176678a.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/0da6afe66176678a.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-d427db53e54de9ce.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-a37b06ddb64521fd.js" defer=""></script><script src="/dashboard/_next/static/chunks/616-162f3033ffcd3d31.js" defer=""></script><script src="/dashboard/_next/static/chunks/5230-df791914b54d91d9.js" defer=""></script><script src="/dashboard/_next/static/chunks/5739-5ea3ffa10fc884f2.js" defer=""></script><script src="/dashboard/_next/static/chunks/1664-d65361e92b85e786.js" defer=""></script><script src="/dashboard/_next/static/chunks/804-9f5e98ce84d46bdd.js" defer=""></script><script src="/dashboard/_next/static/chunks/1272-1ef0bf0237faccdb.js" defer=""></script><script src="/dashboard/_next/static/chunks/3947-b059261d6fa88a1f.js" defer=""></script><script src="/dashboard/_next/static/chunks/6989-6ff4e45dfb49d11d.js" defer=""></script><script src="/dashboard/_next/static/chunks/3698-52ad1ca228faa776.js" defer=""></script><script src="/dashboard/_next/static/chunks/9470-21d059a1dfa03f61.js" defer=""></script><script src="/dashboard/_next/static/chunks/6990-d0dc765474fa0eca.js" defer=""></script><script src="/dashboard/_next/static/chunks/8969-909d53833da080cb.js" defer=""></script><script src="/dashboard/_next/static/chunks/1043-1b39779691bb4030.js" defer=""></script><script src="/dashboard/_next/static/chunks/6601-fcfad0ddf92ec7ab.js" defer=""></script><script src="/dashboard/_next/static/chunks/938-044ad21de8b4626b.js" defer=""></script><script src="/dashboard/_next/static/chunks/1141-726e5a3f00b67185.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces/%5Bname%5D-7c0187f43757a548.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces/[name]","query":{},"buildId":"NdypbqMxaYucRGfopkKXa","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/
|
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/0da6afe66176678a.css" as="style"/><link rel="stylesheet" href="/dashboard/_next/static/css/0da6afe66176678a.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-d427db53e54de9ce.js" defer=""></script><script src="/dashboard/_next/static/chunks/framework-efc06c2733009cd3.js" defer=""></script><script src="/dashboard/_next/static/chunks/main-c0a4f1ea606d48d2.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/_app-a37b06ddb64521fd.js" defer=""></script><script src="/dashboard/_next/static/chunks/pages/workspaces-06bde99155fa6292.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_buildManifest.js" defer=""></script><script src="/dashboard/_next/static/NdypbqMxaYucRGfopkKXa/_ssgManifest.js" defer=""></script></head><body><div id="__next"></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/workspaces","query":{},"buildId":"NdypbqMxaYucRGfopkKXa","assetPrefix":"/dashboard","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
sky/data/storage.py
CHANGED
@@ -1802,7 +1802,8 @@ class S3Store(AbstractStore):
|
|
1802
1802
|
|
1803
1803
|
# Add AWS tags configured in config.yaml to the bucket.
|
1804
1804
|
# This is useful for cost tracking and external cleanup.
|
1805
|
-
bucket_tags = skypilot_config.
|
1805
|
+
bucket_tags = skypilot_config.get_effective_region_config(
|
1806
|
+
cloud='aws', region=None, keys=('labels',), default_value={})
|
1806
1807
|
if bucket_tags:
|
1807
1808
|
s3_client.put_bucket_tagging(
|
1808
1809
|
Bucket=bucket_name,
|
@@ -2765,8 +2766,12 @@ class AzureBlobStore(AbstractStore):
|
|
2765
2766
|
# Creates new resource group and storage account or use the
|
2766
2767
|
# storage_account provided by the user through config.yaml
|
2767
2768
|
else:
|
2768
|
-
config_storage_account =
|
2769
|
-
(
|
2769
|
+
config_storage_account = (
|
2770
|
+
skypilot_config.get_effective_region_config(
|
2771
|
+
cloud='azure',
|
2772
|
+
region=None,
|
2773
|
+
keys=('storage_account',),
|
2774
|
+
default_value=None))
|
2770
2775
|
if config_storage_account is not None:
|
2771
2776
|
# using user provided storage account from config.yaml
|
2772
2777
|
storage_account_name = config_storage_account
|
sky/global_user_state.py
CHANGED
@@ -65,6 +65,7 @@ user_table = sqlalchemy.Table(
|
|
65
65
|
sqlalchemy.Column('id', sqlalchemy.Text, primary_key=True),
|
66
66
|
sqlalchemy.Column('name', sqlalchemy.Text),
|
67
67
|
sqlalchemy.Column('password', sqlalchemy.Text),
|
68
|
+
sqlalchemy.Column('created_at', sqlalchemy.Integer),
|
68
69
|
)
|
69
70
|
|
70
71
|
cluster_table = sqlalchemy.Table(
|
@@ -167,6 +168,21 @@ ssh_key_table = sqlalchemy.Table(
|
|
167
168
|
sqlalchemy.Column('ssh_private_key', sqlalchemy.Text),
|
168
169
|
)
|
169
170
|
|
171
|
+
service_account_token_table = sqlalchemy.Table(
|
172
|
+
'service_account_tokens',
|
173
|
+
Base.metadata,
|
174
|
+
sqlalchemy.Column('token_id', sqlalchemy.Text, primary_key=True),
|
175
|
+
sqlalchemy.Column('token_name', sqlalchemy.Text),
|
176
|
+
sqlalchemy.Column('token_hash', sqlalchemy.Text),
|
177
|
+
sqlalchemy.Column('created_at', sqlalchemy.Integer),
|
178
|
+
sqlalchemy.Column('last_used_at', sqlalchemy.Integer, server_default=None),
|
179
|
+
sqlalchemy.Column('expires_at', sqlalchemy.Integer, server_default=None),
|
180
|
+
sqlalchemy.Column('creator_user_hash',
|
181
|
+
sqlalchemy.Text), # Who created this token
|
182
|
+
sqlalchemy.Column('service_account_user_id',
|
183
|
+
sqlalchemy.Text), # Service account's own user ID
|
184
|
+
)
|
185
|
+
|
170
186
|
cluster_yaml_table = sqlalchemy.Table(
|
171
187
|
'cluster_yaml',
|
172
188
|
Base.metadata,
|
@@ -174,6 +190,15 @@ cluster_yaml_table = sqlalchemy.Table(
|
|
174
190
|
sqlalchemy.Column('yaml', sqlalchemy.Text),
|
175
191
|
)
|
176
192
|
|
193
|
+
system_config_table = sqlalchemy.Table(
|
194
|
+
'system_config',
|
195
|
+
Base.metadata,
|
196
|
+
sqlalchemy.Column('config_key', sqlalchemy.Text, primary_key=True),
|
197
|
+
sqlalchemy.Column('config_value', sqlalchemy.Text),
|
198
|
+
sqlalchemy.Column('created_at', sqlalchemy.Integer),
|
199
|
+
sqlalchemy.Column('updated_at', sqlalchemy.Integer),
|
200
|
+
)
|
201
|
+
|
177
202
|
|
178
203
|
def _glob_to_similar(glob_pattern):
|
179
204
|
"""Converts a glob pattern to a PostgreSQL LIKE pattern."""
|
@@ -331,6 +356,12 @@ def create_table():
|
|
331
356
|
'password',
|
332
357
|
sqlalchemy.Text(),
|
333
358
|
default_statement='DEFAULT NULL')
|
359
|
+
db_utils.add_column_to_table_sqlalchemy(
|
360
|
+
session,
|
361
|
+
'users',
|
362
|
+
'created_at',
|
363
|
+
sqlalchemy.Integer(),
|
364
|
+
default_statement='DEFAULT NULL')
|
334
365
|
|
335
366
|
db_utils.add_column_to_table_sqlalchemy(
|
336
367
|
session,
|
@@ -383,7 +414,8 @@ def _init_db(func):
|
|
383
414
|
|
384
415
|
|
385
416
|
@_init_db
|
386
|
-
def add_or_update_user(user: models.User
|
417
|
+
def add_or_update_user(user: models.User,
|
418
|
+
allow_duplicate_name: bool = True) -> bool:
|
387
419
|
"""Store the mapping from user hash to user name for display purposes.
|
388
420
|
|
389
421
|
Returns:
|
@@ -394,7 +426,18 @@ def add_or_update_user(user: models.User) -> bool:
|
|
394
426
|
if user.name is None:
|
395
427
|
return False
|
396
428
|
|
429
|
+
# Set created_at if not already set
|
430
|
+
created_at = user.created_at
|
431
|
+
if created_at is None:
|
432
|
+
created_at = int(time.time())
|
397
433
|
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
434
|
+
# Check for duplicate names if not allowed (within the same transaction)
|
435
|
+
if not allow_duplicate_name:
|
436
|
+
existing_user = session.query(user_table).filter(
|
437
|
+
user_table.c.name == user.name).first()
|
438
|
+
if existing_user is not None:
|
439
|
+
return False
|
440
|
+
|
398
441
|
if (_SQLALCHEMY_ENGINE.dialect.name ==
|
399
442
|
db_utils.SQLAlchemyDialect.SQLITE.value):
|
400
443
|
# For SQLite, use INSERT OR IGNORE followed by UPDATE to detect new
|
@@ -405,14 +448,15 @@ def add_or_update_user(user: models.User) -> bool:
|
|
405
448
|
insert_stmnt = insert_func(user_table).prefix_with(
|
406
449
|
'OR IGNORE').values(id=user.id,
|
407
450
|
name=user.name,
|
408
|
-
password=user.password
|
451
|
+
password=user.password,
|
452
|
+
created_at=created_at)
|
409
453
|
result = session.execute(insert_stmnt)
|
410
454
|
|
411
455
|
# Check if the INSERT actually inserted a row
|
412
456
|
was_inserted = result.rowcount > 0
|
413
457
|
|
414
458
|
if not was_inserted:
|
415
|
-
# User existed, so update it
|
459
|
+
# User existed, so update it (but don't update created_at)
|
416
460
|
if user.password:
|
417
461
|
session.query(user_table).filter_by(id=user.id).update({
|
418
462
|
user_table.c.name: user.name,
|
@@ -430,8 +474,12 @@ def add_or_update_user(user: models.User) -> bool:
|
|
430
474
|
# For PostgreSQL, use INSERT ... ON CONFLICT with RETURNING to
|
431
475
|
# detect insert vs update
|
432
476
|
insert_func = postgresql.insert
|
477
|
+
|
433
478
|
insert_stmnt = insert_func(user_table).values(
|
434
|
-
id=user.id,
|
479
|
+
id=user.id,
|
480
|
+
name=user.name,
|
481
|
+
password=user.password,
|
482
|
+
created_at=created_at)
|
435
483
|
|
436
484
|
# Use a sentinel in the RETURNING clause to detect insert vs update
|
437
485
|
if user.password:
|
@@ -464,7 +512,10 @@ def get_user(user_id: str) -> Optional[models.User]:
|
|
464
512
|
row = session.query(user_table).filter_by(id=user_id).first()
|
465
513
|
if row is None:
|
466
514
|
return None
|
467
|
-
return models.User(id=row.id,
|
515
|
+
return models.User(id=row.id,
|
516
|
+
name=row.name,
|
517
|
+
password=row.password,
|
518
|
+
created_at=row.created_at)
|
468
519
|
|
469
520
|
|
470
521
|
def get_user_by_name(username: str) -> List[models.User]:
|
@@ -473,8 +524,10 @@ def get_user_by_name(username: str) -> List[models.User]:
|
|
473
524
|
if len(rows) == 0:
|
474
525
|
return []
|
475
526
|
return [
|
476
|
-
models.User(id=row.id,
|
477
|
-
|
527
|
+
models.User(id=row.id,
|
528
|
+
name=row.name,
|
529
|
+
password=row.password,
|
530
|
+
created_at=row.created_at) for row in rows
|
478
531
|
]
|
479
532
|
|
480
533
|
|
@@ -490,8 +543,10 @@ def get_all_users() -> List[models.User]:
|
|
490
543
|
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
491
544
|
rows = session.query(user_table).all()
|
492
545
|
return [
|
493
|
-
models.User(id=row.id,
|
494
|
-
|
546
|
+
models.User(id=row.id,
|
547
|
+
name=row.name,
|
548
|
+
password=row.password,
|
549
|
+
created_at=row.created_at) for row in rows
|
495
550
|
]
|
496
551
|
|
497
552
|
|
@@ -1592,6 +1647,137 @@ def set_ssh_keys(user_hash: str, ssh_public_key: str, ssh_private_key: str):
|
|
1592
1647
|
session.commit()
|
1593
1648
|
|
1594
1649
|
|
1650
|
+
@_init_db
|
1651
|
+
def add_service_account_token(token_id: str,
|
1652
|
+
token_name: str,
|
1653
|
+
token_hash: str,
|
1654
|
+
creator_user_hash: str,
|
1655
|
+
service_account_user_id: str,
|
1656
|
+
expires_at: Optional[int] = None) -> None:
|
1657
|
+
"""Add a service account token to the database."""
|
1658
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1659
|
+
created_at = int(time.time())
|
1660
|
+
|
1661
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1662
|
+
if (_SQLALCHEMY_ENGINE.dialect.name ==
|
1663
|
+
db_utils.SQLAlchemyDialect.SQLITE.value):
|
1664
|
+
insert_func = sqlite.insert
|
1665
|
+
elif (_SQLALCHEMY_ENGINE.dialect.name ==
|
1666
|
+
db_utils.SQLAlchemyDialect.POSTGRESQL.value):
|
1667
|
+
insert_func = postgresql.insert
|
1668
|
+
else:
|
1669
|
+
raise ValueError('Unsupported database dialect')
|
1670
|
+
|
1671
|
+
insert_stmnt = insert_func(service_account_token_table).values(
|
1672
|
+
token_id=token_id,
|
1673
|
+
token_name=token_name,
|
1674
|
+
token_hash=token_hash,
|
1675
|
+
created_at=created_at,
|
1676
|
+
expires_at=expires_at,
|
1677
|
+
creator_user_hash=creator_user_hash,
|
1678
|
+
service_account_user_id=service_account_user_id)
|
1679
|
+
session.execute(insert_stmnt)
|
1680
|
+
session.commit()
|
1681
|
+
|
1682
|
+
|
1683
|
+
@_init_db
|
1684
|
+
def get_service_account_token(token_id: str) -> Optional[Dict[str, Any]]:
|
1685
|
+
"""Get a service account token by token_id."""
|
1686
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1687
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1688
|
+
row = session.query(service_account_token_table).filter_by(
|
1689
|
+
token_id=token_id).first()
|
1690
|
+
if row is None:
|
1691
|
+
return None
|
1692
|
+
return {
|
1693
|
+
'token_id': row.token_id,
|
1694
|
+
'token_name': row.token_name,
|
1695
|
+
'token_hash': row.token_hash,
|
1696
|
+
'created_at': row.created_at,
|
1697
|
+
'last_used_at': row.last_used_at,
|
1698
|
+
'expires_at': row.expires_at,
|
1699
|
+
'creator_user_hash': row.creator_user_hash,
|
1700
|
+
'service_account_user_id': row.service_account_user_id,
|
1701
|
+
}
|
1702
|
+
|
1703
|
+
|
1704
|
+
@_init_db
|
1705
|
+
def get_user_service_account_tokens(user_hash: str) -> List[Dict[str, Any]]:
|
1706
|
+
"""Get all service account tokens for a user (as creator)."""
|
1707
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1708
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1709
|
+
rows = session.query(service_account_token_table).filter_by(
|
1710
|
+
creator_user_hash=user_hash).all()
|
1711
|
+
return [{
|
1712
|
+
'token_id': row.token_id,
|
1713
|
+
'token_name': row.token_name,
|
1714
|
+
'token_hash': row.token_hash,
|
1715
|
+
'created_at': row.created_at,
|
1716
|
+
'last_used_at': row.last_used_at,
|
1717
|
+
'expires_at': row.expires_at,
|
1718
|
+
'creator_user_hash': row.creator_user_hash,
|
1719
|
+
'service_account_user_id': row.service_account_user_id,
|
1720
|
+
} for row in rows]
|
1721
|
+
|
1722
|
+
|
1723
|
+
@_init_db
|
1724
|
+
def update_service_account_token_last_used(token_id: str) -> None:
|
1725
|
+
"""Update the last_used_at timestamp for a service account token."""
|
1726
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1727
|
+
last_used_at = int(time.time())
|
1728
|
+
|
1729
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1730
|
+
session.query(service_account_token_table).filter_by(
|
1731
|
+
token_id=token_id).update(
|
1732
|
+
{service_account_token_table.c.last_used_at: last_used_at})
|
1733
|
+
session.commit()
|
1734
|
+
|
1735
|
+
|
1736
|
+
@_init_db
|
1737
|
+
def delete_service_account_token(token_id: str) -> bool:
|
1738
|
+
"""Delete a service account token.
|
1739
|
+
|
1740
|
+
Returns:
|
1741
|
+
True if token was found and deleted.
|
1742
|
+
"""
|
1743
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1744
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1745
|
+
result = session.query(service_account_token_table).filter_by(
|
1746
|
+
token_id=token_id).delete()
|
1747
|
+
session.commit()
|
1748
|
+
return result > 0
|
1749
|
+
|
1750
|
+
|
1751
|
+
@_init_db
|
1752
|
+
def rotate_service_account_token(token_id: str,
|
1753
|
+
new_token_hash: str,
|
1754
|
+
new_expires_at: Optional[int] = None) -> None:
|
1755
|
+
"""Rotate a service account token by updating its hash and expiration.
|
1756
|
+
|
1757
|
+
Args:
|
1758
|
+
token_id: The token ID to rotate.
|
1759
|
+
new_token_hash: The new hashed token value.
|
1760
|
+
new_expires_at: New expiration timestamp, or None for no expiration.
|
1761
|
+
"""
|
1762
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1763
|
+
current_time = int(time.time())
|
1764
|
+
|
1765
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1766
|
+
count = session.query(service_account_token_table).filter_by(
|
1767
|
+
token_id=token_id
|
1768
|
+
).update({
|
1769
|
+
service_account_token_table.c.token_hash: new_token_hash,
|
1770
|
+
service_account_token_table.c.expires_at: new_expires_at,
|
1771
|
+
service_account_token_table.c.last_used_at: None, # Reset last used
|
1772
|
+
# Update creation time
|
1773
|
+
service_account_token_table.c.created_at: current_time,
|
1774
|
+
})
|
1775
|
+
session.commit()
|
1776
|
+
|
1777
|
+
if count == 0:
|
1778
|
+
raise ValueError(f'Service account token {token_id} not found.')
|
1779
|
+
|
1780
|
+
|
1595
1781
|
@_init_db
|
1596
1782
|
def get_cluster_yaml_str(cluster_yaml_path: Optional[str]) -> Optional[str]:
|
1597
1783
|
"""Get the cluster yaml from the database or the local file system.
|
@@ -1662,3 +1848,65 @@ def remove_cluster_yaml(cluster_name: str):
|
|
1662
1848
|
session.query(cluster_yaml_table).filter_by(
|
1663
1849
|
cluster_name=cluster_name).delete()
|
1664
1850
|
session.commit()
|
1851
|
+
|
1852
|
+
|
1853
|
+
@_init_db
|
1854
|
+
def get_all_service_account_tokens() -> List[Dict[str, Any]]:
|
1855
|
+
"""Get all service account tokens across all users (for admin access)."""
|
1856
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1857
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1858
|
+
rows = session.query(service_account_token_table).all()
|
1859
|
+
return [{
|
1860
|
+
'token_id': row.token_id,
|
1861
|
+
'token_name': row.token_name,
|
1862
|
+
'token_hash': row.token_hash,
|
1863
|
+
'created_at': row.created_at,
|
1864
|
+
'last_used_at': row.last_used_at,
|
1865
|
+
'expires_at': row.expires_at,
|
1866
|
+
'creator_user_hash': row.creator_user_hash,
|
1867
|
+
'service_account_user_id': row.service_account_user_id,
|
1868
|
+
} for row in rows]
|
1869
|
+
|
1870
|
+
|
1871
|
+
@_init_db
|
1872
|
+
def get_system_config(config_key: str) -> Optional[str]:
|
1873
|
+
"""Get a system configuration value by key."""
|
1874
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1875
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1876
|
+
row = session.query(system_config_table).filter_by(
|
1877
|
+
config_key=config_key).first()
|
1878
|
+
if row is None:
|
1879
|
+
return None
|
1880
|
+
return row.config_value
|
1881
|
+
|
1882
|
+
|
1883
|
+
@_init_db
|
1884
|
+
def set_system_config(config_key: str, config_value: str) -> None:
|
1885
|
+
"""Set a system configuration value."""
|
1886
|
+
assert _SQLALCHEMY_ENGINE is not None
|
1887
|
+
current_time = int(time.time())
|
1888
|
+
|
1889
|
+
with orm.Session(_SQLALCHEMY_ENGINE) as session:
|
1890
|
+
if (_SQLALCHEMY_ENGINE.dialect.name ==
|
1891
|
+
db_utils.SQLAlchemyDialect.SQLITE.value):
|
1892
|
+
insert_func = sqlite.insert
|
1893
|
+
elif (_SQLALCHEMY_ENGINE.dialect.name ==
|
1894
|
+
db_utils.SQLAlchemyDialect.POSTGRESQL.value):
|
1895
|
+
insert_func = postgresql.insert
|
1896
|
+
else:
|
1897
|
+
raise ValueError('Unsupported database dialect')
|
1898
|
+
|
1899
|
+
insert_stmnt = insert_func(system_config_table).values(
|
1900
|
+
config_key=config_key,
|
1901
|
+
config_value=config_value,
|
1902
|
+
created_at=current_time,
|
1903
|
+
updated_at=current_time)
|
1904
|
+
|
1905
|
+
upsert_stmnt = insert_stmnt.on_conflict_do_update(
|
1906
|
+
index_elements=[system_config_table.c.config_key],
|
1907
|
+
set_={
|
1908
|
+
system_config_table.c.config_value: config_value,
|
1909
|
+
system_config_table.c.updated_at: current_time,
|
1910
|
+
})
|
1911
|
+
session.execute(upsert_stmnt)
|
1912
|
+
session.commit()
|
sky/jobs/client/sdk.py
CHANGED
@@ -82,12 +82,11 @@ def launch(
|
|
82
82
|
task=dag_str,
|
83
83
|
name=name,
|
84
84
|
)
|
85
|
-
response =
|
86
|
-
|
85
|
+
response = server_common.make_authenticated_request(
|
86
|
+
'POST',
|
87
|
+
'/jobs/launch',
|
87
88
|
json=json.loads(body.model_dump_json()),
|
88
|
-
timeout=(5, None)
|
89
|
-
cookies=server_common.get_api_cookie_jar(),
|
90
|
-
)
|
89
|
+
timeout=(5, None))
|
91
90
|
return server_common.get_request_id(response)
|
92
91
|
|
93
92
|
|
@@ -142,12 +141,11 @@ def queue(refresh: bool,
|
|
142
141
|
all_users=all_users,
|
143
142
|
job_ids=job_ids,
|
144
143
|
)
|
145
|
-
response =
|
146
|
-
|
144
|
+
response = server_common.make_authenticated_request(
|
145
|
+
'POST',
|
146
|
+
'/jobs/queue',
|
147
147
|
json=json.loads(body.model_dump_json()),
|
148
|
-
timeout=(5, None)
|
149
|
-
cookies=server_common.get_api_cookie_jar(),
|
150
|
-
)
|
148
|
+
timeout=(5, None))
|
151
149
|
return server_common.get_request_id(response=response)
|
152
150
|
|
153
151
|
|
@@ -182,12 +180,11 @@ def cancel(
|
|
182
180
|
all=all,
|
183
181
|
all_users=all_users,
|
184
182
|
)
|
185
|
-
response =
|
186
|
-
|
183
|
+
response = server_common.make_authenticated_request(
|
184
|
+
'POST',
|
185
|
+
'/jobs/cancel',
|
187
186
|
json=json.loads(body.model_dump_json()),
|
188
|
-
timeout=(5, None)
|
189
|
-
cookies=server_common.get_api_cookie_jar(),
|
190
|
-
)
|
187
|
+
timeout=(5, None))
|
191
188
|
return server_common.get_request_id(response=response)
|
192
189
|
|
193
190
|
|
@@ -233,13 +230,12 @@ def tail_logs(name: Optional[str] = None,
|
|
233
230
|
refresh=refresh,
|
234
231
|
tail=tail,
|
235
232
|
)
|
236
|
-
response =
|
237
|
-
|
233
|
+
response = server_common.make_authenticated_request(
|
234
|
+
'POST',
|
235
|
+
'/jobs/logs',
|
238
236
|
json=json.loads(body.model_dump_json()),
|
239
237
|
stream=True,
|
240
|
-
timeout=(5, None)
|
241
|
-
cookies=server_common.get_api_cookie_jar(),
|
242
|
-
)
|
238
|
+
timeout=(5, None))
|
243
239
|
request_id = server_common.get_request_id(response)
|
244
240
|
# Log request is idempotent when tail is 0, thus can resume previous
|
245
241
|
# streaming point on retry.
|
@@ -283,12 +279,11 @@ def download_logs(
|
|
283
279
|
controller=controller,
|
284
280
|
local_dir=local_dir,
|
285
281
|
)
|
286
|
-
response =
|
287
|
-
|
282
|
+
response = server_common.make_authenticated_request(
|
283
|
+
'POST',
|
284
|
+
'/jobs/download_logs',
|
288
285
|
json=json.loads(body.model_dump_json()),
|
289
|
-
timeout=(5, None)
|
290
|
-
cookies=server_common.get_api_cookie_jar(),
|
291
|
-
)
|
286
|
+
timeout=(5, None))
|
292
287
|
job_id_remote_path_dict = sdk.stream_and_get(
|
293
288
|
server_common.get_request_id(response))
|
294
289
|
remote2local_path_dict = client_common.download_logs_from_api_server(
|
sky/models.py
CHANGED
@@ -20,6 +20,18 @@ class User:
|
|
20
20
|
# Display name of the user
|
21
21
|
name: Optional[str] = None
|
22
22
|
password: Optional[str] = None
|
23
|
+
created_at: Optional[int] = None
|
24
|
+
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
id: str, # pylint: disable=redefined-builtin
|
28
|
+
name: Optional[str] = None,
|
29
|
+
password: Optional[str] = None,
|
30
|
+
created_at: Optional[int] = None):
|
31
|
+
self.id = id.strip().lower()
|
32
|
+
self.name = name
|
33
|
+
self.password = password
|
34
|
+
self.created_at = created_at
|
23
35
|
|
24
36
|
def to_dict(self) -> Dict[str, Any]:
|
25
37
|
return {'id': self.id, 'name': self.name}
|
@@ -37,6 +49,10 @@ class User:
|
|
37
49
|
user_hash = common_utils.get_user_hash()
|
38
50
|
return User(id=user_hash, name=user_name)
|
39
51
|
|
52
|
+
def is_service_account(self) -> bool:
|
53
|
+
"""Check if the user is a service account."""
|
54
|
+
return self.id.lower().startswith('sa-')
|
55
|
+
|
40
56
|
|
41
57
|
RealtimeGpuAvailability = collections.namedtuple(
|
42
58
|
'RealtimeGpuAvailability', ['gpu', 'counts', 'capacity', 'available'])
|
sky/optimizer.py
CHANGED
@@ -1252,6 +1252,52 @@ def _check_specified_clouds(dag: 'dag_lib.Dag') -> None:
|
|
1252
1252
|
logger.warning(
|
1253
1253
|
f'{colorama.Fore.YELLOW}{msg}{colorama.Style.RESET_ALL}')
|
1254
1254
|
|
1255
|
+
_check_specified_regions(task)
|
1256
|
+
|
1257
|
+
|
1258
|
+
def _check_specified_regions(task: task_lib.Task) -> None:
|
1259
|
+
"""Check if specified regions (Kubernetes contexts) are enabled.
|
1260
|
+
|
1261
|
+
Args:
|
1262
|
+
task: The task to check.
|
1263
|
+
"""
|
1264
|
+
# Only check for Kubernetes now
|
1265
|
+
if not all(
|
1266
|
+
isinstance(resources.cloud, clouds.Kubernetes)
|
1267
|
+
for resources in task.resources):
|
1268
|
+
return
|
1269
|
+
# Kubernetes region is a context if set
|
1270
|
+
for resources in task.resources:
|
1271
|
+
if resources.region is None:
|
1272
|
+
continue
|
1273
|
+
existing_contexts = clouds.Kubernetes.existing_allowed_contexts()
|
1274
|
+
region = resources.region
|
1275
|
+
task_name = f' {task.name!r}' if task.name is not None else ''
|
1276
|
+
msg = f'Task{task_name} requires '
|
1277
|
+
if region not in existing_contexts:
|
1278
|
+
infra_str = f'Kubernetes/{region}'
|
1279
|
+
logger.warning(f'{infra_str} is not enabled.')
|
1280
|
+
volume_mounts_str = ''
|
1281
|
+
if task.volume_mounts:
|
1282
|
+
if len(task.volume_mounts) > 1:
|
1283
|
+
volume_mounts_str += 'volumes '
|
1284
|
+
else:
|
1285
|
+
volume_mounts_str += 'volume '
|
1286
|
+
volume_mounts_str += ', '.join(
|
1287
|
+
[f'{v.volume_name}' for v in task.volume_mounts])
|
1288
|
+
volume_mounts_str += f' with infra {infra_str}'
|
1289
|
+
if volume_mounts_str:
|
1290
|
+
msg += volume_mounts_str
|
1291
|
+
else:
|
1292
|
+
msg += f'infra {infra_str}'
|
1293
|
+
msg += (
|
1294
|
+
f' which is not enabled. To enable access, change '
|
1295
|
+
f'the task infra requirement or run: {colorama.Style.BRIGHT}'
|
1296
|
+
f'sky check {colorama.Style.RESET_ALL}'
|
1297
|
+
f'to ensure the infra is enabled.')
|
1298
|
+
with ux_utils.print_exception_no_traceback():
|
1299
|
+
raise exceptions.ResourcesUnavailableError(msg)
|
1300
|
+
|
1255
1301
|
|
1256
1302
|
def _fill_in_launchable_resources(
|
1257
1303
|
task: task_lib.Task,
|