skypilot-nightly 1.0.0.dev20250502__py3-none-any.whl → 1.0.0.dev20251203__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 +22 -6
- sky/adaptors/aws.py +81 -16
- sky/adaptors/common.py +25 -2
- sky/adaptors/coreweave.py +278 -0
- sky/adaptors/do.py +8 -2
- sky/adaptors/gcp.py +11 -0
- sky/adaptors/hyperbolic.py +8 -0
- sky/adaptors/ibm.py +5 -2
- sky/adaptors/kubernetes.py +149 -18
- sky/adaptors/nebius.py +173 -30
- sky/adaptors/primeintellect.py +1 -0
- sky/adaptors/runpod.py +68 -0
- sky/adaptors/seeweb.py +183 -0
- sky/adaptors/shadeform.py +89 -0
- sky/admin_policy.py +187 -4
- sky/authentication.py +179 -225
- sky/backends/__init__.py +4 -2
- sky/backends/backend.py +22 -9
- sky/backends/backend_utils.py +1323 -397
- sky/backends/cloud_vm_ray_backend.py +1749 -1029
- sky/backends/docker_utils.py +1 -1
- sky/backends/local_docker_backend.py +11 -6
- sky/backends/task_codegen.py +633 -0
- sky/backends/wheel_utils.py +55 -9
- sky/{clouds/service_catalog → catalog}/__init__.py +21 -19
- sky/{clouds/service_catalog → catalog}/aws_catalog.py +27 -8
- sky/{clouds/service_catalog → catalog}/azure_catalog.py +10 -7
- sky/{clouds/service_catalog → catalog}/common.py +90 -49
- sky/{clouds/service_catalog → catalog}/cudo_catalog.py +8 -5
- sky/{clouds/service_catalog → catalog}/data_fetchers/analyze.py +1 -1
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_aws.py +116 -80
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_cudo.py +38 -38
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_gcp.py +70 -16
- sky/catalog/data_fetchers/fetch_hyperbolic.py +136 -0
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_lambda_cloud.py +1 -0
- sky/catalog/data_fetchers/fetch_nebius.py +338 -0
- sky/catalog/data_fetchers/fetch_runpod.py +698 -0
- sky/catalog/data_fetchers/fetch_seeweb.py +329 -0
- sky/catalog/data_fetchers/fetch_shadeform.py +142 -0
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_vast.py +1 -1
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_vsphere.py +1 -1
- sky/{clouds/service_catalog → catalog}/do_catalog.py +5 -2
- sky/{clouds/service_catalog → catalog}/fluidstack_catalog.py +6 -3
- sky/{clouds/service_catalog → catalog}/gcp_catalog.py +41 -15
- sky/catalog/hyperbolic_catalog.py +136 -0
- sky/{clouds/service_catalog → catalog}/ibm_catalog.py +9 -6
- sky/{clouds/service_catalog → catalog}/kubernetes_catalog.py +36 -24
- sky/{clouds/service_catalog → catalog}/lambda_catalog.py +9 -6
- sky/{clouds/service_catalog → catalog}/nebius_catalog.py +9 -7
- sky/{clouds/service_catalog → catalog}/oci_catalog.py +9 -6
- sky/{clouds/service_catalog → catalog}/paperspace_catalog.py +5 -2
- sky/catalog/primeintellect_catalog.py +95 -0
- sky/{clouds/service_catalog → catalog}/runpod_catalog.py +11 -4
- sky/{clouds/service_catalog → catalog}/scp_catalog.py +9 -6
- sky/catalog/seeweb_catalog.py +184 -0
- sky/catalog/shadeform_catalog.py +165 -0
- sky/catalog/ssh_catalog.py +167 -0
- sky/{clouds/service_catalog → catalog}/vast_catalog.py +6 -3
- sky/{clouds/service_catalog → catalog}/vsphere_catalog.py +5 -2
- sky/check.py +533 -185
- sky/cli.py +5 -5975
- sky/client/{cli.py → cli/command.py} +2591 -1956
- sky/client/cli/deprecation_utils.py +99 -0
- sky/client/cli/flags.py +359 -0
- sky/client/cli/table_utils.py +322 -0
- sky/client/cli/utils.py +79 -0
- sky/client/common.py +78 -32
- sky/client/oauth.py +82 -0
- sky/client/sdk.py +1219 -319
- sky/client/sdk_async.py +827 -0
- sky/client/service_account_auth.py +47 -0
- sky/cloud_stores.py +82 -3
- sky/clouds/__init__.py +13 -0
- sky/clouds/aws.py +564 -164
- sky/clouds/azure.py +105 -83
- sky/clouds/cloud.py +140 -40
- sky/clouds/cudo.py +68 -50
- sky/clouds/do.py +66 -48
- sky/clouds/fluidstack.py +63 -44
- sky/clouds/gcp.py +339 -110
- sky/clouds/hyperbolic.py +293 -0
- sky/clouds/ibm.py +70 -49
- sky/clouds/kubernetes.py +570 -162
- sky/clouds/lambda_cloud.py +74 -54
- sky/clouds/nebius.py +210 -81
- sky/clouds/oci.py +88 -66
- sky/clouds/paperspace.py +61 -44
- sky/clouds/primeintellect.py +317 -0
- sky/clouds/runpod.py +164 -74
- sky/clouds/scp.py +89 -86
- sky/clouds/seeweb.py +477 -0
- sky/clouds/shadeform.py +400 -0
- sky/clouds/ssh.py +263 -0
- sky/clouds/utils/aws_utils.py +10 -4
- sky/clouds/utils/gcp_utils.py +87 -11
- sky/clouds/utils/oci_utils.py +38 -14
- sky/clouds/utils/scp_utils.py +231 -167
- sky/clouds/vast.py +99 -77
- sky/clouds/vsphere.py +51 -40
- sky/core.py +375 -173
- sky/dag.py +15 -0
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/96_E2yl3QAiIJGOYCkSpB/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/1141-e6aa9ab418717c59.js +11 -0
- sky/dashboard/out/_next/static/chunks/1272-1ef0bf0237faccdb.js +1 -0
- sky/dashboard/out/_next/static/chunks/1871-7e202677c42f43fe.js +6 -0
- sky/dashboard/out/_next/static/chunks/2260-7703229c33c5ebd5.js +1 -0
- sky/dashboard/out/_next/static/chunks/2350.fab69e61bac57b23.js +1 -0
- sky/dashboard/out/_next/static/chunks/2369.fc20f0c2c8ed9fe7.js +15 -0
- sky/dashboard/out/_next/static/chunks/2755.edd818326d489a1d.js +26 -0
- sky/dashboard/out/_next/static/chunks/3294.20a8540fe697d5ee.js +1 -0
- sky/dashboard/out/_next/static/chunks/3785.7e245f318f9d1121.js +1 -0
- sky/dashboard/out/_next/static/chunks/3800-7b45f9fbb6308557.js +1 -0
- sky/dashboard/out/_next/static/chunks/3850-ff4a9a69d978632b.js +1 -0
- sky/dashboard/out/_next/static/chunks/3937.210053269f121201.js +1 -0
- sky/dashboard/out/_next/static/chunks/4725.172ede95d1b21022.js +1 -0
- sky/dashboard/out/_next/static/chunks/4937.a2baa2df5572a276.js +15 -0
- sky/dashboard/out/_next/static/chunks/5739-d67458fcb1386c92.js +8 -0
- sky/dashboard/out/_next/static/chunks/6130-2be46d70a38f1e82.js +1 -0
- sky/dashboard/out/_next/static/chunks/616-3d59f75e2ccf9321.js +39 -0
- sky/dashboard/out/_next/static/chunks/6212-7bd06f60ba693125.js +13 -0
- sky/dashboard/out/_next/static/chunks/6856-8f27d1c10c98def8.js +1 -0
- sky/dashboard/out/_next/static/chunks/6989-01359c57e018caa4.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-9146207c4567fdfd.js +1 -0
- sky/dashboard/out/_next/static/chunks/7359-c8d04e06886000b3.js +30 -0
- sky/dashboard/out/_next/static/chunks/7411-b15471acd2cba716.js +41 -0
- sky/dashboard/out/_next/static/chunks/7615-019513abc55b3b47.js +1 -0
- sky/dashboard/out/_next/static/chunks/8640.5b9475a2d18c5416.js +16 -0
- sky/dashboard/out/_next/static/chunks/8969-452f9d5cbdd2dc73.js +1 -0
- sky/dashboard/out/_next/static/chunks/9025.fa408f3242e9028d.js +6 -0
- sky/dashboard/out/_next/static/chunks/9353-cff34f7e773b2e2b.js +1 -0
- sky/dashboard/out/_next/static/chunks/9360.a536cf6b1fa42355.js +31 -0
- sky/dashboard/out/_next/static/chunks/9847.3aaca6bb33455140.js +30 -0
- sky/dashboard/out/_next/static/chunks/fd9d1056-86323a29a8f7e46a.js +1 -0
- sky/dashboard/out/_next/static/chunks/framework-cf60a09ccd051a10.js +33 -0
- sky/dashboard/out/_next/static/chunks/main-app-587214043926b3cc.js +1 -0
- sky/dashboard/out/_next/static/chunks/main-f15ccb73239a3bf1.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/_app-bde01e4a2beec258.js +34 -0
- sky/dashboard/out/_next/static/chunks/pages/_error-c66a4e8afc46f17b.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-792db96d918c98c9.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-abfcac9c137aa543.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-ee39056f9851a3ff.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/config-dfb9bf07b13045f4.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/index-444f1804401f04ea.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra/[context]-c0b5935149902e6f.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/infra-aed0ea19df7cf961.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-d66997e2bfc837cf.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-9faf940b253e3e06.js +21 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-2072b48b617989c9.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-f42674164aa73423.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/volumes-b84b948ff357c43e.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-84a40f8c7c627fe4.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces-531b2f8c4bf89f82.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-64e05f17bf2cf8ce.js +1 -0
- sky/dashboard/out/_next/static/css/0748ce22df867032.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 -0
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra/[context].html +1 -0
- sky/dashboard/out/infra.html +1 -0
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs/pools/[pool].html +1 -0
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -0
- sky/dashboard/out/volumes.html +1 -0
- sky/dashboard/out/workspace/new.html +1 -0
- sky/dashboard/out/workspaces/[name].html +1 -0
- sky/dashboard/out/workspaces.html +1 -0
- sky/data/data_utils.py +137 -1
- sky/data/mounting_utils.py +269 -84
- sky/data/storage.py +1460 -1807
- sky/data/storage_utils.py +43 -57
- sky/exceptions.py +126 -2
- sky/execution.py +216 -63
- sky/global_user_state.py +2390 -586
- sky/jobs/__init__.py +7 -0
- sky/jobs/client/sdk.py +300 -58
- sky/jobs/client/sdk_async.py +161 -0
- sky/jobs/constants.py +15 -8
- sky/jobs/controller.py +848 -275
- sky/jobs/file_content_utils.py +128 -0
- sky/jobs/log_gc.py +193 -0
- sky/jobs/recovery_strategy.py +402 -152
- sky/jobs/scheduler.py +314 -189
- sky/jobs/server/core.py +836 -255
- sky/jobs/server/server.py +156 -115
- sky/jobs/server/utils.py +136 -0
- sky/jobs/state.py +2109 -706
- sky/jobs/utils.py +1306 -215
- sky/logs/__init__.py +21 -0
- sky/logs/agent.py +108 -0
- sky/logs/aws.py +243 -0
- sky/logs/gcp.py +91 -0
- sky/metrics/__init__.py +0 -0
- sky/metrics/utils.py +453 -0
- sky/models.py +78 -1
- sky/optimizer.py +164 -70
- sky/provision/__init__.py +90 -4
- sky/provision/aws/config.py +147 -26
- sky/provision/aws/instance.py +136 -50
- sky/provision/azure/instance.py +11 -6
- sky/provision/common.py +13 -1
- sky/provision/cudo/cudo_machine_type.py +1 -1
- sky/provision/cudo/cudo_utils.py +14 -8
- sky/provision/cudo/cudo_wrapper.py +72 -71
- sky/provision/cudo/instance.py +10 -6
- sky/provision/do/instance.py +10 -6
- sky/provision/do/utils.py +4 -3
- sky/provision/docker_utils.py +140 -33
- sky/provision/fluidstack/instance.py +13 -8
- sky/provision/gcp/__init__.py +1 -0
- sky/provision/gcp/config.py +301 -19
- sky/provision/gcp/constants.py +218 -0
- sky/provision/gcp/instance.py +36 -8
- sky/provision/gcp/instance_utils.py +18 -4
- sky/provision/gcp/volume_utils.py +247 -0
- sky/provision/hyperbolic/__init__.py +12 -0
- sky/provision/hyperbolic/config.py +10 -0
- sky/provision/hyperbolic/instance.py +437 -0
- sky/provision/hyperbolic/utils.py +373 -0
- sky/provision/instance_setup.py +101 -20
- sky/provision/kubernetes/__init__.py +5 -0
- sky/provision/kubernetes/config.py +9 -52
- sky/provision/kubernetes/constants.py +17 -0
- sky/provision/kubernetes/instance.py +919 -280
- sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml +1 -2
- sky/provision/kubernetes/network.py +27 -17
- sky/provision/kubernetes/network_utils.py +44 -43
- sky/provision/kubernetes/utils.py +1221 -534
- sky/provision/kubernetes/volume.py +343 -0
- sky/provision/lambda_cloud/instance.py +22 -16
- sky/provision/nebius/constants.py +50 -0
- sky/provision/nebius/instance.py +19 -6
- sky/provision/nebius/utils.py +237 -137
- sky/provision/oci/instance.py +10 -5
- sky/provision/paperspace/instance.py +10 -7
- sky/provision/paperspace/utils.py +1 -1
- sky/provision/primeintellect/__init__.py +10 -0
- sky/provision/primeintellect/config.py +11 -0
- sky/provision/primeintellect/instance.py +454 -0
- sky/provision/primeintellect/utils.py +398 -0
- sky/provision/provisioner.py +117 -36
- sky/provision/runpod/__init__.py +5 -0
- sky/provision/runpod/instance.py +27 -6
- sky/provision/runpod/utils.py +51 -18
- sky/provision/runpod/volume.py +214 -0
- sky/provision/scp/__init__.py +15 -0
- sky/provision/scp/config.py +93 -0
- sky/provision/scp/instance.py +707 -0
- sky/provision/seeweb/__init__.py +11 -0
- sky/provision/seeweb/config.py +13 -0
- sky/provision/seeweb/instance.py +812 -0
- sky/provision/shadeform/__init__.py +11 -0
- sky/provision/shadeform/config.py +12 -0
- sky/provision/shadeform/instance.py +351 -0
- sky/provision/shadeform/shadeform_utils.py +83 -0
- sky/provision/ssh/__init__.py +18 -0
- sky/provision/vast/instance.py +13 -8
- sky/provision/vast/utils.py +10 -7
- sky/provision/volume.py +164 -0
- sky/provision/vsphere/common/ssl_helper.py +1 -1
- sky/provision/vsphere/common/vapiconnect.py +2 -1
- sky/provision/vsphere/common/vim_utils.py +4 -4
- sky/provision/vsphere/instance.py +15 -10
- sky/provision/vsphere/vsphere_utils.py +17 -20
- sky/py.typed +0 -0
- sky/resources.py +845 -119
- sky/schemas/__init__.py +0 -0
- sky/schemas/api/__init__.py +0 -0
- sky/schemas/api/responses.py +227 -0
- sky/schemas/db/README +4 -0
- sky/schemas/db/env.py +90 -0
- sky/schemas/db/global_user_state/001_initial_schema.py +124 -0
- sky/schemas/db/global_user_state/002_add_workspace_to_cluster_history.py +35 -0
- sky/schemas/db/global_user_state/003_fix_initial_revision.py +61 -0
- sky/schemas/db/global_user_state/004_is_managed.py +34 -0
- sky/schemas/db/global_user_state/005_cluster_event.py +32 -0
- sky/schemas/db/global_user_state/006_provision_log.py +41 -0
- sky/schemas/db/global_user_state/007_cluster_event_request_id.py +34 -0
- sky/schemas/db/global_user_state/008_skylet_ssh_tunnel_metadata.py +34 -0
- sky/schemas/db/global_user_state/009_last_activity_and_launched_at.py +89 -0
- sky/schemas/db/global_user_state/010_save_ssh_key.py +66 -0
- sky/schemas/db/global_user_state/011_is_ephemeral.py +34 -0
- sky/schemas/db/kv_cache/001_initial_schema.py +29 -0
- sky/schemas/db/script.py.mako +28 -0
- sky/schemas/db/serve_state/001_initial_schema.py +67 -0
- sky/schemas/db/serve_state/002_yaml_content.py +34 -0
- sky/schemas/db/skypilot_config/001_initial_schema.py +30 -0
- sky/schemas/db/spot_jobs/001_initial_schema.py +97 -0
- sky/schemas/db/spot_jobs/002_cluster_pool.py +42 -0
- sky/schemas/db/spot_jobs/003_pool_hash.py +34 -0
- sky/schemas/db/spot_jobs/004_job_file_contents.py +42 -0
- sky/schemas/db/spot_jobs/005_logs_gc.py +38 -0
- sky/schemas/db/spot_jobs/006_controller_pid_started_at.py +34 -0
- sky/schemas/db/spot_jobs/007_config_file_content.py +34 -0
- sky/schemas/generated/__init__.py +0 -0
- sky/schemas/generated/autostopv1_pb2.py +36 -0
- sky/schemas/generated/autostopv1_pb2.pyi +43 -0
- sky/schemas/generated/autostopv1_pb2_grpc.py +146 -0
- sky/schemas/generated/jobsv1_pb2.py +86 -0
- sky/schemas/generated/jobsv1_pb2.pyi +254 -0
- sky/schemas/generated/jobsv1_pb2_grpc.py +542 -0
- sky/schemas/generated/managed_jobsv1_pb2.py +76 -0
- sky/schemas/generated/managed_jobsv1_pb2.pyi +278 -0
- sky/schemas/generated/managed_jobsv1_pb2_grpc.py +278 -0
- sky/schemas/generated/servev1_pb2.py +58 -0
- sky/schemas/generated/servev1_pb2.pyi +115 -0
- sky/schemas/generated/servev1_pb2_grpc.py +322 -0
- sky/serve/autoscalers.py +357 -5
- sky/serve/client/impl.py +310 -0
- sky/serve/client/sdk.py +47 -139
- sky/serve/client/sdk_async.py +130 -0
- sky/serve/constants.py +12 -9
- sky/serve/controller.py +68 -17
- sky/serve/load_balancer.py +106 -60
- sky/serve/load_balancing_policies.py +116 -2
- sky/serve/replica_managers.py +434 -249
- sky/serve/serve_rpc_utils.py +179 -0
- sky/serve/serve_state.py +569 -257
- sky/serve/serve_utils.py +775 -265
- sky/serve/server/core.py +66 -711
- sky/serve/server/impl.py +1093 -0
- sky/serve/server/server.py +21 -18
- sky/serve/service.py +192 -89
- sky/serve/service_spec.py +144 -20
- sky/serve/spot_placer.py +3 -0
- sky/server/auth/__init__.py +0 -0
- sky/server/auth/authn.py +50 -0
- sky/server/auth/loopback.py +38 -0
- sky/server/auth/oauth2_proxy.py +202 -0
- sky/server/common.py +478 -182
- sky/server/config.py +85 -23
- sky/server/constants.py +44 -6
- sky/server/daemons.py +295 -0
- sky/server/html/token_page.html +185 -0
- sky/server/metrics.py +160 -0
- sky/server/middleware_utils.py +166 -0
- sky/server/requests/executor.py +558 -138
- sky/server/requests/payloads.py +364 -24
- sky/server/requests/preconditions.py +21 -17
- sky/server/requests/process.py +112 -29
- sky/server/requests/request_names.py +121 -0
- sky/server/requests/requests.py +822 -226
- sky/server/requests/serializers/decoders.py +82 -31
- sky/server/requests/serializers/encoders.py +140 -22
- sky/server/requests/threads.py +117 -0
- sky/server/rest.py +455 -0
- sky/server/server.py +1309 -285
- sky/server/state.py +20 -0
- sky/server/stream_utils.py +327 -61
- sky/server/uvicorn.py +217 -3
- sky/server/versions.py +270 -0
- sky/setup_files/MANIFEST.in +11 -1
- sky/setup_files/alembic.ini +160 -0
- sky/setup_files/dependencies.py +139 -31
- sky/setup_files/setup.py +44 -42
- sky/sky_logging.py +114 -7
- sky/skylet/attempt_skylet.py +106 -24
- sky/skylet/autostop_lib.py +129 -8
- sky/skylet/configs.py +29 -20
- sky/skylet/constants.py +216 -25
- sky/skylet/events.py +101 -21
- sky/skylet/job_lib.py +345 -164
- sky/skylet/log_lib.py +297 -18
- sky/skylet/log_lib.pyi +44 -1
- sky/skylet/providers/ibm/node_provider.py +12 -8
- sky/skylet/providers/ibm/vpc_provider.py +13 -12
- sky/skylet/ray_patches/__init__.py +17 -3
- sky/skylet/ray_patches/autoscaler.py.diff +18 -0
- sky/skylet/ray_patches/cli.py.diff +19 -0
- sky/skylet/ray_patches/command_runner.py.diff +17 -0
- sky/skylet/ray_patches/log_monitor.py.diff +20 -0
- sky/skylet/ray_patches/resource_demand_scheduler.py.diff +32 -0
- sky/skylet/ray_patches/updater.py.diff +18 -0
- sky/skylet/ray_patches/worker.py.diff +41 -0
- sky/skylet/runtime_utils.py +21 -0
- sky/skylet/services.py +568 -0
- sky/skylet/skylet.py +72 -4
- sky/skylet/subprocess_daemon.py +104 -29
- sky/skypilot_config.py +506 -99
- sky/ssh_node_pools/__init__.py +1 -0
- sky/ssh_node_pools/core.py +135 -0
- sky/ssh_node_pools/server.py +233 -0
- sky/task.py +685 -163
- sky/templates/aws-ray.yml.j2 +11 -3
- sky/templates/azure-ray.yml.j2 +2 -1
- sky/templates/cudo-ray.yml.j2 +1 -0
- sky/templates/do-ray.yml.j2 +2 -1
- sky/templates/fluidstack-ray.yml.j2 +1 -0
- sky/templates/gcp-ray.yml.j2 +62 -1
- sky/templates/hyperbolic-ray.yml.j2 +68 -0
- sky/templates/ibm-ray.yml.j2 +2 -1
- sky/templates/jobs-controller.yaml.j2 +27 -24
- sky/templates/kubernetes-loadbalancer.yml.j2 +2 -0
- sky/templates/kubernetes-ray.yml.j2 +611 -50
- sky/templates/lambda-ray.yml.j2 +2 -1
- sky/templates/nebius-ray.yml.j2 +34 -12
- sky/templates/oci-ray.yml.j2 +1 -0
- sky/templates/paperspace-ray.yml.j2 +2 -1
- sky/templates/primeintellect-ray.yml.j2 +72 -0
- sky/templates/runpod-ray.yml.j2 +10 -1
- sky/templates/scp-ray.yml.j2 +4 -50
- sky/templates/seeweb-ray.yml.j2 +171 -0
- sky/templates/shadeform-ray.yml.j2 +73 -0
- sky/templates/sky-serve-controller.yaml.j2 +22 -2
- sky/templates/vast-ray.yml.j2 +1 -0
- sky/templates/vsphere-ray.yml.j2 +1 -0
- sky/templates/websocket_proxy.py +212 -37
- sky/usage/usage_lib.py +31 -15
- sky/users/__init__.py +0 -0
- sky/users/model.conf +15 -0
- sky/users/permission.py +397 -0
- sky/users/rbac.py +121 -0
- sky/users/server.py +720 -0
- sky/users/token_service.py +218 -0
- sky/utils/accelerator_registry.py +35 -5
- sky/utils/admin_policy_utils.py +84 -38
- sky/utils/annotations.py +38 -5
- sky/utils/asyncio_utils.py +78 -0
- sky/utils/atomic.py +1 -1
- sky/utils/auth_utils.py +153 -0
- sky/utils/benchmark_utils.py +60 -0
- sky/utils/cli_utils/status_utils.py +159 -86
- sky/utils/cluster_utils.py +31 -9
- sky/utils/command_runner.py +354 -68
- sky/utils/command_runner.pyi +93 -3
- sky/utils/common.py +35 -8
- sky/utils/common_utils.py +314 -91
- sky/utils/config_utils.py +74 -5
- sky/utils/context.py +403 -0
- sky/utils/context_utils.py +242 -0
- sky/utils/controller_utils.py +383 -89
- sky/utils/dag_utils.py +31 -12
- sky/utils/db/__init__.py +0 -0
- sky/utils/db/db_utils.py +485 -0
- sky/utils/db/kv_cache.py +149 -0
- sky/utils/db/migration_utils.py +137 -0
- sky/utils/directory_utils.py +12 -0
- sky/utils/env_options.py +13 -0
- sky/utils/git.py +567 -0
- sky/utils/git_clone.sh +460 -0
- sky/utils/infra_utils.py +195 -0
- sky/utils/kubernetes/cleanup-tunnel.sh +62 -0
- sky/utils/kubernetes/config_map_utils.py +133 -0
- sky/utils/kubernetes/create_cluster.sh +15 -29
- sky/utils/kubernetes/delete_cluster.sh +10 -7
- sky/utils/kubernetes/deploy_ssh_node_pools.py +1177 -0
- sky/utils/kubernetes/exec_kubeconfig_converter.py +22 -31
- sky/utils/kubernetes/generate_kind_config.py +6 -66
- sky/utils/kubernetes/generate_kubeconfig.sh +4 -1
- sky/utils/kubernetes/gpu_labeler.py +18 -8
- sky/utils/kubernetes/k8s_gpu_labeler_job.yaml +2 -1
- sky/utils/kubernetes/k8s_gpu_labeler_setup.yaml +16 -16
- sky/utils/kubernetes/kubernetes_deploy_utils.py +284 -114
- sky/utils/kubernetes/rsync_helper.sh +11 -3
- sky/utils/kubernetes/ssh-tunnel.sh +379 -0
- sky/utils/kubernetes/ssh_utils.py +221 -0
- sky/utils/kubernetes_enums.py +8 -15
- sky/utils/lock_events.py +94 -0
- sky/utils/locks.py +416 -0
- sky/utils/log_utils.py +82 -107
- sky/utils/perf_utils.py +22 -0
- sky/utils/resource_checker.py +298 -0
- sky/utils/resources_utils.py +249 -32
- sky/utils/rich_utils.py +217 -39
- sky/utils/schemas.py +955 -160
- sky/utils/serialize_utils.py +16 -0
- sky/utils/status_lib.py +10 -0
- sky/utils/subprocess_utils.py +29 -15
- sky/utils/tempstore.py +70 -0
- sky/utils/thread_utils.py +91 -0
- sky/utils/timeline.py +26 -53
- sky/utils/ux_utils.py +84 -15
- sky/utils/validator.py +11 -1
- sky/utils/volume.py +165 -0
- sky/utils/yaml_utils.py +111 -0
- sky/volumes/__init__.py +13 -0
- sky/volumes/client/__init__.py +0 -0
- sky/volumes/client/sdk.py +150 -0
- sky/volumes/server/__init__.py +0 -0
- sky/volumes/server/core.py +270 -0
- sky/volumes/server/server.py +124 -0
- sky/volumes/volume.py +215 -0
- sky/workspaces/__init__.py +0 -0
- sky/workspaces/core.py +655 -0
- sky/workspaces/server.py +101 -0
- sky/workspaces/utils.py +56 -0
- sky_templates/README.md +3 -0
- sky_templates/__init__.py +3 -0
- sky_templates/ray/__init__.py +0 -0
- sky_templates/ray/start_cluster +183 -0
- sky_templates/ray/stop_cluster +75 -0
- skypilot_nightly-1.0.0.dev20251203.dist-info/METADATA +676 -0
- skypilot_nightly-1.0.0.dev20251203.dist-info/RECORD +611 -0
- {skypilot_nightly-1.0.0.dev20250502.dist-info → skypilot_nightly-1.0.0.dev20251203.dist-info}/WHEEL +1 -1
- skypilot_nightly-1.0.0.dev20251203.dist-info/top_level.txt +2 -0
- sky/benchmark/benchmark_state.py +0 -256
- sky/benchmark/benchmark_utils.py +0 -641
- sky/clouds/service_catalog/constants.py +0 -7
- sky/dashboard/out/_next/static/GWvVBSCS7FmUiVmjaL1a7/_buildManifest.js +0 -1
- sky/dashboard/out/_next/static/chunks/236-2db3ee3fba33dd9e.js +0 -6
- sky/dashboard/out/_next/static/chunks/312-c3c8845990db8ffc.js +0 -15
- sky/dashboard/out/_next/static/chunks/37-0a572fe0dbb89c4d.js +0 -6
- sky/dashboard/out/_next/static/chunks/678-206dddca808e6d16.js +0 -59
- sky/dashboard/out/_next/static/chunks/845-9e60713e0c441abc.js +0 -1
- sky/dashboard/out/_next/static/chunks/979-7bf73a4c7cea0f5c.js +0 -1
- sky/dashboard/out/_next/static/chunks/fd9d1056-2821b0f0cabcd8bd.js +0 -1
- sky/dashboard/out/_next/static/chunks/framework-87d061ee6ed71b28.js +0 -33
- sky/dashboard/out/_next/static/chunks/main-app-241eb28595532291.js +0 -1
- sky/dashboard/out/_next/static/chunks/main-e0e2335212e72357.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/_app-e6b013bc3f77ad60.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]-6ac338bc2239cb45.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-f383db7389368ea7.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/clusters-a93b93e10b8b074e.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/index-f9f039532ca8cbc4.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-1c519e1afc523dc9.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs-a75029b67aab6a2e.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-830f59b8404e96b8.js +0 -1
- sky/dashboard/out/_next/static/css/c6933bbb2ce7f4dd.css +0 -3
- sky/jobs/dashboard/dashboard.py +0 -223
- sky/jobs/dashboard/static/favicon.ico +0 -0
- sky/jobs/dashboard/templates/index.html +0 -831
- sky/jobs/server/dashboard_utils.py +0 -69
- sky/skylet/providers/scp/__init__.py +0 -2
- sky/skylet/providers/scp/config.py +0 -149
- sky/skylet/providers/scp/node_provider.py +0 -578
- sky/templates/kubernetes-ssh-jump.yml.j2 +0 -94
- sky/utils/db_utils.py +0 -100
- sky/utils/kubernetes/deploy_remote_cluster.sh +0 -308
- sky/utils/kubernetes/ssh_jump_lifecycle_manager.py +0 -191
- skypilot_nightly-1.0.0.dev20250502.dist-info/METADATA +0 -361
- skypilot_nightly-1.0.0.dev20250502.dist-info/RECORD +0 -396
- skypilot_nightly-1.0.0.dev20250502.dist-info/top_level.txt +0 -1
- /sky/{clouds/service_catalog → catalog}/config.py +0 -0
- /sky/{benchmark → catalog/data_fetchers}/__init__.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_azure.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_fluidstack.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_ibm.py +0 -0
- /sky/{clouds/service_catalog/data_fetchers → client/cli}/__init__.py +0 -0
- /sky/dashboard/out/_next/static/{GWvVBSCS7FmUiVmjaL1a7 → 96_E2yl3QAiIJGOYCkSpB}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250502.dist-info → skypilot_nightly-1.0.0.dev20251203.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250502.dist-info → skypilot_nightly-1.0.0.dev20251203.dist-info}/licenses/LICENSE +0 -0
sky/check.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Credential checks: check cloud credentials and enable clouds."""
|
|
2
|
+
import collections
|
|
2
3
|
import os
|
|
3
4
|
import traceback
|
|
4
5
|
from types import ModuleType
|
|
5
|
-
from typing import
|
|
6
|
-
Union)
|
|
6
|
+
from typing import Callable, Dict, Iterable, List, Optional, Set, Tuple, Union
|
|
7
7
|
|
|
8
8
|
import click
|
|
9
9
|
import colorama
|
|
@@ -11,174 +11,351 @@ import colorama
|
|
|
11
11
|
from sky import clouds as sky_clouds
|
|
12
12
|
from sky import exceptions
|
|
13
13
|
from sky import global_user_state
|
|
14
|
+
from sky import sky_logging
|
|
14
15
|
from sky import skypilot_config
|
|
15
16
|
from sky.adaptors import cloudflare
|
|
17
|
+
from sky.adaptors import coreweave
|
|
16
18
|
from sky.clouds import cloud as sky_cloud
|
|
19
|
+
from sky.skylet import constants
|
|
20
|
+
from sky.utils import common_utils
|
|
17
21
|
from sky.utils import registry
|
|
18
22
|
from sky.utils import rich_utils
|
|
23
|
+
from sky.utils import subprocess_utils
|
|
19
24
|
from sky.utils import ux_utils
|
|
20
25
|
|
|
21
26
|
CHECK_MARK_EMOJI = '\U00002714' # Heavy check mark unicode
|
|
22
27
|
PARTY_POPPER_EMOJI = '\U0001F389' # Party popper unicode
|
|
23
28
|
|
|
29
|
+
logger = sky_logging.init_logger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_workspace_allowed_clouds(workspace: str) -> List[str]:
|
|
33
|
+
# Use allowed_clouds from config if it exists, otherwise check all
|
|
34
|
+
# clouds. Also validate names with get_cloud_tuple.
|
|
35
|
+
config_allowed_cloud_names = skypilot_config.get_nested(
|
|
36
|
+
('allowed_clouds',),
|
|
37
|
+
[repr(c) for c in registry.CLOUD_REGISTRY.values()] +
|
|
38
|
+
[cloudflare.NAME, coreweave.NAME])
|
|
39
|
+
# filter out the clouds that are disabled in the workspace config
|
|
40
|
+
workspace_disabled_clouds = []
|
|
41
|
+
for cloud in config_allowed_cloud_names:
|
|
42
|
+
cloud_config = skypilot_config.get_workspace_cloud(cloud.lower(),
|
|
43
|
+
workspace=workspace)
|
|
44
|
+
cloud_disabled = cloud_config.get('disabled', False)
|
|
45
|
+
if cloud_disabled:
|
|
46
|
+
workspace_disabled_clouds.append(cloud.lower())
|
|
47
|
+
|
|
48
|
+
config_allowed_cloud_names = [
|
|
49
|
+
c for c in config_allowed_cloud_names
|
|
50
|
+
if c.lower() not in workspace_disabled_clouds
|
|
51
|
+
]
|
|
52
|
+
return config_allowed_cloud_names
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _get_workspace_cloud_capabilities(
|
|
56
|
+
workspace: str,
|
|
57
|
+
cloud: str) -> Optional[List[sky_cloud.CloudCapability]]:
|
|
58
|
+
"""Get the capabilities for a cloud in a workspace.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
A list of capabilities for the cloud in the workspace.
|
|
62
|
+
None if the capabilities are not explicitly specified
|
|
63
|
+
in the workspace or global config.
|
|
64
|
+
Returned value of None does not mean the cloud is disabled.
|
|
65
|
+
"""
|
|
66
|
+
cloud_config = skypilot_config.get_workspace_cloud(cloud,
|
|
67
|
+
workspace=workspace)
|
|
68
|
+
cloud_capabilities = cloud_config.get('capabilities', None)
|
|
69
|
+
if not cloud_capabilities:
|
|
70
|
+
# get the capabilities from the global config
|
|
71
|
+
cloud_capabilities = skypilot_config.get_nested(
|
|
72
|
+
(cloud.lower(), 'capabilities'), default_value=None)
|
|
73
|
+
if cloud_capabilities:
|
|
74
|
+
return [
|
|
75
|
+
sky_cloud.CloudCapability(capability.lower())
|
|
76
|
+
for capability in cloud_capabilities
|
|
77
|
+
]
|
|
78
|
+
return None
|
|
79
|
+
|
|
24
80
|
|
|
25
81
|
def check_capabilities(
|
|
26
82
|
quiet: bool = False,
|
|
27
83
|
verbose: bool = False,
|
|
28
84
|
clouds: Optional[Iterable[str]] = None,
|
|
29
85
|
capabilities: Optional[List[sky_cloud.CloudCapability]] = None,
|
|
30
|
-
|
|
86
|
+
workspace: Optional[str] = None,
|
|
87
|
+
) -> Dict[str, Dict[str, List[sky_cloud.CloudCapability]]]:
|
|
88
|
+
# pylint: disable=import-outside-toplevel
|
|
89
|
+
from sky.workspaces import core
|
|
90
|
+
|
|
31
91
|
echo = (lambda *_args, **_kwargs: None
|
|
32
92
|
) if quiet else lambda *args, **kwargs: click.echo(
|
|
33
93
|
*args, **kwargs, color=True)
|
|
34
|
-
|
|
94
|
+
all_workspaces_results: Dict[str,
|
|
95
|
+
Dict[str,
|
|
96
|
+
List[sky_cloud.CloudCapability]]] = {}
|
|
97
|
+
available_workspaces = list(core.get_workspaces().keys())
|
|
98
|
+
hide_workspace_str = (available_workspaces == [
|
|
99
|
+
constants.SKYPILOT_DEFAULT_WORKSPACE
|
|
100
|
+
])
|
|
101
|
+
initial_hint = 'Checking credentials to enable infra for SkyPilot.'
|
|
102
|
+
if len(available_workspaces) > 1:
|
|
103
|
+
initial_hint = (f'Checking credentials to enable infra for SkyPilot '
|
|
104
|
+
f'(Workspaces: {", ".join(available_workspaces)}).')
|
|
105
|
+
echo(initial_hint)
|
|
35
106
|
if capabilities is None:
|
|
36
107
|
capabilities = sky_cloud.ALL_CAPABILITIES
|
|
37
108
|
assert capabilities is not None
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
109
|
+
|
|
110
|
+
def get_all_clouds() -> Tuple[str, ...]:
|
|
111
|
+
return tuple([repr(c) for c in registry.CLOUD_REGISTRY.values()] +
|
|
112
|
+
[cloudflare.NAME, coreweave.NAME])
|
|
113
|
+
|
|
114
|
+
def _execute_check_logic_for_workspace(
|
|
115
|
+
current_workspace_name: str,
|
|
116
|
+
hide_per_cloud_details: bool,
|
|
117
|
+
hide_workspace_str: bool,
|
|
118
|
+
) -> Dict[str, List[sky_cloud.CloudCapability]]:
|
|
119
|
+
nonlocal echo, verbose, clouds, quiet
|
|
120
|
+
|
|
121
|
+
enabled_clouds: Dict[str, List[sky_cloud.CloudCapability]] = {}
|
|
122
|
+
disabled_clouds: Dict[str, List[sky_cloud.CloudCapability]] = {}
|
|
123
|
+
|
|
124
|
+
def check_one_cloud_one_capability(
|
|
125
|
+
payload: Tuple[Tuple[str, Union[sky_clouds.Cloud, ModuleType]],
|
|
126
|
+
sky_cloud.CloudCapability, bool]
|
|
127
|
+
) -> Optional[Tuple[sky_cloud.CloudCapability, bool, Optional[Union[
|
|
128
|
+
str, Dict[str, str]]]]]:
|
|
129
|
+
cloud_tuple, capability, allowed = payload
|
|
130
|
+
if not allowed:
|
|
131
|
+
return (capability, False, f'{cloud_tuple[0]} is not included '
|
|
132
|
+
'in allowed_clouds in ~/.sky/config.yaml')
|
|
133
|
+
with skypilot_config.local_active_workspace_ctx(
|
|
134
|
+
current_workspace_name):
|
|
135
|
+
# Have to override again for specific thread, as the
|
|
136
|
+
# local_active_workspace_ctx is thread-local.
|
|
137
|
+
_, cloud = cloud_tuple
|
|
52
138
|
try:
|
|
53
139
|
ok, reason = cloud.check_credentials(capability)
|
|
54
140
|
except exceptions.NotSupportedError:
|
|
55
|
-
|
|
141
|
+
return None
|
|
56
142
|
except Exception: # pylint: disable=broad-except
|
|
57
|
-
# Catch all exceptions to prevent a single cloud
|
|
58
|
-
# from blocking the check for other clouds.
|
|
59
143
|
ok, reason = False, traceback.format_exc()
|
|
60
|
-
|
|
61
|
-
|
|
144
|
+
if not isinstance(reason, dict):
|
|
145
|
+
reason = reason.strip() if reason else None
|
|
146
|
+
return (capability, ok, reason)
|
|
147
|
+
|
|
148
|
+
def get_cloud_tuple(
|
|
149
|
+
cloud_name: str
|
|
150
|
+
) -> Tuple[str, Union[sky_clouds.Cloud, ModuleType]]:
|
|
151
|
+
# Validates cloud_name and returns a tuple of the cloud's name and
|
|
152
|
+
# the cloud object. Includes special handling for Cloudflare and
|
|
153
|
+
# CoreWeave.
|
|
154
|
+
if cloud_name.lower().startswith('cloudflare'):
|
|
155
|
+
return cloudflare.NAME, cloudflare
|
|
156
|
+
elif cloud_name.lower().startswith('coreweave'):
|
|
157
|
+
return coreweave.NAME, coreweave
|
|
158
|
+
else:
|
|
159
|
+
cloud_obj = registry.CLOUD_REGISTRY.from_str(cloud_name)
|
|
160
|
+
assert cloud_obj is not None, f'Cloud {cloud_name!r} not found'
|
|
161
|
+
return repr(cloud_obj), cloud_obj
|
|
162
|
+
|
|
163
|
+
if clouds is not None:
|
|
164
|
+
cloud_list = clouds
|
|
165
|
+
check_explicit = True
|
|
166
|
+
else:
|
|
167
|
+
cloud_list = get_all_clouds()
|
|
168
|
+
check_explicit = False
|
|
169
|
+
|
|
170
|
+
clouds_to_check = [get_cloud_tuple(c) for c in cloud_list]
|
|
171
|
+
|
|
172
|
+
# Use allowed_clouds from config if it exists, otherwise check all
|
|
173
|
+
# clouds. Also validate names with get_cloud_tuple.
|
|
174
|
+
config_allowed_cloud_names = sorted([
|
|
175
|
+
get_cloud_tuple(c)[0] for c in skypilot_config.get_nested((
|
|
176
|
+
'allowed_clouds',), get_all_clouds())
|
|
177
|
+
])
|
|
178
|
+
|
|
179
|
+
# filter out the clouds that are disabled in the workspace config
|
|
180
|
+
workspace_disabled_clouds = []
|
|
181
|
+
workspace_cloud_capabilities: Dict[
|
|
182
|
+
str, List[sky_cloud.CloudCapability]] = {}
|
|
183
|
+
for cloud in config_allowed_cloud_names:
|
|
184
|
+
cloud_config = skypilot_config.get_workspace_cloud(
|
|
185
|
+
cloud, workspace=current_workspace_name)
|
|
186
|
+
cloud_disabled = cloud_config.get('disabled', False)
|
|
187
|
+
if cloud_disabled:
|
|
188
|
+
workspace_disabled_clouds.append(cloud)
|
|
189
|
+
else:
|
|
190
|
+
specified_capabilities = _get_workspace_cloud_capabilities(
|
|
191
|
+
current_workspace_name, cloud)
|
|
192
|
+
if specified_capabilities:
|
|
193
|
+
# filter the capabilities to only the ones passed
|
|
194
|
+
# in as argument to this function
|
|
195
|
+
workspace_cloud_capabilities[cloud] = [
|
|
196
|
+
enabled_capability
|
|
197
|
+
for enabled_capability in specified_capabilities
|
|
198
|
+
if enabled_capability in capabilities
|
|
199
|
+
]
|
|
200
|
+
# mark capabilities that are not enabled
|
|
201
|
+
# in the workspace config as disabled
|
|
202
|
+
for capability in capabilities:
|
|
203
|
+
if capability not in workspace_cloud_capabilities[
|
|
204
|
+
cloud]:
|
|
205
|
+
disabled_clouds.setdefault(cloud,
|
|
206
|
+
[]).append(capability)
|
|
207
|
+
|
|
208
|
+
config_allowed_cloud_names = [
|
|
209
|
+
c for c in config_allowed_cloud_names
|
|
210
|
+
if c not in workspace_disabled_clouds
|
|
211
|
+
]
|
|
212
|
+
global_user_state.set_allowed_clouds(
|
|
213
|
+
[c for c in config_allowed_cloud_names], current_workspace_name)
|
|
214
|
+
|
|
215
|
+
# Use disallowed_cloud_names for logging the clouds that will be
|
|
216
|
+
# disabled because they are not included in allowed_clouds in
|
|
217
|
+
# config.yaml.
|
|
218
|
+
disallowed_cloud_names = [
|
|
219
|
+
c for c in get_all_clouds() if c not in config_allowed_cloud_names
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
combinations = []
|
|
223
|
+
for c in clouds_to_check:
|
|
224
|
+
allowed = c[0] in config_allowed_cloud_names
|
|
225
|
+
if allowed or check_explicit:
|
|
226
|
+
for capability in workspace_cloud_capabilities.get(
|
|
227
|
+
c[0], capabilities):
|
|
228
|
+
combinations.append((c, capability, allowed))
|
|
229
|
+
|
|
230
|
+
cloud2ctx2text: Dict[str, Dict[str, str]] = {}
|
|
231
|
+
|
|
232
|
+
workspace_str = f' for workspace: {current_workspace_name!r}'
|
|
233
|
+
if hide_workspace_str:
|
|
234
|
+
workspace_str = ''
|
|
235
|
+
with rich_utils.safe_status(
|
|
236
|
+
ux_utils.spinner_message(
|
|
237
|
+
f'Checking infra choices{workspace_str}...')):
|
|
238
|
+
check_results = subprocess_utils.run_in_parallel(
|
|
239
|
+
check_one_cloud_one_capability, combinations)
|
|
240
|
+
|
|
241
|
+
check_results_dict: Dict[
|
|
242
|
+
Tuple[str, Union[sky_clouds.Cloud, ModuleType]],
|
|
243
|
+
List[Tuple[sky_cloud.CloudCapability, bool,
|
|
244
|
+
Optional[Union[str, Dict[str, str]]]]]] = (
|
|
245
|
+
collections.defaultdict(list))
|
|
246
|
+
for combination, check_result in zip(combinations, check_results):
|
|
247
|
+
if check_result is None:
|
|
248
|
+
continue
|
|
249
|
+
capability, ok, ctx2text = check_result
|
|
250
|
+
cloud_tuple, _, _ = combination
|
|
251
|
+
cloud_repr = cloud_tuple[0]
|
|
252
|
+
if isinstance(ctx2text, dict):
|
|
253
|
+
cloud2ctx2text[cloud_repr] = ctx2text
|
|
62
254
|
if ok:
|
|
63
255
|
enabled_clouds.setdefault(cloud_repr, []).append(capability)
|
|
64
256
|
else:
|
|
65
257
|
disabled_clouds.setdefault(cloud_repr, []).append(capability)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
258
|
+
check_results_dict[cloud_tuple].append(check_result)
|
|
259
|
+
|
|
260
|
+
if not hide_per_cloud_details:
|
|
261
|
+
for cloud_tuple, check_result_list in sorted(
|
|
262
|
+
check_results_dict.items(), key=lambda item: item[0][0]):
|
|
263
|
+
_print_checked_cloud(echo, verbose, cloud_tuple,
|
|
264
|
+
check_result_list,
|
|
265
|
+
cloud2ctx2text.get(cloud_tuple[0], {}))
|
|
266
|
+
|
|
267
|
+
# Determine the set of enabled clouds: (previously enabled clouds +
|
|
268
|
+
# newly enabled clouds - newly disabled clouds) intersected with
|
|
269
|
+
# config_allowed_clouds, if specified in config.yaml.
|
|
270
|
+
# This means that if a cloud is already enabled and is not included in
|
|
271
|
+
# allowed_clouds in config.yaml, it will be disabled.
|
|
272
|
+
all_enabled_clouds: Set[str] = set()
|
|
273
|
+
for capability in capabilities:
|
|
274
|
+
# Cloudflare and CoreWeave are not real clouds in
|
|
275
|
+
# registry.CLOUD_REGISTRY, and should not be inserted into the DB
|
|
276
|
+
# (otherwise `sky launch` and other code would error out when it's
|
|
277
|
+
# trying to look it up in the registry).
|
|
278
|
+
enabled_clouds_set = {
|
|
279
|
+
cloud for cloud, capabilities in enabled_clouds.items()
|
|
280
|
+
if capability in capabilities and not cloud.startswith(
|
|
281
|
+
'Cloudflare') and not cloud.startswith('CoreWeave')
|
|
282
|
+
}
|
|
283
|
+
disabled_clouds_set = {
|
|
284
|
+
cloud for cloud, capabilities in disabled_clouds.items()
|
|
285
|
+
if capability in capabilities and not cloud.startswith(
|
|
286
|
+
'Cloudflare') and not cloud.startswith('CoreWeave')
|
|
287
|
+
}
|
|
288
|
+
config_allowed_clouds_set = {
|
|
289
|
+
cloud for cloud in config_allowed_cloud_names
|
|
290
|
+
if not cloud.startswith('Cloudflare') and
|
|
291
|
+
not cloud.startswith('CoreWeave')
|
|
292
|
+
}
|
|
293
|
+
previously_enabled_clouds_set = {
|
|
294
|
+
repr(cloud)
|
|
295
|
+
for cloud in global_user_state.get_cached_enabled_clouds(
|
|
296
|
+
capability, current_workspace_name)
|
|
297
|
+
}
|
|
298
|
+
enabled_clouds_for_capability = (config_allowed_clouds_set & (
|
|
299
|
+
(previously_enabled_clouds_set | enabled_clouds_set) -
|
|
300
|
+
disabled_clouds_set))
|
|
301
|
+
|
|
302
|
+
global_user_state.set_enabled_clouds(
|
|
303
|
+
list(enabled_clouds_for_capability), capability,
|
|
304
|
+
current_workspace_name)
|
|
305
|
+
all_enabled_clouds = all_enabled_clouds.union(
|
|
306
|
+
enabled_clouds_for_capability)
|
|
104
307
|
|
|
105
|
-
for cloud_tuple in sorted(clouds_to_check):
|
|
106
|
-
check_one_cloud(cloud_tuple)
|
|
107
|
-
|
|
108
|
-
# Determine the set of enabled clouds: (previously enabled clouds + newly
|
|
109
|
-
# enabled clouds - newly disabled clouds) intersected with
|
|
110
|
-
# config_allowed_clouds, if specified in config.yaml.
|
|
111
|
-
# This means that if a cloud is already enabled and is not included in
|
|
112
|
-
# allowed_clouds in config.yaml, it will be disabled.
|
|
113
|
-
all_enabled_clouds: Set[str] = set()
|
|
114
|
-
for capability in capabilities:
|
|
115
|
-
# Cloudflare is not a real cloud in registry.CLOUD_REGISTRY, and should
|
|
116
|
-
# not be inserted into the DB (otherwise `sky launch` and other code
|
|
117
|
-
# would error out when it's trying to look it up in the registry).
|
|
118
|
-
enabled_clouds_set = {
|
|
119
|
-
cloud for cloud, capabilities in enabled_clouds.items()
|
|
120
|
-
if capability in capabilities and not cloud.startswith('Cloudflare')
|
|
121
|
-
}
|
|
122
|
-
disabled_clouds_set = {
|
|
123
|
-
cloud for cloud, capabilities in disabled_clouds.items()
|
|
124
|
-
if capability in capabilities and not cloud.startswith('Cloudflare')
|
|
125
|
-
}
|
|
126
|
-
config_allowed_clouds_set = {
|
|
127
|
-
cloud for cloud in config_allowed_cloud_names
|
|
128
|
-
if not cloud.startswith('Cloudflare')
|
|
129
|
-
}
|
|
130
|
-
previously_enabled_clouds_set = {
|
|
131
|
-
repr(cloud)
|
|
132
|
-
for cloud in global_user_state.get_cached_enabled_clouds(capability)
|
|
133
|
-
}
|
|
134
|
-
enabled_clouds_for_capability = (config_allowed_clouds_set & (
|
|
135
|
-
(previously_enabled_clouds_set | enabled_clouds_set) -
|
|
136
|
-
disabled_clouds_set))
|
|
137
|
-
global_user_state.set_enabled_clouds(
|
|
138
|
-
list(enabled_clouds_for_capability), capability)
|
|
139
|
-
all_enabled_clouds = all_enabled_clouds.union(
|
|
140
|
-
enabled_clouds_for_capability)
|
|
141
|
-
disallowed_clouds_hint = None
|
|
142
|
-
if disallowed_cloud_names:
|
|
143
|
-
disallowed_clouds_hint = (
|
|
144
|
-
'\nNote: The following clouds were disabled because they were not '
|
|
145
|
-
'included in allowed_clouds in ~/.sky/config.yaml: '
|
|
146
|
-
f'{", ".join([c for c in disallowed_cloud_names])}')
|
|
147
|
-
if not all_enabled_clouds:
|
|
148
308
|
echo(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
309
|
+
_summary_message(enabled_clouds, cloud2ctx2text,
|
|
310
|
+
current_workspace_name, hide_workspace_str,
|
|
311
|
+
disallowed_cloud_names))
|
|
312
|
+
|
|
313
|
+
return enabled_clouds
|
|
314
|
+
|
|
315
|
+
# --- Main check_capabilities logic ---
|
|
316
|
+
|
|
317
|
+
if workspace is not None:
|
|
318
|
+
# Check only the specified workspace
|
|
319
|
+
if workspace not in available_workspaces:
|
|
320
|
+
with ux_utils.print_exception_no_traceback():
|
|
321
|
+
raise ValueError(
|
|
322
|
+
f'Workspace {workspace!r} not found in SkyPilot '
|
|
323
|
+
'configuration. '
|
|
324
|
+
f'Available workspaces: {", ".join(available_workspaces)}')
|
|
325
|
+
|
|
326
|
+
# Always show details for single specified check (if verbose)
|
|
327
|
+
hide_per_cloud_details_flag = False
|
|
328
|
+
with skypilot_config.local_active_workspace_ctx(workspace):
|
|
329
|
+
enabled_ws_clouds = _execute_check_logic_for_workspace(
|
|
330
|
+
workspace, hide_per_cloud_details_flag, hide_workspace_str)
|
|
331
|
+
all_workspaces_results[workspace] = enabled_ws_clouds
|
|
157
332
|
else:
|
|
158
|
-
|
|
159
|
-
|
|
333
|
+
# Check all workspaces
|
|
334
|
+
workspaces_to_check = available_workspaces
|
|
335
|
+
|
|
336
|
+
hide_per_cloud_details_flag = (not verbose and clouds is None and
|
|
337
|
+
len(workspaces_to_check) > 1)
|
|
338
|
+
|
|
339
|
+
for ws_name in workspaces_to_check:
|
|
340
|
+
if not hide_workspace_str:
|
|
341
|
+
echo(f'\nChecking enabled infra for workspace: {ws_name!r}')
|
|
342
|
+
with skypilot_config.local_active_workspace_ctx(ws_name):
|
|
343
|
+
enabled_ws_clouds = _execute_check_logic_for_workspace(
|
|
344
|
+
ws_name, hide_per_cloud_details_flag, hide_workspace_str)
|
|
345
|
+
all_workspaces_results[ws_name] = enabled_ws_clouds
|
|
346
|
+
|
|
347
|
+
# Global "To enable a cloud..." message, printed once if relevant
|
|
348
|
+
if not quiet:
|
|
160
349
|
echo(
|
|
161
350
|
click.style(
|
|
162
351
|
'\nTo enable a cloud, follow the hints above and rerun: ',
|
|
163
|
-
dim=True) + click.style(
|
|
164
|
-
|
|
352
|
+
dim=True) + click.style('sky check', bold=True) + '\n' +
|
|
353
|
+
click.style(
|
|
165
354
|
'If any problems remain, refer to detailed docs at: '
|
|
166
355
|
'https://docs.skypilot.co/en/latest/getting-started/installation.html', # pylint: disable=line-too-long
|
|
167
356
|
dim=True))
|
|
168
357
|
|
|
169
|
-
|
|
170
|
-
echo(click.style(disallowed_clouds_hint, dim=True))
|
|
171
|
-
|
|
172
|
-
# Pretty print for UX.
|
|
173
|
-
if not quiet:
|
|
174
|
-
enabled_clouds_str = '\n ' + '\n '.join([
|
|
175
|
-
_format_enabled_cloud(cloud, capabilities)
|
|
176
|
-
for cloud, capabilities in enabled_clouds.items()
|
|
177
|
-
])
|
|
178
|
-
echo(f'\n{colorama.Fore.GREEN}{PARTY_POPPER_EMOJI} '
|
|
179
|
-
f'Enabled clouds {PARTY_POPPER_EMOJI}'
|
|
180
|
-
f'{colorama.Style.RESET_ALL}{enabled_clouds_str}')
|
|
181
|
-
return enabled_clouds
|
|
358
|
+
return all_workspaces_results
|
|
182
359
|
|
|
183
360
|
|
|
184
361
|
def check_capability(
|
|
@@ -186,12 +363,15 @@ def check_capability(
|
|
|
186
363
|
quiet: bool = False,
|
|
187
364
|
verbose: bool = False,
|
|
188
365
|
clouds: Optional[Iterable[str]] = None,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
366
|
+
workspace: Optional[str] = None,
|
|
367
|
+
) -> Dict[str, List[str]]:
|
|
368
|
+
clouds_with_capability = collections.defaultdict(list)
|
|
369
|
+
workspace_enabled_clouds = check_capabilities(quiet, verbose, clouds,
|
|
370
|
+
[capability], workspace)
|
|
371
|
+
for workspace, enabled_clouds in workspace_enabled_clouds.items():
|
|
372
|
+
for cloud, capabilities in enabled_clouds.items():
|
|
373
|
+
if capability in capabilities:
|
|
374
|
+
clouds_with_capability[workspace].append(cloud)
|
|
195
375
|
return clouds_with_capability
|
|
196
376
|
|
|
197
377
|
|
|
@@ -199,10 +379,31 @@ def check(
|
|
|
199
379
|
quiet: bool = False,
|
|
200
380
|
verbose: bool = False,
|
|
201
381
|
clouds: Optional[Iterable[str]] = None,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
382
|
+
workspace: Optional[str] = None,
|
|
383
|
+
) -> Dict[str, List[str]]:
|
|
384
|
+
enabled_clouds_by_workspace: Dict[str,
|
|
385
|
+
List[str]] = collections.defaultdict(list)
|
|
386
|
+
capabilities_result = check_capabilities(quiet, verbose, clouds,
|
|
387
|
+
sky_cloud.ALL_CAPABILITIES,
|
|
388
|
+
workspace)
|
|
389
|
+
for ws_name, enabled_clouds_with_capabilities in capabilities_result.items(
|
|
390
|
+
):
|
|
391
|
+
# For each workspace, get a list of cloud names that have any
|
|
392
|
+
# capabilities enabled.
|
|
393
|
+
# The inner dict enabled_clouds_with_capabilities maps cloud_name to
|
|
394
|
+
# List[CloudCapability].
|
|
395
|
+
# If the list of capabilities is non-empty, the cloud is considered
|
|
396
|
+
# enabled.
|
|
397
|
+
# We are interested in the keys (cloud names) of this dict if their
|
|
398
|
+
# value (list of capabilities) is not empty.
|
|
399
|
+
# However, check_capabilities already ensures that only clouds with
|
|
400
|
+
# *some* enabled capabilities (from the ones being checked, i.e.
|
|
401
|
+
# ALL_CAPABILITIES here) are included in its return value.
|
|
402
|
+
# So, the keys of enabled_clouds_with_capabilities are the enabled cloud
|
|
403
|
+
# names for that workspace.
|
|
404
|
+
enabled_clouds_by_workspace[ws_name] = list(
|
|
405
|
+
enabled_clouds_with_capabilities.keys())
|
|
406
|
+
return enabled_clouds_by_workspace
|
|
206
407
|
|
|
207
408
|
|
|
208
409
|
def get_cached_enabled_clouds_or_refresh(
|
|
@@ -220,18 +421,30 @@ def get_cached_enabled_clouds_or_refresh(
|
|
|
220
421
|
exceptions.NoCloudAccessError: if no public cloud is enabled and
|
|
221
422
|
raise_if_no_cloud_access is set to True.
|
|
222
423
|
"""
|
|
424
|
+
active_workspace = skypilot_config.get_active_workspace()
|
|
425
|
+
allowed_clouds_changed = False
|
|
426
|
+
cached_allowed_clouds = global_user_state.get_allowed_clouds(
|
|
427
|
+
active_workspace)
|
|
428
|
+
skypilot_config_allowed_clouds = _get_workspace_allowed_clouds(
|
|
429
|
+
active_workspace)
|
|
430
|
+
if sorted(cached_allowed_clouds) != sorted(skypilot_config_allowed_clouds):
|
|
431
|
+
allowed_clouds_changed = True
|
|
432
|
+
|
|
223
433
|
cached_enabled_clouds = global_user_state.get_cached_enabled_clouds(
|
|
224
|
-
capability)
|
|
225
|
-
if not cached_enabled_clouds:
|
|
434
|
+
capability, active_workspace)
|
|
435
|
+
if not cached_enabled_clouds or allowed_clouds_changed:
|
|
226
436
|
try:
|
|
227
|
-
check_capability(
|
|
437
|
+
check_capability(capability, quiet=True, workspace=active_workspace)
|
|
438
|
+
if allowed_clouds_changed:
|
|
439
|
+
global_user_state.set_allowed_clouds(
|
|
440
|
+
skypilot_config_allowed_clouds, active_workspace)
|
|
228
441
|
except SystemExit:
|
|
229
442
|
# If no cloud is enabled, check() will raise SystemExit.
|
|
230
443
|
# Here we catch it and raise the exception later only if
|
|
231
444
|
# raise_if_no_cloud_access is set to True.
|
|
232
445
|
pass
|
|
233
446
|
cached_enabled_clouds = global_user_state.get_cached_enabled_clouds(
|
|
234
|
-
capability)
|
|
447
|
+
capability, active_workspace)
|
|
235
448
|
if raise_if_no_cloud_access and not cached_enabled_clouds:
|
|
236
449
|
with ux_utils.print_exception_no_traceback():
|
|
237
450
|
raise exceptions.NoCloudAccessError(
|
|
@@ -261,7 +474,8 @@ def get_cloud_credential_file_mounts(
|
|
|
261
474
|
cloud_file_mounts = cloud.get_credential_file_mounts()
|
|
262
475
|
for remote_path, local_path in cloud_file_mounts.items():
|
|
263
476
|
if os.path.exists(os.path.expanduser(local_path)):
|
|
264
|
-
file_mounts[remote_path] =
|
|
477
|
+
file_mounts[remote_path] = os.path.realpath(
|
|
478
|
+
os.path.expanduser(local_path))
|
|
265
479
|
# Currently, get_cached_enabled_clouds_or_refresh() does not support r2 as
|
|
266
480
|
# only clouds with computing instances are marked as enabled by skypilot.
|
|
267
481
|
# This will be removed when cloudflare/r2 is added as a 'cloud'.
|
|
@@ -269,6 +483,12 @@ def get_cloud_credential_file_mounts(
|
|
|
269
483
|
if r2_is_enabled:
|
|
270
484
|
r2_credential_mounts = cloudflare.get_credential_file_mounts()
|
|
271
485
|
file_mounts.update(r2_credential_mounts)
|
|
486
|
+
|
|
487
|
+
# Similarly, handle CoreWeave storage credentials
|
|
488
|
+
coreweave_is_enabled, _ = coreweave.check_storage_credentials()
|
|
489
|
+
if coreweave_is_enabled:
|
|
490
|
+
coreweave_credential_mounts = coreweave.get_credential_file_mounts()
|
|
491
|
+
file_mounts.update(coreweave_credential_mounts)
|
|
272
492
|
return file_mounts
|
|
273
493
|
|
|
274
494
|
|
|
@@ -277,7 +497,8 @@ def _print_checked_cloud(
|
|
|
277
497
|
verbose: bool,
|
|
278
498
|
cloud_tuple: Tuple[str, Union[sky_clouds.Cloud, ModuleType]],
|
|
279
499
|
cloud_capabilities: List[Tuple[sky_cloud.CloudCapability, bool,
|
|
280
|
-
Optional[str]]],
|
|
500
|
+
Optional[Union[str, Dict[str, str]]]]],
|
|
501
|
+
ctx2text: Dict[str, str],
|
|
281
502
|
) -> None:
|
|
282
503
|
"""Prints whether a cloud is enabled, and the capabilities that are enabled.
|
|
283
504
|
If any hints (for enabled capabilities) or
|
|
@@ -304,34 +525,146 @@ def _print_checked_cloud(
|
|
|
304
525
|
for capability, ok, reason in cloud_capabilities:
|
|
305
526
|
if ok:
|
|
306
527
|
enabled_capabilities.append(capability)
|
|
528
|
+
# `dict` reasons for K8s and SSH will be printed in detail in
|
|
529
|
+
# _format_enabled_cloud. Skip here unless the cloud is disabled.
|
|
530
|
+
if not isinstance(reason, str):
|
|
531
|
+
if not ok and isinstance(cloud_tuple[1],
|
|
532
|
+
(sky_clouds.SSH, sky_clouds.Kubernetes)):
|
|
533
|
+
if reason is not None:
|
|
534
|
+
reason_str = _format_context_details(cloud_tuple[1],
|
|
535
|
+
show_details=True,
|
|
536
|
+
ctx2text=reason)
|
|
537
|
+
reason_str = '\n'.join(
|
|
538
|
+
' ' + line for line in reason_str.splitlines())
|
|
539
|
+
reasons_to_capabilities.setdefault(reason_str,
|
|
540
|
+
[]).append(capability)
|
|
541
|
+
continue
|
|
542
|
+
if ok:
|
|
307
543
|
if reason is not None:
|
|
308
544
|
hints_to_capabilities.setdefault(reason, []).append(capability)
|
|
309
545
|
elif reason is not None:
|
|
310
546
|
reasons_to_capabilities.setdefault(reason, []).append(capability)
|
|
547
|
+
style_str = f'{colorama.Style.DIM}'
|
|
311
548
|
status_msg: str = 'disabled'
|
|
312
|
-
styles: Dict[str, Any] = {'dim': True}
|
|
313
549
|
capability_string: str = ''
|
|
550
|
+
detail_string: str = ''
|
|
314
551
|
activated_account: Optional[str] = None
|
|
315
552
|
if enabled_capabilities:
|
|
553
|
+
style_str = f'{colorama.Fore.GREEN}{colorama.Style.NORMAL}'
|
|
316
554
|
status_msg = 'enabled'
|
|
317
|
-
styles = {'fg': 'green', 'bold': False}
|
|
318
555
|
capability_string = f'[{", ".join(enabled_capabilities)}]'
|
|
319
|
-
if verbose and cloud is not cloudflare:
|
|
556
|
+
if verbose and cloud is not cloudflare and cloud is not coreweave:
|
|
320
557
|
activated_account = cloud.get_active_user_identity_str()
|
|
321
|
-
|
|
558
|
+
if isinstance(cloud_tuple[1], (sky_clouds.SSH, sky_clouds.Kubernetes)):
|
|
559
|
+
detail_string = _format_context_details(cloud_tuple[1],
|
|
560
|
+
show_details=True,
|
|
561
|
+
ctx2text=ctx2text)
|
|
322
562
|
echo(
|
|
323
|
-
click.style(
|
|
324
|
-
|
|
563
|
+
click.style(
|
|
564
|
+
f'{style_str} {cloud_repr}: {status_msg} {capability_string}'
|
|
565
|
+
f'{colorama.Style.RESET_ALL}{detail_string}'))
|
|
325
566
|
if activated_account is not None:
|
|
326
567
|
echo(f' Activated account: {activated_account}')
|
|
327
|
-
for reason,
|
|
328
|
-
echo(f' Hint [{", ".join(
|
|
329
|
-
for reason,
|
|
330
|
-
echo(f' Reason [{", ".join(
|
|
568
|
+
for reason, capabilities in hints_to_capabilities.items():
|
|
569
|
+
echo(f' Hint [{", ".join(capabilities)}]: {_yellow_color(reason)}')
|
|
570
|
+
for reason, capabilities in reasons_to_capabilities.items():
|
|
571
|
+
echo(f' Reason [{", ".join(capabilities)}]: {reason}')
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def _green_color(str_to_format: str) -> str:
|
|
575
|
+
return f'{colorama.Fore.GREEN}{str_to_format}{colorama.Style.RESET_ALL}'
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _format_context_details(cloud: Union[str, sky_clouds.Cloud],
|
|
579
|
+
show_details: bool,
|
|
580
|
+
ctx2text: Optional[Dict[str, str]] = None) -> str:
|
|
581
|
+
if isinstance(cloud, str):
|
|
582
|
+
cloud_type = registry.CLOUD_REGISTRY.from_str(cloud)
|
|
583
|
+
assert cloud_type is not None
|
|
584
|
+
else:
|
|
585
|
+
cloud_type = cloud
|
|
586
|
+
if isinstance(cloud_type, sky_clouds.SSH):
|
|
587
|
+
# Get the cluster names by reading from the node pools file
|
|
588
|
+
contexts = sky_clouds.SSH.get_ssh_node_pool_contexts()
|
|
589
|
+
else:
|
|
590
|
+
assert isinstance(cloud_type, sky_clouds.Kubernetes)
|
|
591
|
+
contexts = sky_clouds.Kubernetes.existing_allowed_contexts()
|
|
592
|
+
|
|
593
|
+
filtered_contexts = []
|
|
594
|
+
for context in contexts:
|
|
595
|
+
if not show_details:
|
|
596
|
+
# Skip
|
|
597
|
+
if (ctx2text is None or context not in ctx2text or
|
|
598
|
+
'disabled' in ctx2text[context]):
|
|
599
|
+
continue
|
|
600
|
+
filtered_contexts.append(context)
|
|
601
|
+
|
|
602
|
+
if not filtered_contexts:
|
|
603
|
+
return ''
|
|
604
|
+
|
|
605
|
+
def _red_color(str_to_format: str) -> str:
|
|
606
|
+
return (f'{colorama.Fore.LIGHTRED_EX}'
|
|
607
|
+
f'{str_to_format}'
|
|
608
|
+
f'{colorama.Style.RESET_ALL}')
|
|
609
|
+
|
|
610
|
+
def _dim_color(str_to_format: str) -> str:
|
|
611
|
+
return (f'{colorama.Style.DIM}'
|
|
612
|
+
f'{str_to_format}'
|
|
613
|
+
f'{colorama.Style.RESET_ALL}')
|
|
614
|
+
|
|
615
|
+
# For SSH, determine which contexts are disabled due to allowed_node_pools
|
|
616
|
+
disabled_due_to_allowed_node_pools = set()
|
|
617
|
+
if isinstance(cloud_type, sky_clouds.SSH):
|
|
618
|
+
# Get all node pool contexts from file
|
|
619
|
+
all_node_pool_contexts = sky_clouds.SSH.get_ssh_node_pool_contexts()
|
|
620
|
+
# Get allowed contexts (after filtering)
|
|
621
|
+
allowed_contexts = sky_clouds.SSH.existing_allowed_contexts()
|
|
622
|
+
# Contexts that exist in file but not in allowed list are disabled
|
|
623
|
+
# due to allowed_node_pools configuration
|
|
624
|
+
disabled_due_to_allowed_node_pools = (set(all_node_pool_contexts) -
|
|
625
|
+
set(allowed_contexts))
|
|
626
|
+
|
|
627
|
+
# Format the context info with consistent styling
|
|
628
|
+
contexts_formatted = []
|
|
629
|
+
for i, context in enumerate(filtered_contexts):
|
|
630
|
+
if isinstance(cloud_type, sky_clouds.SSH):
|
|
631
|
+
# TODO: This is a hack to remove the 'ssh-' prefix from the
|
|
632
|
+
# context name. Once we have a separate kubeconfig for SSH,
|
|
633
|
+
# this will not be required.
|
|
634
|
+
cleaned_context = common_utils.removeprefix(context, 'ssh-')
|
|
635
|
+
else:
|
|
636
|
+
cleaned_context = context
|
|
637
|
+
symbol = (ux_utils.INDENT_LAST_SYMBOL if i == len(filtered_contexts) -
|
|
638
|
+
1 else ux_utils.INDENT_SYMBOL)
|
|
639
|
+
text_suffix = ''
|
|
640
|
+
if show_details:
|
|
641
|
+
if ctx2text is not None:
|
|
642
|
+
if context in ctx2text:
|
|
643
|
+
text_suffix = f': {ctx2text[context]}'
|
|
644
|
+
elif (isinstance(cloud_type, sky_clouds.SSH) and
|
|
645
|
+
context in disabled_due_to_allowed_node_pools):
|
|
646
|
+
# Context is disabled due to allowed_node_pools config
|
|
647
|
+
text_suffix = (': ' + _red_color('disabled. ') +
|
|
648
|
+
_dim_color('Reason: Not included in '
|
|
649
|
+
'allowed_node_pools '
|
|
650
|
+
'configuration.'))
|
|
651
|
+
else:
|
|
652
|
+
# Default case - not set up
|
|
653
|
+
text_suffix = (': ' + _red_color('disabled. ') +
|
|
654
|
+
_dim_color('Reason: Not set up. Use '
|
|
655
|
+
'`sky ssh up --infra '
|
|
656
|
+
f'{context.lstrip("ssh-")}` '
|
|
657
|
+
'to set up.'))
|
|
658
|
+
contexts_formatted.append(
|
|
659
|
+
f'\n {symbol}{cleaned_context}{text_suffix}')
|
|
660
|
+
identity_str = ('SSH Node Pools' if isinstance(cloud_type, sky_clouds.SSH)
|
|
661
|
+
else 'Allowed contexts')
|
|
662
|
+
return f'\n {identity_str}:{"".join(contexts_formatted)}'
|
|
331
663
|
|
|
332
664
|
|
|
333
665
|
def _format_enabled_cloud(cloud_name: str,
|
|
334
|
-
capabilities: List[sky_cloud.CloudCapability]
|
|
666
|
+
capabilities: List[sky_cloud.CloudCapability],
|
|
667
|
+
ctx2text: Optional[Dict[str, str]] = None) -> str:
|
|
335
668
|
"""Format the summary of enabled cloud and its enabled capabilities.
|
|
336
669
|
|
|
337
670
|
Args:
|
|
@@ -342,34 +675,49 @@ def _format_enabled_cloud(cloud_name: str,
|
|
|
342
675
|
A string of the formatted cloud and capabilities.
|
|
343
676
|
"""
|
|
344
677
|
cloud_and_capabilities = f'{cloud_name} [{", ".join(capabilities)}]'
|
|
678
|
+
title = _green_color(cloud_and_capabilities)
|
|
345
679
|
|
|
346
|
-
|
|
347
|
-
return (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if cloud_name == repr(sky_clouds.Kubernetes()):
|
|
351
|
-
# Get enabled contexts for Kubernetes
|
|
352
|
-
existing_contexts = sky_clouds.Kubernetes.existing_allowed_contexts()
|
|
353
|
-
if not existing_contexts:
|
|
354
|
-
return _green_color(cloud_and_capabilities)
|
|
355
|
-
|
|
356
|
-
# Check if allowed_contexts is explicitly set in config
|
|
357
|
-
allowed_contexts = skypilot_config.get_nested(
|
|
358
|
-
('kubernetes', 'allowed_contexts'), None)
|
|
359
|
-
|
|
360
|
-
# Format the context info with consistent styling
|
|
361
|
-
if allowed_contexts is not None:
|
|
362
|
-
contexts_formatted = []
|
|
363
|
-
for i, context in enumerate(existing_contexts):
|
|
364
|
-
symbol = (ux_utils.INDENT_LAST_SYMBOL
|
|
365
|
-
if i == len(existing_contexts) -
|
|
366
|
-
1 else ux_utils.INDENT_SYMBOL)
|
|
367
|
-
contexts_formatted.append(f'\n {symbol}{context}')
|
|
368
|
-
context_info = f' Allowed contexts:{"".join(contexts_formatted)}'
|
|
369
|
-
else:
|
|
370
|
-
context_info = f' Active context: {existing_contexts[0]}'
|
|
371
|
-
|
|
372
|
-
return (f'{_green_color(cloud_and_capabilities)}\n'
|
|
373
|
-
f' {colorama.Style.DIM}{context_info}'
|
|
374
|
-
f'{colorama.Style.RESET_ALL}')
|
|
680
|
+
if cloud_name in [repr(sky_clouds.Kubernetes()), repr(sky_clouds.SSH())]:
|
|
681
|
+
return (f'{title}' + _format_context_details(
|
|
682
|
+
cloud_name, show_details=False, ctx2text=ctx2text))
|
|
375
683
|
return _green_color(cloud_and_capabilities)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def _summary_message(
|
|
687
|
+
enabled_clouds: Dict[str, List[sky_cloud.CloudCapability]],
|
|
688
|
+
cloud2ctx2text: Dict[str, Dict[str, str]],
|
|
689
|
+
current_workspace_name: str,
|
|
690
|
+
hide_workspace_str: bool,
|
|
691
|
+
disallowed_cloud_names: List[str],
|
|
692
|
+
) -> str:
|
|
693
|
+
if not enabled_clouds:
|
|
694
|
+
enabled_clouds_str = '\n No infra to check/enabled.'
|
|
695
|
+
else:
|
|
696
|
+
enabled_clouds_str = '\n ' + '\n '.join([
|
|
697
|
+
_format_enabled_cloud(cloud, capabilities,
|
|
698
|
+
cloud2ctx2text.get(cloud, None))
|
|
699
|
+
for cloud, capabilities in sorted(enabled_clouds.items(),
|
|
700
|
+
key=lambda item: item[0])
|
|
701
|
+
])
|
|
702
|
+
|
|
703
|
+
workspace_str = f' for workspace: {current_workspace_name!r}'
|
|
704
|
+
if hide_workspace_str:
|
|
705
|
+
workspace_str = ''
|
|
706
|
+
|
|
707
|
+
disallowed_clouds_hint = ''
|
|
708
|
+
if disallowed_cloud_names:
|
|
709
|
+
disable_for_workspace_hint = (
|
|
710
|
+
f' or disabled for this workspace {current_workspace_name!r}')
|
|
711
|
+
if hide_workspace_str:
|
|
712
|
+
disable_for_workspace_hint = ''
|
|
713
|
+
disallowed_clouds_hint = (
|
|
714
|
+
'\nNote: The following clouds were disabled because they were not '
|
|
715
|
+
'included in allowed_clouds in ~/.sky/config.yaml'
|
|
716
|
+
f'{disable_for_workspace_hint}: '
|
|
717
|
+
f'{", ".join([c for c in disallowed_cloud_names])}')
|
|
718
|
+
|
|
719
|
+
return (f'\n{colorama.Fore.GREEN}{PARTY_POPPER_EMOJI} '
|
|
720
|
+
f'Enabled infra{workspace_str} '
|
|
721
|
+
f'{PARTY_POPPER_EMOJI}'
|
|
722
|
+
f'{colorama.Style.RESET_ALL}{enabled_clouds_str}'
|
|
723
|
+
f'{disallowed_clouds_hint}')
|