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/usage/usage_lib.py
CHANGED
|
@@ -14,6 +14,7 @@ from typing_extensions import ParamSpec
|
|
|
14
14
|
|
|
15
15
|
import sky
|
|
16
16
|
from sky import sky_logging
|
|
17
|
+
from sky import skypilot_config
|
|
17
18
|
from sky.adaptors import common as adaptors_common
|
|
18
19
|
from sky.usage import constants
|
|
19
20
|
from sky.utils import common_utils
|
|
@@ -167,6 +168,7 @@ class UsageMessageToReport(MessageToReport):
|
|
|
167
168
|
self.runtimes: Dict[str, float] = {} # update_runtime
|
|
168
169
|
self.exception: Optional[str] = None # entrypoint_context
|
|
169
170
|
self.stacktrace: Optional[str] = None # entrypoint_context
|
|
171
|
+
self.skypilot_config: Optional[Dict[str, Any]] = None
|
|
170
172
|
|
|
171
173
|
# Whether API server is deployed remotely.
|
|
172
174
|
self.using_remote_api_server: bool = (
|
|
@@ -177,6 +179,7 @@ class UsageMessageToReport(MessageToReport):
|
|
|
177
179
|
self.client_entrypoint = common_utils.get_current_client_entrypoint(
|
|
178
180
|
msg)
|
|
179
181
|
self.entrypoint = msg
|
|
182
|
+
self.skypilot_config = dict(skypilot_config.to_dict())
|
|
180
183
|
|
|
181
184
|
def set_internal(self):
|
|
182
185
|
self.internal = True
|
|
@@ -313,21 +316,30 @@ class MessageCollection:
|
|
|
313
316
|
"""A collection of messages."""
|
|
314
317
|
|
|
315
318
|
def __init__(self):
|
|
316
|
-
self._messages = {
|
|
319
|
+
self._messages: Dict[MessageType, MessageToReport] = {
|
|
317
320
|
MessageType.USAGE: UsageMessageToReport(),
|
|
318
321
|
MessageType.HEARTBEAT: HeartbeatMessageToReport()
|
|
319
322
|
}
|
|
320
323
|
|
|
321
324
|
@property
|
|
322
325
|
def usage(self) -> UsageMessageToReport:
|
|
323
|
-
|
|
326
|
+
msg = self._messages[MessageType.USAGE]
|
|
327
|
+
assert isinstance(msg, UsageMessageToReport)
|
|
328
|
+
return msg
|
|
324
329
|
|
|
325
330
|
@property
|
|
326
331
|
def heartbeat(self) -> HeartbeatMessageToReport:
|
|
327
|
-
|
|
332
|
+
msg = self._messages[MessageType.HEARTBEAT]
|
|
333
|
+
assert isinstance(msg, HeartbeatMessageToReport)
|
|
334
|
+
return msg
|
|
328
335
|
|
|
329
336
|
def reset(self, message_type: MessageType):
|
|
330
|
-
|
|
337
|
+
if message_type == MessageType.USAGE:
|
|
338
|
+
self._messages[message_type] = UsageMessageToReport()
|
|
339
|
+
elif message_type == MessageType.HEARTBEAT:
|
|
340
|
+
self._messages[message_type] = HeartbeatMessageToReport()
|
|
341
|
+
else:
|
|
342
|
+
raise ValueError(f'Unknown message type: {message_type}')
|
|
331
343
|
|
|
332
344
|
def __getitem__(self, key):
|
|
333
345
|
return self._messages[key]
|
sky/users/model.conf
CHANGED
sky/users/permission.py
CHANGED
|
@@ -3,7 +3,7 @@ import contextlib
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
|
-
from typing import Generator, List
|
|
6
|
+
from typing import Generator, List, Optional
|
|
7
7
|
|
|
8
8
|
import casbin
|
|
9
9
|
import filelock
|
|
@@ -14,6 +14,7 @@ from sky import models
|
|
|
14
14
|
from sky import sky_logging
|
|
15
15
|
from sky.skylet import constants
|
|
16
16
|
from sky.users import rbac
|
|
17
|
+
from sky.utils import annotations
|
|
17
18
|
from sky.utils import common_utils
|
|
18
19
|
from sky.utils.db import db_utils
|
|
19
20
|
|
|
@@ -27,14 +28,14 @@ logger = sky_logging.init_logger(__name__)
|
|
|
27
28
|
POLICY_UPDATE_LOCK_PATH = os.path.expanduser('~/.sky/.policy_update.lock')
|
|
28
29
|
POLICY_UPDATE_LOCK_TIMEOUT_SECONDS = 20
|
|
29
30
|
|
|
30
|
-
_enforcer_instance = None
|
|
31
|
+
_enforcer_instance: Optional['PermissionService'] = None
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class PermissionService:
|
|
34
35
|
"""Permission service for SkyPilot API Server."""
|
|
35
36
|
|
|
36
37
|
def __init__(self):
|
|
37
|
-
self.enforcer = None
|
|
38
|
+
self.enforcer: Optional[casbin.Enforcer] = None
|
|
38
39
|
|
|
39
40
|
def _lazy_initialize(self):
|
|
40
41
|
if self.enforcer is not None:
|
|
@@ -42,7 +43,6 @@ class PermissionService:
|
|
|
42
43
|
with _policy_lock():
|
|
43
44
|
global _enforcer_instance
|
|
44
45
|
if _enforcer_instance is None:
|
|
45
|
-
_enforcer_instance = self
|
|
46
46
|
engine = global_user_state.initialize_and_get_db()
|
|
47
47
|
db_utils.add_all_tables_to_db_sqlalchemy(
|
|
48
48
|
sqlalchemy_adapter.Base.metadata, engine)
|
|
@@ -52,11 +52,43 @@ class PermissionService:
|
|
|
52
52
|
'model.conf')
|
|
53
53
|
enforcer = casbin.Enforcer(model_path, adapter)
|
|
54
54
|
self.enforcer = enforcer
|
|
55
|
+
# Only set the enforcer instance once the enforcer
|
|
56
|
+
# is successfully initialized, if we change it and then fail
|
|
57
|
+
# we will set it to None and all subsequent calls will fail.
|
|
58
|
+
_enforcer_instance = self
|
|
55
59
|
self._maybe_initialize_policies()
|
|
56
60
|
self._maybe_initialize_basic_auth_user()
|
|
57
61
|
else:
|
|
62
|
+
assert _enforcer_instance is not None
|
|
58
63
|
self.enforcer = _enforcer_instance.enforcer
|
|
59
64
|
|
|
65
|
+
def _ensure_enforcer(self) -> casbin.Enforcer:
|
|
66
|
+
"""Ensure enforcer is initialized and return it."""
|
|
67
|
+
self._lazy_initialize()
|
|
68
|
+
assert self.enforcer is not None, (
|
|
69
|
+
'Enforcer should be initialized after _lazy_initialize()')
|
|
70
|
+
return self.enforcer
|
|
71
|
+
|
|
72
|
+
def _get_plugin_rbac_rules(self):
|
|
73
|
+
"""Get RBAC rules from loaded plugins.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Dictionary of plugin RBAC rules, or empty dict if plugins module
|
|
77
|
+
is not available or no rules are defined.
|
|
78
|
+
"""
|
|
79
|
+
try:
|
|
80
|
+
# pylint: disable=import-outside-toplevel
|
|
81
|
+
from sky.server import plugins as server_plugins
|
|
82
|
+
return server_plugins.get_plugin_rbac_rules()
|
|
83
|
+
except ImportError:
|
|
84
|
+
# Plugin module not available (e.g., not running as server)
|
|
85
|
+
logger.debug(
|
|
86
|
+
'Plugin module not available, skipping plugin RBAC rules')
|
|
87
|
+
return {}
|
|
88
|
+
except Exception as e: # pylint: disable=broad-except
|
|
89
|
+
logger.warning(f'Failed to get plugin RBAC rules: {e}')
|
|
90
|
+
return {}
|
|
91
|
+
|
|
60
92
|
def _maybe_initialize_basic_auth_user(self) -> None:
|
|
61
93
|
"""Initialize basic auth user if it is enabled."""
|
|
62
94
|
basic_auth = os.environ.get(constants.SKYPILOT_INITIAL_BASIC_AUTH)
|
|
@@ -72,9 +104,9 @@ class PermissionService:
|
|
|
72
104
|
return
|
|
73
105
|
global_user_state.add_or_update_user(
|
|
74
106
|
models.User(id=user_hash, name=username, password=password))
|
|
75
|
-
self.
|
|
76
|
-
|
|
77
|
-
|
|
107
|
+
enforcer = self._ensure_enforcer()
|
|
108
|
+
enforcer.add_grouping_policy(user_hash, rbac.RoleName.ADMIN.value)
|
|
109
|
+
enforcer.save_policy()
|
|
78
110
|
logger.info(f'Basic auth user {username} initialized')
|
|
79
111
|
|
|
80
112
|
def _maybe_initialize_policies(self) -> None:
|
|
@@ -86,11 +118,15 @@ class PermissionService:
|
|
|
86
118
|
|
|
87
119
|
# Check if policies are already initialized by looking for existing
|
|
88
120
|
# permission policies in the enforcer
|
|
89
|
-
|
|
121
|
+
enforcer = self._ensure_enforcer()
|
|
122
|
+
existing_policies = enforcer.get_policy()
|
|
123
|
+
|
|
124
|
+
# Get plugin RBAC rules dynamically
|
|
125
|
+
plugin_rules = self._get_plugin_rbac_rules()
|
|
90
126
|
|
|
91
127
|
# If we already have policies for the expected roles, skip
|
|
92
128
|
# initialization
|
|
93
|
-
role_permissions = rbac.get_role_permissions()
|
|
129
|
+
role_permissions = rbac.get_role_permissions(plugin_rules=plugin_rules)
|
|
94
130
|
expected_policies = []
|
|
95
131
|
for role, permissions in role_permissions.items():
|
|
96
132
|
if permissions['permissions'] and 'blocklist' in permissions[
|
|
@@ -123,7 +159,7 @@ class PermissionService:
|
|
|
123
159
|
logger.debug('Policies not found or incomplete, initializing...')
|
|
124
160
|
# Only clear p policies (permission policies),
|
|
125
161
|
# keep g policies (role policies)
|
|
126
|
-
|
|
162
|
+
enforcer.remove_filtered_policy(0)
|
|
127
163
|
for role, permissions in role_permissions.items():
|
|
128
164
|
if permissions['permissions'] and 'blocklist' in permissions[
|
|
129
165
|
'permissions']:
|
|
@@ -133,14 +169,14 @@ class PermissionService:
|
|
|
133
169
|
method = item['method']
|
|
134
170
|
logger.debug(f'Adding role policy: role={role}, '
|
|
135
171
|
f'path={path}, method={method}')
|
|
136
|
-
|
|
172
|
+
enforcer.add_policy(role, path, method)
|
|
137
173
|
policy_updated = True
|
|
138
174
|
|
|
139
175
|
for workspace_name, users in workspace_policy_permissions.items():
|
|
140
176
|
for user in users:
|
|
141
177
|
logger.debug(f'Initializing workspace policy: user={user}, '
|
|
142
178
|
f'workspace={workspace_name}')
|
|
143
|
-
|
|
179
|
+
enforcer.add_policy(user, workspace_name, '*')
|
|
144
180
|
policy_updated = True
|
|
145
181
|
logger.debug('Policies initialized successfully')
|
|
146
182
|
else:
|
|
@@ -153,7 +189,7 @@ class PermissionService:
|
|
|
153
189
|
policy_updated = policy_updated or user_added
|
|
154
190
|
|
|
155
191
|
if policy_updated:
|
|
156
|
-
|
|
192
|
+
enforcer.save_policy()
|
|
157
193
|
|
|
158
194
|
def add_user_if_not_exists(self, user_id: str) -> None:
|
|
159
195
|
"""Add user role relationship."""
|
|
@@ -167,34 +203,35 @@ class PermissionService:
|
|
|
167
203
|
Returns:
|
|
168
204
|
True if the user was added, False otherwise.
|
|
169
205
|
"""
|
|
170
|
-
|
|
206
|
+
enforcer = self._ensure_enforcer()
|
|
207
|
+
user_roles = enforcer.get_roles_for_user(user_id)
|
|
171
208
|
if not user_roles:
|
|
172
|
-
|
|
209
|
+
enforcer.add_grouping_policy(user_id, rbac.get_default_role())
|
|
173
210
|
return True
|
|
174
211
|
return False
|
|
175
212
|
|
|
176
213
|
def delete_user(self, user_id: str) -> None:
|
|
177
214
|
"""Delete user role relationship."""
|
|
178
|
-
self._lazy_initialize()
|
|
179
215
|
with _policy_lock():
|
|
180
216
|
# Get current roles
|
|
181
217
|
self._load_policy_no_lock()
|
|
182
218
|
# Avoid calling get_user_roles, as it will require the lock.
|
|
183
|
-
|
|
219
|
+
enforcer = self._ensure_enforcer()
|
|
220
|
+
current_roles = enforcer.get_roles_for_user(user_id)
|
|
184
221
|
if not current_roles:
|
|
185
222
|
logger.debug(f'User {user_id} has no roles')
|
|
186
223
|
return
|
|
187
|
-
|
|
188
|
-
|
|
224
|
+
enforcer.remove_grouping_policy(user_id, current_roles[0])
|
|
225
|
+
enforcer.save_policy()
|
|
189
226
|
|
|
190
227
|
def update_role(self, user_id: str, new_role: str) -> None:
|
|
191
228
|
"""Update user role relationship."""
|
|
192
|
-
self._lazy_initialize()
|
|
193
229
|
with _policy_lock():
|
|
194
230
|
# Get current roles
|
|
195
231
|
self._load_policy_no_lock()
|
|
196
232
|
# Avoid calling get_user_roles, as it will require the lock.
|
|
197
|
-
|
|
233
|
+
enforcer = self._ensure_enforcer()
|
|
234
|
+
current_roles = enforcer.get_roles_for_user(user_id)
|
|
198
235
|
if not current_roles:
|
|
199
236
|
logger.debug(f'User {user_id} has no roles')
|
|
200
237
|
else:
|
|
@@ -203,11 +240,11 @@ class PermissionService:
|
|
|
203
240
|
if current_role == new_role:
|
|
204
241
|
logger.debug(f'User {user_id} already has role {new_role}')
|
|
205
242
|
return
|
|
206
|
-
|
|
243
|
+
enforcer.remove_grouping_policy(user_id, current_role)
|
|
207
244
|
|
|
208
245
|
# Update user role
|
|
209
|
-
|
|
210
|
-
|
|
246
|
+
enforcer.add_grouping_policy(user_id, new_role)
|
|
247
|
+
enforcer.save_policy()
|
|
211
248
|
|
|
212
249
|
def get_user_roles(self, user_id: str) -> List[str]:
|
|
213
250
|
"""Get all roles for a user.
|
|
@@ -222,15 +259,15 @@ class PermissionService:
|
|
|
222
259
|
Returns:
|
|
223
260
|
A list of role names that the user has.
|
|
224
261
|
"""
|
|
225
|
-
self._lazy_initialize()
|
|
226
262
|
self._load_policy_no_lock()
|
|
227
|
-
|
|
263
|
+
enforcer = self._ensure_enforcer()
|
|
264
|
+
return enforcer.get_roles_for_user(user_id)
|
|
228
265
|
|
|
229
266
|
def get_users_for_role(self, role: str) -> List[str]:
|
|
230
267
|
"""Get all users for a role."""
|
|
231
|
-
self._lazy_initialize()
|
|
232
268
|
self._load_policy_no_lock()
|
|
233
|
-
|
|
269
|
+
enforcer = self._ensure_enforcer()
|
|
270
|
+
return enforcer.get_users_for_role(role)
|
|
234
271
|
|
|
235
272
|
def check_endpoint_permission(self, user_id: str, path: str,
|
|
236
273
|
method: str) -> bool:
|
|
@@ -241,19 +278,22 @@ class PermissionService:
|
|
|
241
278
|
# it is a hot path in every request. It is ok to have a stale policy,
|
|
242
279
|
# as long as it is eventually consistent.
|
|
243
280
|
# self._load_policy_no_lock()
|
|
244
|
-
self.
|
|
245
|
-
return
|
|
281
|
+
enforcer = self._ensure_enforcer()
|
|
282
|
+
return enforcer.enforce(user_id, path, method)
|
|
246
283
|
|
|
247
284
|
def _load_policy_no_lock(self):
|
|
248
285
|
"""Load policy from storage."""
|
|
249
|
-
self.
|
|
286
|
+
enforcer = self._ensure_enforcer()
|
|
287
|
+
enforcer.load_policy()
|
|
250
288
|
|
|
251
289
|
def load_policy(self):
|
|
252
290
|
"""Load policy from storage with lock."""
|
|
253
|
-
self._lazy_initialize()
|
|
254
291
|
with _policy_lock():
|
|
255
292
|
self._load_policy_no_lock()
|
|
256
293
|
|
|
294
|
+
# Right now, not a lot of users are using multiple workspaces,
|
|
295
|
+
# so 5 should be more than enough.
|
|
296
|
+
@annotations.lru_cache(scope='request', maxsize=5)
|
|
257
297
|
def check_workspace_permission(self, user_id: str,
|
|
258
298
|
workspace_name: str) -> bool:
|
|
259
299
|
"""Check workspace permission.
|
|
@@ -266,7 +306,6 @@ class PermissionService:
|
|
|
266
306
|
For public workspaces, the permission is granted via a wildcard policy
|
|
267
307
|
('*').
|
|
268
308
|
"""
|
|
269
|
-
self._lazy_initialize()
|
|
270
309
|
if os.getenv(constants.ENV_VAR_IS_SKYPILOT_SERVER) is None:
|
|
271
310
|
# When it is not on API server, we allow all users to access all
|
|
272
311
|
# workspaces, as the workspace check has been done on API server.
|
|
@@ -279,7 +318,8 @@ class PermissionService:
|
|
|
279
318
|
# r.act == p.act
|
|
280
319
|
# This means if there's a policy ('*', workspace_name, '*'), it will
|
|
281
320
|
# match any user
|
|
282
|
-
|
|
321
|
+
enforcer = self._ensure_enforcer()
|
|
322
|
+
result = enforcer.enforce(user_id, workspace_name, '*')
|
|
283
323
|
logger.debug(f'Workspace permission check: user={user_id}, '
|
|
284
324
|
f'workspace={workspace_name}, result={result}')
|
|
285
325
|
return result
|
|
@@ -323,13 +363,13 @@ class PermissionService:
|
|
|
323
363
|
For public workspaces, this should be ['*'].
|
|
324
364
|
For private workspaces, this should be specific user IDs.
|
|
325
365
|
"""
|
|
326
|
-
self._lazy_initialize()
|
|
327
366
|
with _policy_lock():
|
|
367
|
+
enforcer = self._ensure_enforcer()
|
|
328
368
|
for user in users:
|
|
329
369
|
logger.debug(f'Adding workspace policy: user={user}, '
|
|
330
370
|
f'workspace={workspace_name}')
|
|
331
|
-
|
|
332
|
-
|
|
371
|
+
enforcer.add_policy(user, workspace_name, '*')
|
|
372
|
+
enforcer.save_policy()
|
|
333
373
|
|
|
334
374
|
def update_workspace_policy(self, workspace_name: str,
|
|
335
375
|
users: List[str]) -> None:
|
|
@@ -341,24 +381,24 @@ class PermissionService:
|
|
|
341
381
|
For public workspaces, this should be ['*'].
|
|
342
382
|
For private workspaces, this should be specific user IDs.
|
|
343
383
|
"""
|
|
344
|
-
self._lazy_initialize()
|
|
345
384
|
with _policy_lock():
|
|
346
385
|
self._load_policy_no_lock()
|
|
386
|
+
enforcer = self._ensure_enforcer()
|
|
347
387
|
# Remove all existing policies for this workspace
|
|
348
|
-
|
|
388
|
+
enforcer.remove_filtered_policy(1, workspace_name)
|
|
349
389
|
# Add new policies
|
|
350
390
|
for user in users:
|
|
351
391
|
logger.debug(f'Updating workspace policy: user={user}, '
|
|
352
392
|
f'workspace={workspace_name}')
|
|
353
|
-
|
|
354
|
-
|
|
393
|
+
enforcer.add_policy(user, workspace_name, '*')
|
|
394
|
+
enforcer.save_policy()
|
|
355
395
|
|
|
356
396
|
def remove_workspace_policy(self, workspace_name: str) -> None:
|
|
357
397
|
"""Remove workspace policy."""
|
|
358
|
-
self._lazy_initialize()
|
|
359
398
|
with _policy_lock():
|
|
360
|
-
self.
|
|
361
|
-
|
|
399
|
+
enforcer = self._ensure_enforcer()
|
|
400
|
+
enforcer.remove_filtered_policy(1, workspace_name)
|
|
401
|
+
enforcer.save_policy()
|
|
362
402
|
|
|
363
403
|
|
|
364
404
|
@contextlib.contextmanager
|
sky/users/rbac.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""RBAC (Role-Based Access Control) functionality for SkyPilot API Server."""
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
|
-
from typing import Dict, List
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
from sky import sky_logging
|
|
7
7
|
from sky import skypilot_config
|
|
@@ -55,8 +55,13 @@ def get_default_role() -> str:
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def get_role_permissions(
|
|
58
|
+
plugin_rules: Optional[Dict[str, List[Dict[str, str]]]] = None
|
|
58
59
|
) -> Dict[str, Dict[str, Dict[str, List[Dict[str, str]]]]]:
|
|
59
|
-
"""Get all role permissions from config.
|
|
60
|
+
"""Get all role permissions from config and plugins.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
plugin_rules: Optional dictionary of plugin RBAC rules to merge.
|
|
64
|
+
Format: {'user': [{'path': '...', 'method': '...'}]}
|
|
60
65
|
|
|
61
66
|
Returns:
|
|
62
67
|
Dictionary containing all roles and their permissions configuration.
|
|
@@ -91,9 +96,32 @@ def get_role_permissions(
|
|
|
91
96
|
if 'user' not in config_permissions:
|
|
92
97
|
config_permissions['user'] = {
|
|
93
98
|
'permissions': {
|
|
94
|
-
'blocklist': _DEFAULT_USER_BLOCKLIST
|
|
99
|
+
'blocklist': _DEFAULT_USER_BLOCKLIST.copy()
|
|
95
100
|
}
|
|
96
101
|
}
|
|
102
|
+
|
|
103
|
+
# Merge plugin rules into the appropriate roles
|
|
104
|
+
if plugin_rules:
|
|
105
|
+
for role, rules in plugin_rules.items():
|
|
106
|
+
if role not in supported_roles:
|
|
107
|
+
logger.warning(f'Plugin specified invalid role: {role}')
|
|
108
|
+
continue
|
|
109
|
+
if role not in config_permissions:
|
|
110
|
+
config_permissions[role] = {'permissions': {'blocklist': []}}
|
|
111
|
+
if 'permissions' not in config_permissions[role]:
|
|
112
|
+
config_permissions[role]['permissions'] = {'blocklist': []}
|
|
113
|
+
if 'blocklist' not in config_permissions[role]['permissions']:
|
|
114
|
+
config_permissions[role]['permissions']['blocklist'] = []
|
|
115
|
+
|
|
116
|
+
# Merge plugin rules, avoiding duplicates
|
|
117
|
+
existing_rules = config_permissions[role]['permissions'][
|
|
118
|
+
'blocklist']
|
|
119
|
+
for rule in rules:
|
|
120
|
+
if rule not in existing_rules:
|
|
121
|
+
existing_rules.append(rule)
|
|
122
|
+
logger.debug(f'Added plugin RBAC rule for {role}: '
|
|
123
|
+
f'{rule["method"]} {rule["path"]}')
|
|
124
|
+
|
|
97
125
|
return config_permissions
|
|
98
126
|
|
|
99
127
|
|
|
@@ -3,6 +3,7 @@ import typing
|
|
|
3
3
|
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
from sky import catalog
|
|
6
|
+
from sky.catalog import common as catalog_common
|
|
6
7
|
from sky.utils import rich_utils
|
|
7
8
|
from sky.utils import ux_utils
|
|
8
9
|
|
|
@@ -34,8 +35,8 @@ if typing.TYPE_CHECKING:
|
|
|
34
35
|
|
|
35
36
|
# Use a cached version of accelerators to cloud mapping, so that we don't have
|
|
36
37
|
# to download and read the catalog file for every cloud locally.
|
|
37
|
-
_accelerator_df =
|
|
38
|
-
_memory_df =
|
|
38
|
+
_accelerator_df = catalog_common.read_catalog('common/accelerators.csv')
|
|
39
|
+
_memory_df = catalog_common.read_catalog('common/metadata.csv')
|
|
39
40
|
|
|
40
41
|
# List of non-GPU accelerators that are supported by our backend for job queue
|
|
41
42
|
# scheduling.
|
|
@@ -107,10 +108,12 @@ def canonicalize_accelerator_name(accelerator: str,
|
|
|
107
108
|
if not names and cloud_str in ['Kubernetes', None]:
|
|
108
109
|
with rich_utils.safe_status(
|
|
109
110
|
ux_utils.spinner_message('Listing accelerators on Kubernetes')):
|
|
111
|
+
# Only search for Kubernetes to reduce the lookup cost.
|
|
112
|
+
# For other clouds, the catalog has been searched in previous steps.
|
|
110
113
|
searched = catalog.list_accelerators(
|
|
111
114
|
name_filter=accelerator,
|
|
112
115
|
case_sensitive=False,
|
|
113
|
-
clouds=
|
|
116
|
+
clouds='Kubernetes',
|
|
114
117
|
)
|
|
115
118
|
names = list(searched.keys())
|
|
116
119
|
if accelerator in names:
|
sky/utils/admin_policy_utils.py
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import contextlib
|
|
3
3
|
import copy
|
|
4
4
|
import importlib
|
|
5
|
+
import typing
|
|
5
6
|
from typing import Iterator, Optional, Tuple, Union
|
|
6
|
-
import
|
|
7
|
+
from urllib import parse as urlparse
|
|
7
8
|
|
|
8
9
|
import colorama
|
|
9
10
|
|
|
@@ -13,17 +14,21 @@ from sky import exceptions
|
|
|
13
14
|
from sky import sky_logging
|
|
14
15
|
from sky import skypilot_config
|
|
15
16
|
from sky import task as task_lib
|
|
17
|
+
from sky.server.requests import request_names
|
|
16
18
|
from sky.utils import common_utils
|
|
17
19
|
from sky.utils import config_utils
|
|
18
20
|
from sky.utils import ux_utils
|
|
19
21
|
|
|
20
22
|
logger = sky_logging.init_logger(__name__)
|
|
21
23
|
|
|
24
|
+
if typing.TYPE_CHECKING:
|
|
25
|
+
from sky import models
|
|
26
|
+
|
|
22
27
|
|
|
23
28
|
def _is_url(policy_string: str) -> bool:
|
|
24
29
|
"""Check if the policy string is a URL."""
|
|
25
30
|
try:
|
|
26
|
-
parsed =
|
|
31
|
+
parsed = urlparse.urlparse(policy_string)
|
|
27
32
|
return parsed.scheme in ('http', 'https')
|
|
28
33
|
except Exception: # pylint: disable=broad-except
|
|
29
34
|
return False
|
|
@@ -73,6 +78,7 @@ def _get_policy_impl(
|
|
|
73
78
|
@contextlib.contextmanager
|
|
74
79
|
def apply_and_use_config_in_current_request(
|
|
75
80
|
entrypoint: Union['dag_lib.Dag', 'task_lib.Task'],
|
|
81
|
+
request_name: request_names.AdminPolicyRequestName,
|
|
76
82
|
request_options: Optional[admin_policy.RequestOptions] = None,
|
|
77
83
|
at_client_side: bool = False,
|
|
78
84
|
) -> Iterator['dag_lib.Dag']:
|
|
@@ -86,7 +92,8 @@ def apply_and_use_config_in_current_request(
|
|
|
86
92
|
Refer to `apply()` for more details.
|
|
87
93
|
"""
|
|
88
94
|
original_config = skypilot_config.to_dict()
|
|
89
|
-
dag, mutated_config = apply(entrypoint, request_options,
|
|
95
|
+
dag, mutated_config = apply(entrypoint, request_name, request_options,
|
|
96
|
+
at_client_side)
|
|
90
97
|
if mutated_config != original_config:
|
|
91
98
|
with skypilot_config.replace_skypilot_config(mutated_config):
|
|
92
99
|
yield dag
|
|
@@ -96,6 +103,7 @@ def apply_and_use_config_in_current_request(
|
|
|
96
103
|
|
|
97
104
|
def apply(
|
|
98
105
|
entrypoint: Union['dag_lib.Dag', 'task_lib.Task'],
|
|
106
|
+
request_name: request_names.AdminPolicyRequestName,
|
|
99
107
|
request_options: Optional[admin_policy.RequestOptions] = None,
|
|
100
108
|
at_client_side: bool = False,
|
|
101
109
|
) -> Tuple['dag_lib.Dag', config_utils.Config]:
|
|
@@ -126,9 +134,13 @@ def apply(
|
|
|
126
134
|
if policy is None:
|
|
127
135
|
return dag, skypilot_config.to_dict()
|
|
128
136
|
|
|
137
|
+
user = None
|
|
129
138
|
if at_client_side:
|
|
130
139
|
logger.info(f'Applying client admin policy: {policy}')
|
|
131
140
|
else:
|
|
141
|
+
# When being called by the server, the middleware has set the
|
|
142
|
+
# current user and this information is available at this point.
|
|
143
|
+
user = common_utils.get_current_user()
|
|
132
144
|
logger.info(f'Applying server admin policy: {policy}')
|
|
133
145
|
config = copy.deepcopy(skypilot_config.to_dict())
|
|
134
146
|
mutated_dag = dag_lib.Dag()
|
|
@@ -136,8 +148,9 @@ def apply(
|
|
|
136
148
|
|
|
137
149
|
mutated_config = None
|
|
138
150
|
for task in dag.tasks:
|
|
139
|
-
user_request = admin_policy.UserRequest(task, config,
|
|
140
|
-
at_client_side
|
|
151
|
+
user_request = admin_policy.UserRequest(task, config, request_name,
|
|
152
|
+
request_options, at_client_side,
|
|
153
|
+
user)
|
|
141
154
|
try:
|
|
142
155
|
mutated_user_request = policy.apply(user_request)
|
|
143
156
|
# Avoid duplicate exception wrapping.
|