skypilot-nightly 1.0.0.dev20250905__py3-none-any.whl → 1.0.0.dev20251210__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sky/__init__.py +12 -2
- sky/adaptors/aws.py +27 -22
- 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/ibm.py +5 -2
- sky/adaptors/kubernetes.py +64 -0
- sky/adaptors/nebius.py +3 -1
- sky/adaptors/primeintellect.py +1 -0
- sky/adaptors/seeweb.py +183 -0
- sky/adaptors/shadeform.py +89 -0
- sky/adaptors/slurm.py +478 -0
- sky/admin_policy.py +20 -0
- sky/authentication.py +157 -263
- sky/backends/__init__.py +3 -2
- sky/backends/backend.py +11 -3
- sky/backends/backend_utils.py +630 -185
- sky/backends/cloud_vm_ray_backend.py +1111 -928
- sky/backends/local_docker_backend.py +9 -5
- sky/backends/task_codegen.py +971 -0
- sky/backends/wheel_utils.py +18 -0
- sky/catalog/__init__.py +8 -3
- sky/catalog/aws_catalog.py +4 -0
- sky/catalog/common.py +19 -1
- sky/catalog/data_fetchers/fetch_aws.py +102 -80
- sky/catalog/data_fetchers/fetch_gcp.py +30 -3
- sky/catalog/data_fetchers/fetch_nebius.py +9 -6
- 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/catalog/kubernetes_catalog.py +36 -32
- sky/catalog/primeintellect_catalog.py +95 -0
- sky/catalog/runpod_catalog.py +5 -1
- sky/catalog/seeweb_catalog.py +184 -0
- sky/catalog/shadeform_catalog.py +165 -0
- sky/catalog/slurm_catalog.py +243 -0
- sky/check.py +87 -46
- sky/client/cli/command.py +1004 -434
- sky/client/cli/flags.py +4 -2
- sky/{volumes/utils.py → client/cli/table_utils.py} +111 -13
- sky/client/cli/utils.py +79 -0
- sky/client/common.py +12 -2
- sky/client/sdk.py +188 -65
- sky/client/sdk_async.py +34 -33
- sky/cloud_stores.py +82 -3
- sky/clouds/__init__.py +8 -0
- sky/clouds/aws.py +337 -129
- sky/clouds/azure.py +24 -18
- sky/clouds/cloud.py +47 -13
- sky/clouds/cudo.py +16 -13
- sky/clouds/do.py +9 -7
- sky/clouds/fluidstack.py +12 -5
- sky/clouds/gcp.py +14 -7
- sky/clouds/hyperbolic.py +12 -5
- sky/clouds/ibm.py +12 -5
- sky/clouds/kubernetes.py +80 -45
- sky/clouds/lambda_cloud.py +12 -5
- sky/clouds/nebius.py +23 -9
- sky/clouds/oci.py +19 -12
- sky/clouds/paperspace.py +4 -1
- sky/clouds/primeintellect.py +317 -0
- sky/clouds/runpod.py +85 -24
- sky/clouds/scp.py +12 -8
- sky/clouds/seeweb.py +477 -0
- sky/clouds/shadeform.py +400 -0
- sky/clouds/slurm.py +578 -0
- sky/clouds/ssh.py +6 -3
- sky/clouds/utils/scp_utils.py +61 -50
- sky/clouds/vast.py +43 -27
- sky/clouds/vsphere.py +14 -16
- sky/core.py +296 -195
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/_next/static/KYAhEFa3FTfq4JyKVgo-s/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/1141-9c810f01ff4f398a.js +11 -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/2369.fc20f0c2c8ed9fe7.js +15 -0
- sky/dashboard/out/_next/static/chunks/2755.edd818326d489a1d.js +26 -0
- sky/dashboard/out/_next/static/chunks/3294.ddda8c6c6f9f24dc.js +1 -0
- sky/dashboard/out/_next/static/chunks/3785.7e245f318f9d1121.js +1 -0
- sky/dashboard/out/_next/static/chunks/{6601-06114c982db410b6.js → 3800-b589397dc09c5b4e.js} +1 -1
- sky/dashboard/out/_next/static/chunks/3850-fd5696f3bbbaddae.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/6212-7bd06f60ba693125.js +13 -0
- sky/dashboard/out/_next/static/chunks/6856-da20c5fd999f319c.js +1 -0
- sky/dashboard/out/_next/static/chunks/6990-09cbf02d3cd518c3.js +1 -0
- sky/dashboard/out/_next/static/chunks/7359-c8d04e06886000b3.js +30 -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-8369df1cf105221c.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/pages/_app-68b647e26f9d2793.js +34 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33f525539665fdfd.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-a7565f586ef86467.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-9e5d47818b9bdadd.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/{config-dfb9bf07b13045f4.js → config-718cdc365de82689.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/infra/{[context]-6563820e094f68ca.js → [context]-12c559ec4d81fdbd.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/{infra-aabba60d57826e0f.js → infra-d187cd0413d72475.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-895847b6cf200b04.js +16 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-8d0f4655400b4eb9.js +21 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-e5a98f17f8513a96.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/plugins/[...slug]-4f46050ca065d8f8.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/users-2f7646eb77785a2c.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/volumes-ef19d49c6d0e8500.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-af76bb06dbb3954f.js → [name]-96e0f298308da7e2.js} +1 -1
- sky/dashboard/out/_next/static/chunks/pages/{workspaces-7598c33a746cdc91.js → workspaces-cb4da3abe08ebf19.js} +1 -1
- sky/dashboard/out/_next/static/chunks/webpack-fba3de387ff6bb08.js +1 -0
- sky/dashboard/out/_next/static/css/c5a4cfd2600fc715.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/pools/[pool].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/plugins/[...slug].html +1 -0
- 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/data_utils.py +92 -1
- sky/data/mounting_utils.py +177 -30
- sky/data/storage.py +200 -19
- sky/data/storage_utils.py +10 -45
- sky/exceptions.py +18 -7
- sky/execution.py +74 -31
- sky/global_user_state.py +605 -191
- sky/jobs/__init__.py +2 -0
- sky/jobs/client/sdk.py +101 -4
- sky/jobs/client/sdk_async.py +31 -5
- sky/jobs/constants.py +15 -8
- sky/jobs/controller.py +726 -284
- sky/jobs/file_content_utils.py +128 -0
- sky/jobs/log_gc.py +193 -0
- sky/jobs/recovery_strategy.py +250 -100
- sky/jobs/scheduler.py +271 -173
- sky/jobs/server/core.py +367 -114
- sky/jobs/server/server.py +81 -35
- sky/jobs/server/utils.py +89 -35
- sky/jobs/state.py +1498 -620
- sky/jobs/utils.py +771 -306
- sky/logs/agent.py +40 -5
- sky/logs/aws.py +9 -19
- sky/metrics/utils.py +282 -39
- sky/models.py +2 -0
- sky/optimizer.py +7 -6
- sky/provision/__init__.py +38 -1
- sky/provision/aws/config.py +34 -13
- sky/provision/aws/instance.py +5 -2
- sky/provision/azure/instance.py +5 -3
- sky/provision/common.py +22 -0
- sky/provision/cudo/instance.py +4 -3
- sky/provision/do/instance.py +4 -3
- sky/provision/docker_utils.py +112 -28
- sky/provision/fluidstack/instance.py +6 -5
- sky/provision/gcp/config.py +6 -1
- sky/provision/gcp/instance.py +4 -2
- sky/provision/hyperbolic/instance.py +4 -2
- sky/provision/instance_setup.py +66 -20
- sky/provision/kubernetes/__init__.py +2 -0
- sky/provision/kubernetes/config.py +7 -44
- sky/provision/kubernetes/constants.py +0 -1
- sky/provision/kubernetes/instance.py +609 -213
- sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml +1 -2
- sky/provision/kubernetes/network.py +12 -8
- sky/provision/kubernetes/network_utils.py +8 -25
- sky/provision/kubernetes/utils.py +422 -422
- sky/provision/kubernetes/volume.py +150 -18
- sky/provision/lambda_cloud/instance.py +16 -13
- sky/provision/nebius/instance.py +6 -2
- sky/provision/nebius/utils.py +103 -86
- sky/provision/oci/instance.py +4 -2
- sky/provision/paperspace/instance.py +4 -3
- 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 +45 -15
- sky/provision/runpod/__init__.py +2 -0
- sky/provision/runpod/instance.py +4 -3
- sky/provision/runpod/volume.py +69 -13
- sky/provision/scp/instance.py +307 -130
- 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/slurm/__init__.py +12 -0
- sky/provision/slurm/config.py +13 -0
- sky/provision/slurm/instance.py +572 -0
- sky/provision/slurm/utils.py +583 -0
- sky/provision/vast/instance.py +9 -4
- sky/provision/vast/utils.py +10 -6
- 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 +3 -2
- sky/provision/vsphere/instance.py +8 -6
- sky/provision/vsphere/vsphere_utils.py +8 -1
- sky/resources.py +11 -3
- sky/schemas/api/responses.py +107 -6
- 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/serve_state/002_yaml_content.py +34 -0
- sky/schemas/db/skypilot_config/001_initial_schema.py +30 -0
- sky/schemas/db/spot_jobs/002_cluster_pool.py +3 -3
- 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/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 +2 -0
- sky/serve/client/impl.py +55 -21
- sky/serve/constants.py +4 -3
- sky/serve/controller.py +17 -11
- sky/serve/load_balancing_policies.py +1 -1
- sky/serve/replica_managers.py +219 -142
- sky/serve/serve_rpc_utils.py +179 -0
- sky/serve/serve_state.py +63 -54
- sky/serve/serve_utils.py +145 -109
- sky/serve/server/core.py +46 -25
- sky/serve/server/impl.py +311 -162
- sky/serve/server/server.py +21 -19
- sky/serve/service.py +84 -68
- sky/serve/service_spec.py +45 -7
- sky/server/auth/loopback.py +38 -0
- sky/server/auth/oauth2_proxy.py +12 -7
- sky/server/common.py +47 -24
- sky/server/config.py +62 -28
- sky/server/constants.py +9 -1
- sky/server/daemons.py +109 -38
- sky/server/metrics.py +76 -96
- sky/server/middleware_utils.py +166 -0
- sky/server/plugins.py +222 -0
- sky/server/requests/executor.py +384 -145
- sky/server/requests/payloads.py +83 -19
- sky/server/requests/preconditions.py +15 -13
- sky/server/requests/request_names.py +123 -0
- sky/server/requests/requests.py +511 -157
- sky/server/requests/serializers/decoders.py +48 -17
- sky/server/requests/serializers/encoders.py +102 -20
- sky/server/requests/serializers/return_value_serializers.py +60 -0
- sky/server/requests/threads.py +117 -0
- sky/server/rest.py +116 -24
- sky/server/server.py +497 -179
- sky/server/server_utils.py +30 -0
- sky/server/stream_utils.py +219 -45
- sky/server/uvicorn.py +30 -19
- sky/setup_files/MANIFEST.in +6 -1
- sky/setup_files/alembic.ini +8 -0
- sky/setup_files/dependencies.py +64 -19
- sky/setup_files/setup.py +44 -44
- sky/sky_logging.py +13 -5
- sky/skylet/attempt_skylet.py +116 -24
- sky/skylet/configs.py +3 -1
- sky/skylet/constants.py +139 -29
- sky/skylet/events.py +74 -14
- sky/skylet/executor/__init__.py +1 -0
- sky/skylet/executor/slurm.py +189 -0
- sky/skylet/job_lib.py +143 -105
- sky/skylet/log_lib.py +252 -8
- sky/skylet/log_lib.pyi +47 -7
- sky/skylet/providers/ibm/node_provider.py +12 -8
- sky/skylet/providers/ibm/vpc_provider.py +13 -12
- sky/skylet/runtime_utils.py +21 -0
- sky/skylet/services.py +524 -0
- sky/skylet/skylet.py +27 -2
- sky/skylet/subprocess_daemon.py +104 -28
- sky/skypilot_config.py +99 -79
- sky/ssh_node_pools/constants.py +12 -0
- sky/ssh_node_pools/core.py +40 -3
- sky/ssh_node_pools/deploy/__init__.py +4 -0
- sky/ssh_node_pools/deploy/deploy.py +952 -0
- sky/ssh_node_pools/deploy/tunnel_utils.py +199 -0
- sky/ssh_node_pools/deploy/utils.py +173 -0
- sky/ssh_node_pools/server.py +20 -21
- sky/{utils/kubernetes/ssh_utils.py → ssh_node_pools/utils.py} +9 -6
- sky/task.py +221 -104
- sky/templates/aws-ray.yml.j2 +1 -0
- sky/templates/azure-ray.yml.j2 +1 -0
- sky/templates/cudo-ray.yml.j2 +1 -0
- sky/templates/do-ray.yml.j2 +1 -0
- sky/templates/fluidstack-ray.yml.j2 +1 -0
- sky/templates/gcp-ray.yml.j2 +1 -0
- sky/templates/hyperbolic-ray.yml.j2 +1 -0
- sky/templates/ibm-ray.yml.j2 +2 -1
- sky/templates/jobs-controller.yaml.j2 +3 -0
- sky/templates/kubernetes-ray.yml.j2 +204 -55
- sky/templates/lambda-ray.yml.j2 +1 -0
- sky/templates/nebius-ray.yml.j2 +3 -0
- sky/templates/oci-ray.yml.j2 +1 -0
- sky/templates/paperspace-ray.yml.j2 +1 -0
- sky/templates/primeintellect-ray.yml.j2 +72 -0
- sky/templates/runpod-ray.yml.j2 +1 -0
- sky/templates/scp-ray.yml.j2 +1 -0
- sky/templates/seeweb-ray.yml.j2 +171 -0
- sky/templates/shadeform-ray.yml.j2 +73 -0
- sky/templates/slurm-ray.yml.j2 +85 -0
- sky/templates/vast-ray.yml.j2 +2 -0
- sky/templates/vsphere-ray.yml.j2 +1 -0
- sky/templates/websocket_proxy.py +188 -43
- sky/usage/usage_lib.py +16 -4
- sky/users/model.conf +1 -1
- sky/users/permission.py +84 -44
- sky/users/rbac.py +31 -3
- sky/utils/accelerator_registry.py +6 -3
- sky/utils/admin_policy_utils.py +18 -5
- sky/utils/annotations.py +128 -6
- sky/utils/asyncio_utils.py +78 -0
- sky/utils/atomic.py +1 -1
- sky/utils/auth_utils.py +153 -0
- sky/utils/cli_utils/status_utils.py +12 -7
- sky/utils/cluster_utils.py +28 -6
- sky/utils/command_runner.py +283 -30
- sky/utils/command_runner.pyi +63 -7
- sky/utils/common.py +3 -1
- sky/utils/common_utils.py +55 -7
- sky/utils/config_utils.py +1 -14
- sky/utils/context.py +127 -40
- sky/utils/context_utils.py +73 -18
- sky/utils/controller_utils.py +229 -70
- sky/utils/db/db_utils.py +95 -18
- sky/utils/db/kv_cache.py +149 -0
- sky/utils/db/migration_utils.py +24 -7
- sky/utils/env_options.py +4 -0
- sky/utils/git.py +559 -1
- sky/utils/kubernetes/create_cluster.sh +15 -30
- sky/utils/kubernetes/delete_cluster.sh +10 -7
- sky/utils/kubernetes/generate_kind_config.py +6 -66
- sky/utils/kubernetes/gpu_labeler.py +13 -3
- 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 +187 -260
- sky/utils/kubernetes/rsync_helper.sh +11 -3
- sky/utils/kubernetes/ssh-tunnel.sh +7 -376
- sky/utils/kubernetes_enums.py +7 -15
- sky/utils/lock_events.py +4 -4
- sky/utils/locks.py +128 -31
- sky/utils/log_utils.py +0 -319
- sky/utils/resource_checker.py +13 -10
- sky/utils/resources_utils.py +53 -29
- sky/utils/rich_utils.py +8 -4
- sky/utils/schemas.py +138 -52
- sky/utils/subprocess_utils.py +17 -4
- sky/utils/thread_utils.py +91 -0
- sky/utils/timeline.py +2 -1
- sky/utils/ux_utils.py +35 -1
- sky/utils/volume.py +88 -4
- sky/utils/yaml_utils.py +9 -0
- sky/volumes/client/sdk.py +48 -10
- sky/volumes/server/core.py +59 -22
- sky/volumes/server/server.py +46 -17
- sky/volumes/volume.py +54 -42
- sky/workspaces/core.py +57 -21
- sky/workspaces/server.py +13 -12
- 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.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/METADATA +343 -65
- skypilot_nightly-1.0.0.dev20251210.dist-info/RECORD +629 -0
- skypilot_nightly-1.0.0.dev20251210.dist-info/top_level.txt +2 -0
- sky/client/cli/git.py +0 -549
- sky/dashboard/out/_next/static/chunks/1121-408ed10b2f9fce17.js +0 -1
- sky/dashboard/out/_next/static/chunks/1141-943efc7aff0f0c06.js +0 -1
- sky/dashboard/out/_next/static/chunks/1836-37fede578e2da5f8.js +0 -40
- sky/dashboard/out/_next/static/chunks/3015-86cabed5d4669ad0.js +0 -1
- sky/dashboard/out/_next/static/chunks/3294.c80326aec9bfed40.js +0 -6
- sky/dashboard/out/_next/static/chunks/3785.4872a2f3aa489880.js +0 -1
- sky/dashboard/out/_next/static/chunks/3850-ff4a9a69d978632b.js +0 -1
- sky/dashboard/out/_next/static/chunks/4045.b30465273dc5e468.js +0 -21
- sky/dashboard/out/_next/static/chunks/4676-9da7fdbde90b5549.js +0 -10
- sky/dashboard/out/_next/static/chunks/4725.10f7a9a5d3ea8208.js +0 -1
- sky/dashboard/out/_next/static/chunks/5339.3fda4a4010ff4e06.js +0 -51
- sky/dashboard/out/_next/static/chunks/6135-4b4d5e824b7f9d3c.js +0 -1
- sky/dashboard/out/_next/static/chunks/649.b9d7f7d10c1b8c53.js +0 -45
- sky/dashboard/out/_next/static/chunks/6856-dca7962af4814e1b.js +0 -1
- sky/dashboard/out/_next/static/chunks/6990-08b2a1cae076a943.js +0 -1
- sky/dashboard/out/_next/static/chunks/7325.b4bc99ce0892dcd5.js +0 -6
- sky/dashboard/out/_next/static/chunks/754-d0da8ab45f9509e9.js +0 -18
- sky/dashboard/out/_next/static/chunks/7669.1f5d9a402bf5cc42.js +0 -36
- sky/dashboard/out/_next/static/chunks/8969-0be3036bf86f8256.js +0 -1
- sky/dashboard/out/_next/static/chunks/9025.c12318fb6a1a9093.js +0 -6
- sky/dashboard/out/_next/static/chunks/9037-fa1737818d0a0969.js +0 -6
- sky/dashboard/out/_next/static/chunks/pages/_app-ce361c6959bc2001.js +0 -34
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-1cbba24bd1bd35f8.js +0 -16
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-0b4b35dc1dfe046c.js +0 -16
- sky/dashboard/out/_next/static/chunks/pages/clusters-469814d711d63b1b.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-dd64309c3fe67ed2.js +0 -11
- sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-07349868f7905d37.js +0 -16
- sky/dashboard/out/_next/static/chunks/pages/jobs-1f70d9faa564804f.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/users-018bf31cda52e11b.js +0 -1
- sky/dashboard/out/_next/static/chunks/pages/volumes-739726d6b823f532.js +0 -1
- sky/dashboard/out/_next/static/chunks/webpack-4fe903277b57b523.js +0 -1
- sky/dashboard/out/_next/static/css/4614e06482d7309e.css +0 -3
- sky/dashboard/out/_next/static/mS-4qZPSkRuA1u-g2wQhg/_buildManifest.js +0 -1
- sky/templates/kubernetes-ssh-jump.yml.j2 +0 -94
- sky/utils/kubernetes/cleanup-tunnel.sh +0 -62
- sky/utils/kubernetes/deploy_remote_cluster.py +0 -1299
- sky/utils/kubernetes/ssh_jump_lifecycle_manager.py +0 -191
- skypilot_nightly-1.0.0.dev20250905.dist-info/RECORD +0 -547
- skypilot_nightly-1.0.0.dev20250905.dist-info/top_level.txt +0 -1
- /sky/dashboard/out/_next/static/{mS-4qZPSkRuA1u-g2wQhg → KYAhEFa3FTfq4JyKVgo-s}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250905.dist-info → skypilot_nightly-1.0.0.dev20251210.dist-info}/licenses/LICENSE +0 -0
sky/adaptors/kubernetes.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Kubernetes adaptors"""
|
|
2
|
+
import functools
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
4
5
|
import platform
|
|
@@ -162,8 +163,61 @@ def list_kube_config_contexts():
|
|
|
162
163
|
return kubernetes.config.list_kube_config_contexts(_get_config_file())
|
|
163
164
|
|
|
164
165
|
|
|
166
|
+
class ClientWrapper:
|
|
167
|
+
"""Wrapper around the kubernetes API clients.
|
|
168
|
+
|
|
169
|
+
This is needed because we cache kubernetes.client.ApiClient and other typed
|
|
170
|
+
clients (e.g. kubernetes.client.CoreV1Api) and lru_cache.cache_clear() does
|
|
171
|
+
not call close() on the client to cleanup external resources like
|
|
172
|
+
semaphores. This decorator wraps the client with __del__ to ensure the
|
|
173
|
+
external state of kubernetes clients are properly cleaned up on GC.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
def __init__(self, client):
|
|
177
|
+
self._client = client
|
|
178
|
+
|
|
179
|
+
def __getattr__(self, name):
|
|
180
|
+
"""Delegate to the underlying client"""
|
|
181
|
+
return getattr(self._client, name)
|
|
182
|
+
|
|
183
|
+
def __del__(self):
|
|
184
|
+
"""Clean up the underlying client"""
|
|
185
|
+
try:
|
|
186
|
+
real_client = None
|
|
187
|
+
if isinstance(self._client, kubernetes.client.ApiClient):
|
|
188
|
+
real_client = self._client
|
|
189
|
+
elif isinstance(self._client, kubernetes.watch.Watch):
|
|
190
|
+
real_client = getattr(self._client, '_api_client', None)
|
|
191
|
+
else:
|
|
192
|
+
# Otherwise, the client is a typed client, the typed client
|
|
193
|
+
# is generated by codegen and all of them should have an
|
|
194
|
+
# 'api_client' attribute referring to the real client.
|
|
195
|
+
real_client = getattr(self._client, 'api_client', None)
|
|
196
|
+
if real_client is not None:
|
|
197
|
+
real_client.close()
|
|
198
|
+
else:
|
|
199
|
+
# logger may already be cleaned up during __del__ at shutdown
|
|
200
|
+
if logger is not None:
|
|
201
|
+
logger.debug(f'No client found for {self._client}')
|
|
202
|
+
except Exception as e: # pylint: disable=broad-except
|
|
203
|
+
if logger is not None:
|
|
204
|
+
logger.debug(f'Error closing Kubernetes client: {e}')
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def wrap_kubernetes_client(func):
|
|
208
|
+
"""Wraps kubernetes API clients for proper cleanup."""
|
|
209
|
+
|
|
210
|
+
@functools.wraps(func)
|
|
211
|
+
def wrapper(*args, **kwargs):
|
|
212
|
+
obj = func(*args, **kwargs)
|
|
213
|
+
return ClientWrapper(obj)
|
|
214
|
+
|
|
215
|
+
return wrapper
|
|
216
|
+
|
|
217
|
+
|
|
165
218
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
166
219
|
@annotations.lru_cache(scope='request')
|
|
220
|
+
@wrap_kubernetes_client
|
|
167
221
|
def core_api(context: Optional[str] = None):
|
|
168
222
|
_load_config(context)
|
|
169
223
|
return kubernetes.client.CoreV1Api()
|
|
@@ -171,6 +225,7 @@ def core_api(context: Optional[str] = None):
|
|
|
171
225
|
|
|
172
226
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
173
227
|
@annotations.lru_cache(scope='request')
|
|
228
|
+
@wrap_kubernetes_client
|
|
174
229
|
def storage_api(context: Optional[str] = None):
|
|
175
230
|
_load_config(context)
|
|
176
231
|
return kubernetes.client.StorageV1Api()
|
|
@@ -178,6 +233,7 @@ def storage_api(context: Optional[str] = None):
|
|
|
178
233
|
|
|
179
234
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
180
235
|
@annotations.lru_cache(scope='request')
|
|
236
|
+
@wrap_kubernetes_client
|
|
181
237
|
def auth_api(context: Optional[str] = None):
|
|
182
238
|
_load_config(context)
|
|
183
239
|
return kubernetes.client.RbacAuthorizationV1Api()
|
|
@@ -185,6 +241,7 @@ def auth_api(context: Optional[str] = None):
|
|
|
185
241
|
|
|
186
242
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
187
243
|
@annotations.lru_cache(scope='request')
|
|
244
|
+
@wrap_kubernetes_client
|
|
188
245
|
def networking_api(context: Optional[str] = None):
|
|
189
246
|
_load_config(context)
|
|
190
247
|
return kubernetes.client.NetworkingV1Api()
|
|
@@ -192,6 +249,7 @@ def networking_api(context: Optional[str] = None):
|
|
|
192
249
|
|
|
193
250
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
194
251
|
@annotations.lru_cache(scope='request')
|
|
252
|
+
@wrap_kubernetes_client
|
|
195
253
|
def custom_objects_api(context: Optional[str] = None):
|
|
196
254
|
_load_config(context)
|
|
197
255
|
return kubernetes.client.CustomObjectsApi()
|
|
@@ -199,6 +257,7 @@ def custom_objects_api(context: Optional[str] = None):
|
|
|
199
257
|
|
|
200
258
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
201
259
|
@annotations.lru_cache(scope='global')
|
|
260
|
+
@wrap_kubernetes_client
|
|
202
261
|
def node_api(context: Optional[str] = None):
|
|
203
262
|
_load_config(context)
|
|
204
263
|
return kubernetes.client.NodeV1Api()
|
|
@@ -206,6 +265,7 @@ def node_api(context: Optional[str] = None):
|
|
|
206
265
|
|
|
207
266
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
208
267
|
@annotations.lru_cache(scope='request')
|
|
268
|
+
@wrap_kubernetes_client
|
|
209
269
|
def apps_api(context: Optional[str] = None):
|
|
210
270
|
_load_config(context)
|
|
211
271
|
return kubernetes.client.AppsV1Api()
|
|
@@ -213,6 +273,7 @@ def apps_api(context: Optional[str] = None):
|
|
|
213
273
|
|
|
214
274
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
215
275
|
@annotations.lru_cache(scope='request')
|
|
276
|
+
@wrap_kubernetes_client
|
|
216
277
|
def batch_api(context: Optional[str] = None):
|
|
217
278
|
_load_config(context)
|
|
218
279
|
return kubernetes.client.BatchV1Api()
|
|
@@ -220,6 +281,7 @@ def batch_api(context: Optional[str] = None):
|
|
|
220
281
|
|
|
221
282
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
222
283
|
@annotations.lru_cache(scope='request')
|
|
284
|
+
@wrap_kubernetes_client
|
|
223
285
|
def api_client(context: Optional[str] = None):
|
|
224
286
|
_load_config(context)
|
|
225
287
|
return kubernetes.client.ApiClient()
|
|
@@ -227,6 +289,7 @@ def api_client(context: Optional[str] = None):
|
|
|
227
289
|
|
|
228
290
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
229
291
|
@annotations.lru_cache(scope='request')
|
|
292
|
+
@wrap_kubernetes_client
|
|
230
293
|
def custom_resources_api(context: Optional[str] = None):
|
|
231
294
|
_load_config(context)
|
|
232
295
|
return kubernetes.client.CustomObjectsApi()
|
|
@@ -234,6 +297,7 @@ def custom_resources_api(context: Optional[str] = None):
|
|
|
234
297
|
|
|
235
298
|
@_api_logging_decorator('urllib3', logging.ERROR)
|
|
236
299
|
@annotations.lru_cache(scope='request')
|
|
300
|
+
@wrap_kubernetes_client
|
|
237
301
|
def watch(context: Optional[str] = None):
|
|
238
302
|
_load_config(context)
|
|
239
303
|
return kubernetes.watch.Watch()
|
sky/adaptors/nebius.py
CHANGED
|
@@ -136,7 +136,9 @@ SKY_CHECK_NAME = 'Nebius (for Nebius Object Storae)'
|
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
def request_error():
|
|
139
|
-
|
|
139
|
+
# pylint: disable=import-outside-toplevel
|
|
140
|
+
from nebius.aio import service_error
|
|
141
|
+
return service_error.RequestError
|
|
140
142
|
|
|
141
143
|
|
|
142
144
|
def compute():
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Prime Intellect cloud adaptor."""
|
sky/adaptors/seeweb.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
""" Seeweb Adaptor """
|
|
2
|
+
import configparser
|
|
3
|
+
import pathlib
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import pydantic
|
|
7
|
+
import requests # type: ignore
|
|
8
|
+
|
|
9
|
+
from sky.adaptors import common
|
|
10
|
+
from sky.utils import annotations
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SeewebError(Exception):
|
|
14
|
+
"""Base exception for Seeweb adaptor errors."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SeewebCredentialsFileNotFound(SeewebError):
|
|
18
|
+
"""Raised when the Seeweb credentials file is missing."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SeewebApiKeyMissing(SeewebError):
|
|
22
|
+
"""Raised when the Seeweb API key is missing or empty."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SeewebAuthenticationError(SeewebError):
|
|
26
|
+
"""Raised when authenticating with Seeweb API fails."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_IMPORT_ERROR_MESSAGE = ('Failed to import dependencies for Seeweb.'
|
|
30
|
+
'Try pip install "skypilot[seeweb]"')
|
|
31
|
+
|
|
32
|
+
ecsapi = common.LazyImport(
|
|
33
|
+
'ecsapi',
|
|
34
|
+
import_error_message=_IMPORT_ERROR_MESSAGE,
|
|
35
|
+
)
|
|
36
|
+
boto3 = common.LazyImport('boto3', import_error_message=_IMPORT_ERROR_MESSAGE)
|
|
37
|
+
botocore = common.LazyImport('botocore',
|
|
38
|
+
import_error_message=_IMPORT_ERROR_MESSAGE)
|
|
39
|
+
|
|
40
|
+
_LAZY_MODULES = (ecsapi, boto3, botocore)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@common.load_lazy_modules(_LAZY_MODULES)
|
|
44
|
+
def check_compute_credentials() -> bool:
|
|
45
|
+
"""Checks if the user has access credentials to Seeweb's compute service.
|
|
46
|
+
|
|
47
|
+
Returns True if credentials are valid; otherwise raises a SeewebError.
|
|
48
|
+
"""
|
|
49
|
+
# Read API key from standard Seeweb configuration file
|
|
50
|
+
key_path = pathlib.Path('~/.seeweb_cloud/seeweb_keys').expanduser()
|
|
51
|
+
if not key_path.exists():
|
|
52
|
+
raise SeewebCredentialsFileNotFound(
|
|
53
|
+
'Missing Seeweb API key file ~/.seeweb_cloud/seeweb_keys')
|
|
54
|
+
|
|
55
|
+
parser = configparser.ConfigParser()
|
|
56
|
+
parser.read(key_path)
|
|
57
|
+
try:
|
|
58
|
+
api_key = parser['DEFAULT']['api_key'].strip()
|
|
59
|
+
except KeyError as e:
|
|
60
|
+
raise SeewebApiKeyMissing(
|
|
61
|
+
'Missing api_key in ~/.seeweb_cloud/seeweb_keys') from e
|
|
62
|
+
if not api_key:
|
|
63
|
+
raise SeewebApiKeyMissing(
|
|
64
|
+
'Empty api_key in ~/.seeweb_cloud/seeweb_keys')
|
|
65
|
+
|
|
66
|
+
# Test connection by fetching servers list to validate the key
|
|
67
|
+
try:
|
|
68
|
+
seeweb_client = ecsapi.Api(token=api_key)
|
|
69
|
+
try:
|
|
70
|
+
seeweb_client.fetch_servers()
|
|
71
|
+
except pydantic.ValidationError:
|
|
72
|
+
# Fallback: fetch raw JSON to validate authentication
|
|
73
|
+
# pylint: disable=protected-access
|
|
74
|
+
base_url = seeweb_client._Api__generate_base_url() # type: ignore
|
|
75
|
+
headers = seeweb_client._Api__generate_authentication_headers(
|
|
76
|
+
) # type: ignore
|
|
77
|
+
url = f'{base_url}/servers'
|
|
78
|
+
resp = requests.get(url, headers=headers, timeout=15)
|
|
79
|
+
resp.raise_for_status()
|
|
80
|
+
# If we get here, authentication worked even if schema mismatches
|
|
81
|
+
except Exception as e: # pylint: disable=broad-except
|
|
82
|
+
raise SeewebAuthenticationError(
|
|
83
|
+
f'Unable to authenticate with Seeweb API: {e}') from e
|
|
84
|
+
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@common.load_lazy_modules(_LAZY_MODULES)
|
|
89
|
+
def check_storage_credentials() -> bool:
|
|
90
|
+
"""Checks if the user has access credentials to Seeweb's storage service.
|
|
91
|
+
|
|
92
|
+
Mirrors compute credentials validation.
|
|
93
|
+
"""
|
|
94
|
+
return check_compute_credentials()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@common.load_lazy_modules(_LAZY_MODULES)
|
|
98
|
+
@annotations.lru_cache(scope='global', maxsize=1)
|
|
99
|
+
def client():
|
|
100
|
+
"""Returns an authenticated ecsapi.Api object."""
|
|
101
|
+
# Create authenticated client using the same credential pattern
|
|
102
|
+
key_path = pathlib.Path('~/.seeweb_cloud/seeweb_keys').expanduser()
|
|
103
|
+
if not key_path.exists():
|
|
104
|
+
raise SeewebCredentialsFileNotFound(
|
|
105
|
+
'Missing Seeweb API key file ~/.seeweb_cloud/seeweb_keys')
|
|
106
|
+
|
|
107
|
+
parser = configparser.ConfigParser()
|
|
108
|
+
parser.read(key_path)
|
|
109
|
+
try:
|
|
110
|
+
api_key = parser['DEFAULT']['api_key'].strip()
|
|
111
|
+
except KeyError as e:
|
|
112
|
+
raise SeewebApiKeyMissing(
|
|
113
|
+
'Missing api_key in ~/.seeweb_cloud/seeweb_keys') from e
|
|
114
|
+
if not api_key:
|
|
115
|
+
raise SeewebApiKeyMissing(
|
|
116
|
+
'Empty api_key in ~/.seeweb_cloud/seeweb_keys')
|
|
117
|
+
|
|
118
|
+
api = ecsapi.Api(token=api_key)
|
|
119
|
+
|
|
120
|
+
# Monkey-patch fetch_servers to be tolerant to API schema mismatches.
|
|
121
|
+
orig_fetch_servers = api.fetch_servers
|
|
122
|
+
orig_delete_server = api.delete_server
|
|
123
|
+
|
|
124
|
+
def _tolerant_fetch_servers(
|
|
125
|
+
timeout: Optional[int] = None): # type: ignore[override]
|
|
126
|
+
try:
|
|
127
|
+
return orig_fetch_servers(timeout=timeout)
|
|
128
|
+
except pydantic.ValidationError:
|
|
129
|
+
# Fallback path: fetch raw JSON, drop snapshot fields, then validate
|
|
130
|
+
# pylint: disable=protected-access
|
|
131
|
+
base_url = api._Api__generate_base_url() # type: ignore
|
|
132
|
+
headers = api._Api__generate_authentication_headers(
|
|
133
|
+
) # type: ignore
|
|
134
|
+
url = f'{base_url}/servers'
|
|
135
|
+
resp = requests.get(url, headers=headers, timeout=timeout or 15)
|
|
136
|
+
resp.raise_for_status()
|
|
137
|
+
data = resp.json()
|
|
138
|
+
try:
|
|
139
|
+
servers = data.get('server', [])
|
|
140
|
+
for s in servers:
|
|
141
|
+
s.pop('last_restored_snapshot', None)
|
|
142
|
+
group = s.get('group')
|
|
143
|
+
if isinstance(group, dict):
|
|
144
|
+
group_name = group.get('name')
|
|
145
|
+
s['group'] = group_name if isinstance(
|
|
146
|
+
group_name, str) else str(group_name)
|
|
147
|
+
except (KeyError, TypeError, ValueError):
|
|
148
|
+
pass
|
|
149
|
+
server_list_response_cls = ecsapi._server._ServerListResponse
|
|
150
|
+
servers_response = server_list_response_cls.model_validate(data)
|
|
151
|
+
return servers_response.server
|
|
152
|
+
|
|
153
|
+
api.fetch_servers = _tolerant_fetch_servers # type: ignore[assignment]
|
|
154
|
+
|
|
155
|
+
def _tolerant_delete_server(server_name: str,
|
|
156
|
+
timeout: Optional[int] = None):
|
|
157
|
+
try:
|
|
158
|
+
return orig_delete_server(server_name, timeout=timeout)
|
|
159
|
+
except pydantic.ValidationError:
|
|
160
|
+
# Fallback: perform raw DELETE and interpret not_found as success
|
|
161
|
+
# pylint: disable=protected-access
|
|
162
|
+
base_url = api._Api__generate_base_url() # type: ignore
|
|
163
|
+
headers = api._Api__generate_authentication_headers(
|
|
164
|
+
) # type: ignore
|
|
165
|
+
url = f'{base_url}/servers/{server_name}'
|
|
166
|
+
resp = requests.delete(url, headers=headers, timeout=timeout or 15)
|
|
167
|
+
# Treat 404 as idempotent success
|
|
168
|
+
if resp.status_code == 404:
|
|
169
|
+
return None
|
|
170
|
+
# Some APIs return {status: 'not_found', message: ...}
|
|
171
|
+
try:
|
|
172
|
+
data = resp.json()
|
|
173
|
+
if isinstance(data, dict) and data.get('status') == 'not_found':
|
|
174
|
+
return None
|
|
175
|
+
except (ValueError, TypeError):
|
|
176
|
+
pass
|
|
177
|
+
# If not clearly not_found, re-raise original behavior
|
|
178
|
+
resp.raise_for_status()
|
|
179
|
+
# Best-effort: return None to indicate deletion requested
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
api.delete_server = _tolerant_delete_server # type: ignore[assignment]
|
|
183
|
+
return api
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Shadeform cloud adaptor."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import socket
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from sky import sky_logging
|
|
10
|
+
from sky.provision.shadeform import shadeform_utils
|
|
11
|
+
from sky.utils import common_utils
|
|
12
|
+
|
|
13
|
+
logger = sky_logging.init_logger(__name__)
|
|
14
|
+
|
|
15
|
+
_shadeform_sdk = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def import_package(func):
|
|
19
|
+
|
|
20
|
+
@functools.wraps(func)
|
|
21
|
+
def wrapper(*args, **kwargs):
|
|
22
|
+
global _shadeform_sdk
|
|
23
|
+
if _shadeform_sdk is None:
|
|
24
|
+
try:
|
|
25
|
+
import shadeform as _shadeform # pylint: disable=import-outside-toplevel
|
|
26
|
+
_shadeform_sdk = _shadeform
|
|
27
|
+
except ImportError:
|
|
28
|
+
raise ImportError(
|
|
29
|
+
'Failed to import dependencies for Shadeform. '
|
|
30
|
+
'Try pip install "skypilot[shadeform]"') from None
|
|
31
|
+
return func(*args, **kwargs)
|
|
32
|
+
|
|
33
|
+
return wrapper
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@import_package
|
|
37
|
+
def shadeform():
|
|
38
|
+
"""Return the shadeform package."""
|
|
39
|
+
return _shadeform_sdk
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def list_ssh_keys() -> List[Dict[str, Any]]:
|
|
43
|
+
"""List all SSH keys in Shadeform account."""
|
|
44
|
+
try:
|
|
45
|
+
response = shadeform_utils.get_ssh_keys()
|
|
46
|
+
return response.get('ssh_keys', [])
|
|
47
|
+
except (ValueError, KeyError, requests.exceptions.RequestException) as e:
|
|
48
|
+
logger.warning(f'Failed to list SSH keys from Shadeform: {e}')
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def add_ssh_key_to_shadeform(public_key: str) -> Optional[str]:
|
|
53
|
+
"""Add SSH key to Shadeform if it doesn't already exist.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
public_key: The SSH public key string.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The name of the key if added successfully, None otherwise.
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
# Check if key already exists
|
|
63
|
+
existing_keys = list_ssh_keys()
|
|
64
|
+
key_exists = False
|
|
65
|
+
key_id = None
|
|
66
|
+
for key in existing_keys:
|
|
67
|
+
if key.get('public_key') == public_key:
|
|
68
|
+
key_exists = True
|
|
69
|
+
key_id = key.get('id')
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
if key_exists:
|
|
73
|
+
logger.info('SSH key already exists in Shadeform account')
|
|
74
|
+
return key_id
|
|
75
|
+
|
|
76
|
+
# Generate a unique key name
|
|
77
|
+
hostname = socket.gethostname()
|
|
78
|
+
key_name = f'skypilot-{hostname}-{common_utils.get_user_hash()[:8]}'
|
|
79
|
+
|
|
80
|
+
# Add the key
|
|
81
|
+
response = shadeform_utils.add_ssh_key(name=key_name,
|
|
82
|
+
public_key=public_key)
|
|
83
|
+
key_id = response['id']
|
|
84
|
+
logger.info(f'Added SSH key to Shadeform: {key_name, key_id}')
|
|
85
|
+
return key_id
|
|
86
|
+
|
|
87
|
+
except (ValueError, KeyError, requests.exceptions.RequestException) as e:
|
|
88
|
+
logger.warning(f'Failed to add SSH key to Shadeform: {e}')
|
|
89
|
+
return None
|