dstack 0.0.9__py3-none-any.whl → 0.20.7__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.
- dstack/_internal/cli/commands/__init__.py +80 -0
- dstack/_internal/cli/commands/apply.py +100 -0
- dstack/_internal/cli/commands/attach.py +161 -0
- dstack/_internal/cli/commands/completion.py +22 -0
- dstack/_internal/cli/commands/delete.py +44 -0
- dstack/_internal/cli/commands/event.py +168 -0
- dstack/_internal/cli/commands/fleet.py +161 -0
- dstack/_internal/cli/commands/gateway.py +159 -0
- dstack/_internal/cli/commands/init.py +64 -0
- dstack/_internal/cli/commands/login.py +352 -0
- dstack/_internal/cli/commands/logs.py +62 -0
- dstack/_internal/cli/commands/metrics.py +153 -0
- dstack/_internal/cli/commands/offer.py +146 -0
- dstack/_internal/cli/commands/project.py +259 -0
- dstack/_internal/cli/commands/ps.py +81 -0
- dstack/_internal/cli/commands/run.py +69 -0
- dstack/_internal/cli/commands/secrets.py +92 -0
- dstack/_internal/cli/commands/server.py +96 -0
- dstack/_internal/cli/commands/stop.py +26 -0
- dstack/_internal/cli/commands/volume.py +117 -0
- dstack/_internal/cli/main.py +101 -0
- dstack/_internal/cli/models/gateways.py +16 -0
- dstack/_internal/cli/models/offers.py +47 -0
- dstack/_internal/cli/models/runs.py +16 -0
- dstack/_internal/cli/services/args.py +31 -0
- dstack/_internal/cli/services/completion.py +91 -0
- dstack/_internal/cli/services/configurators/__init__.py +86 -0
- dstack/_internal/cli/services/configurators/base.py +103 -0
- dstack/_internal/cli/services/configurators/fleet.py +475 -0
- dstack/_internal/cli/services/configurators/gateway.py +231 -0
- dstack/_internal/cli/services/configurators/run.py +882 -0
- dstack/_internal/cli/services/configurators/volume.py +222 -0
- dstack/_internal/cli/services/events.py +68 -0
- dstack/_internal/cli/services/profile.py +182 -0
- dstack/_internal/cli/services/repos.py +71 -0
- dstack/_internal/cli/services/resources.py +54 -0
- dstack/_internal/cli/utils/common.py +159 -0
- dstack/_internal/cli/utils/fleet.py +106 -0
- dstack/_internal/cli/utils/gateway.py +56 -0
- dstack/_internal/cli/utils/gpu.py +178 -0
- dstack/_internal/cli/utils/rich.py +156 -0
- dstack/_internal/cli/utils/run.py +517 -0
- dstack/_internal/cli/utils/secrets.py +25 -0
- dstack/_internal/cli/utils/updates.py +98 -0
- dstack/_internal/cli/utils/volume.py +58 -0
- dstack/_internal/compat.py +3 -0
- dstack/_internal/core/backends/amddevcloud/__init__.py +1 -0
- dstack/_internal/core/backends/amddevcloud/backend.py +16 -0
- dstack/_internal/core/backends/amddevcloud/compute.py +5 -0
- dstack/_internal/core/backends/amddevcloud/configurator.py +29 -0
- dstack/_internal/core/backends/aws/auth.py +30 -0
- dstack/_internal/core/backends/aws/backend.py +31 -0
- dstack/_internal/core/backends/aws/compute.py +1153 -0
- dstack/_internal/core/backends/aws/configurator.py +191 -0
- dstack/_internal/core/backends/aws/models.py +135 -0
- dstack/_internal/core/backends/aws/resources.py +700 -0
- dstack/_internal/core/backends/azure/auth.py +39 -0
- dstack/_internal/core/backends/azure/backend.py +21 -0
- dstack/_internal/core/backends/azure/compute.py +676 -0
- dstack/_internal/core/backends/azure/configurator.py +472 -0
- dstack/_internal/core/backends/azure/models.py +98 -0
- dstack/_internal/core/backends/azure/resources.py +116 -0
- dstack/_internal/core/backends/azure/utils.py +42 -0
- dstack/_internal/core/backends/base/backend.py +18 -0
- dstack/_internal/core/backends/base/compute.py +1101 -0
- dstack/_internal/core/backends/base/configurator.py +117 -0
- dstack/_internal/core/backends/base/models.py +24 -0
- dstack/_internal/core/backends/base/offers.py +232 -0
- dstack/_internal/core/backends/cloudrift/api_client.py +220 -0
- dstack/_internal/core/backends/cloudrift/backend.py +16 -0
- dstack/_internal/core/backends/cloudrift/compute.py +138 -0
- dstack/_internal/core/backends/cloudrift/configurator.py +72 -0
- dstack/_internal/core/backends/cloudrift/models.py +40 -0
- dstack/_internal/core/backends/configurators.py +181 -0
- dstack/_internal/core/backends/cudo/__init__.py +0 -0
- dstack/_internal/core/backends/cudo/api_client.py +111 -0
- dstack/_internal/core/backends/cudo/backend.py +16 -0
- dstack/_internal/core/backends/cudo/compute.py +174 -0
- dstack/_internal/core/backends/cudo/configurator.py +63 -0
- dstack/_internal/core/backends/cudo/models.py +37 -0
- dstack/_internal/core/backends/datacrunch/__init__.py +1 -0
- dstack/_internal/core/backends/datacrunch/backend.py +18 -0
- dstack/_internal/core/backends/datacrunch/compute.py +8 -0
- dstack/_internal/core/backends/datacrunch/configurator.py +17 -0
- dstack/_internal/core/backends/digitalocean/__init__.py +1 -0
- dstack/_internal/core/backends/digitalocean/backend.py +16 -0
- dstack/_internal/core/backends/digitalocean/compute.py +5 -0
- dstack/_internal/core/backends/digitalocean/configurator.py +31 -0
- dstack/_internal/core/backends/digitalocean_base/__init__.py +1 -0
- dstack/_internal/core/backends/digitalocean_base/api_client.py +104 -0
- dstack/_internal/core/backends/digitalocean_base/backend.py +5 -0
- dstack/_internal/core/backends/digitalocean_base/compute.py +174 -0
- dstack/_internal/core/backends/digitalocean_base/configurator.py +57 -0
- dstack/_internal/core/backends/digitalocean_base/models.py +43 -0
- dstack/_internal/core/backends/dstack/__init__.py +0 -0
- dstack/_internal/core/backends/dstack/models.py +26 -0
- dstack/_internal/core/backends/features.py +74 -0
- dstack/_internal/core/backends/gcp/__init__.py +0 -0
- dstack/_internal/core/backends/gcp/auth.py +57 -0
- dstack/_internal/core/backends/gcp/backend.py +17 -0
- dstack/_internal/core/backends/gcp/compute.py +1257 -0
- dstack/_internal/core/backends/gcp/configurator.py +206 -0
- dstack/_internal/core/backends/gcp/features/__init__.py +0 -0
- dstack/_internal/core/backends/gcp/features/tcpx.py +65 -0
- dstack/_internal/core/backends/gcp/models.py +160 -0
- dstack/_internal/core/backends/gcp/resources.py +585 -0
- dstack/_internal/core/backends/hotaisle/__init__.py +1 -0
- dstack/_internal/core/backends/hotaisle/api_client.py +101 -0
- dstack/_internal/core/backends/hotaisle/backend.py +16 -0
- dstack/_internal/core/backends/hotaisle/compute.py +188 -0
- dstack/_internal/core/backends/hotaisle/configurator.py +66 -0
- dstack/_internal/core/backends/hotaisle/models.py +45 -0
- dstack/_internal/core/backends/kubernetes/__init__.py +0 -0
- dstack/_internal/core/backends/kubernetes/backend.py +16 -0
- dstack/_internal/core/backends/kubernetes/compute.py +1077 -0
- dstack/_internal/core/backends/kubernetes/configurator.py +61 -0
- dstack/_internal/core/backends/kubernetes/models.py +71 -0
- dstack/_internal/core/backends/kubernetes/utils.py +81 -0
- dstack/_internal/core/backends/lambdalabs/__init__.py +0 -0
- dstack/_internal/core/backends/lambdalabs/api_client.py +87 -0
- dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
- dstack/_internal/core/backends/lambdalabs/compute.py +233 -0
- dstack/_internal/core/backends/lambdalabs/configurator.py +65 -0
- dstack/_internal/core/backends/lambdalabs/models.py +37 -0
- dstack/_internal/core/backends/local/__init__.py +0 -0
- dstack/_internal/core/backends/local/backend.py +14 -0
- dstack/_internal/core/backends/local/compute.py +130 -0
- dstack/_internal/core/backends/models.py +158 -0
- dstack/_internal/core/backends/nebius/__init__.py +0 -0
- dstack/_internal/core/backends/nebius/backend.py +16 -0
- dstack/_internal/core/backends/nebius/compute.py +401 -0
- dstack/_internal/core/backends/nebius/configurator.py +98 -0
- dstack/_internal/core/backends/nebius/models.py +185 -0
- dstack/_internal/core/backends/nebius/resources.py +433 -0
- dstack/_internal/core/backends/oci/__init__.py +0 -0
- dstack/_internal/core/backends/oci/auth.py +21 -0
- dstack/_internal/core/backends/oci/backend.py +16 -0
- dstack/_internal/core/backends/oci/compute.py +209 -0
- dstack/_internal/core/backends/oci/configurator.py +156 -0
- dstack/_internal/core/backends/oci/exceptions.py +15 -0
- dstack/_internal/core/backends/oci/models.py +87 -0
- dstack/_internal/core/backends/oci/region.py +86 -0
- dstack/_internal/core/backends/oci/resources.py +836 -0
- dstack/_internal/core/backends/runpod/__init__.py +0 -0
- dstack/_internal/core/backends/runpod/api_client.py +627 -0
- dstack/_internal/core/backends/runpod/backend.py +16 -0
- dstack/_internal/core/backends/runpod/compute.py +444 -0
- dstack/_internal/core/backends/runpod/configurator.py +63 -0
- dstack/_internal/core/backends/runpod/models.py +54 -0
- dstack/_internal/core/backends/template/__init__.py +0 -0
- dstack/_internal/core/backends/template/backend.py.jinja +16 -0
- dstack/_internal/core/backends/template/compute.py.jinja +95 -0
- dstack/_internal/core/backends/template/configurator.py.jinja +69 -0
- dstack/_internal/core/backends/template/models.py.jinja +62 -0
- dstack/_internal/core/backends/tensordock/models.py +40 -0
- dstack/_internal/core/backends/vastai/__init__.py +0 -0
- dstack/_internal/core/backends/vastai/api_client.py +143 -0
- dstack/_internal/core/backends/vastai/backend.py +16 -0
- dstack/_internal/core/backends/vastai/compute.py +141 -0
- dstack/_internal/core/backends/vastai/configurator.py +69 -0
- dstack/_internal/core/backends/vastai/models.py +37 -0
- dstack/_internal/core/backends/verda/__init__.py +0 -0
- dstack/_internal/core/backends/verda/backend.py +16 -0
- dstack/_internal/core/backends/verda/compute.py +266 -0
- dstack/_internal/core/backends/verda/configurator.py +73 -0
- dstack/_internal/core/backends/verda/models.py +38 -0
- dstack/_internal/core/backends/vultr/__init__.py +0 -0
- dstack/_internal/core/backends/vultr/api_client.py +116 -0
- dstack/_internal/core/backends/vultr/backend.py +16 -0
- dstack/_internal/core/backends/vultr/compute.py +167 -0
- dstack/_internal/core/backends/vultr/configurator.py +71 -0
- dstack/_internal/core/backends/vultr/models.py +34 -0
- dstack/_internal/core/compatibility/__init__.py +0 -0
- dstack/_internal/core/compatibility/events.py +13 -0
- dstack/_internal/core/compatibility/fleets.py +58 -0
- dstack/_internal/core/compatibility/gateways.py +39 -0
- dstack/_internal/core/compatibility/gpus.py +13 -0
- dstack/_internal/core/compatibility/logs.py +14 -0
- dstack/_internal/core/compatibility/runs.py +86 -0
- dstack/_internal/core/compatibility/volumes.py +37 -0
- dstack/_internal/core/consts.py +8 -0
- dstack/_internal/core/errors.py +160 -0
- dstack/_internal/core/models/__init__.py +0 -0
- dstack/_internal/core/models/auth.py +28 -0
- dstack/_internal/core/models/backends/__init__.py +0 -0
- dstack/_internal/core/models/backends/base.py +48 -0
- dstack/_internal/core/models/common.py +143 -0
- dstack/_internal/core/models/compute_groups.py +39 -0
- dstack/_internal/core/models/config.py +28 -0
- dstack/_internal/core/models/configurations.py +1123 -0
- dstack/_internal/core/models/envs.py +149 -0
- dstack/_internal/core/models/events.py +98 -0
- dstack/_internal/core/models/files.py +67 -0
- dstack/_internal/core/models/fleets.py +437 -0
- dstack/_internal/core/models/gateways.py +146 -0
- dstack/_internal/core/models/gpus.py +45 -0
- dstack/_internal/core/models/health.py +28 -0
- dstack/_internal/core/models/instances.py +346 -0
- dstack/_internal/core/models/logs.py +27 -0
- dstack/_internal/core/models/metrics.py +14 -0
- dstack/_internal/core/models/placement.py +27 -0
- dstack/_internal/core/models/profiles.py +431 -0
- dstack/_internal/core/models/projects.py +46 -0
- dstack/_internal/core/models/repos/__init__.py +34 -0
- dstack/_internal/core/models/repos/base.py +36 -0
- dstack/_internal/core/models/repos/local.py +96 -0
- dstack/_internal/core/models/repos/remote.py +341 -0
- dstack/_internal/core/models/repos/virtual.py +85 -0
- dstack/_internal/core/models/resources.py +424 -0
- dstack/_internal/core/models/routers.py +24 -0
- dstack/_internal/core/models/runs.py +618 -0
- dstack/_internal/core/models/secrets.py +16 -0
- dstack/_internal/core/models/server.py +7 -0
- dstack/_internal/core/models/services.py +76 -0
- dstack/_internal/core/models/unix.py +53 -0
- dstack/_internal/core/models/users.py +60 -0
- dstack/_internal/core/models/volumes.py +221 -0
- dstack/_internal/core/services/__init__.py +16 -0
- dstack/_internal/core/services/api_client.py +15 -0
- dstack/_internal/core/services/configs/__init__.py +116 -0
- dstack/_internal/core/services/diff.py +71 -0
- dstack/_internal/core/services/logs.py +58 -0
- dstack/_internal/core/services/profiles.py +46 -0
- dstack/_internal/core/services/repos.py +236 -0
- dstack/_internal/core/services/ssh/__init__.py +27 -0
- dstack/_internal/core/services/ssh/attach.py +241 -0
- dstack/_internal/core/services/ssh/client.py +113 -0
- dstack/_internal/core/services/ssh/key_manager.py +53 -0
- dstack/_internal/core/services/ssh/ports.py +89 -0
- dstack/_internal/core/services/ssh/tunnel.py +337 -0
- dstack/_internal/proxy/__init__.py +8 -0
- dstack/_internal/proxy/gateway/__init__.py +0 -0
- dstack/_internal/proxy/gateway/app.py +89 -0
- dstack/_internal/proxy/gateway/auth.py +26 -0
- dstack/_internal/proxy/gateway/const.py +7 -0
- dstack/_internal/proxy/gateway/deps.py +73 -0
- dstack/_internal/proxy/gateway/main.py +17 -0
- dstack/_internal/proxy/gateway/models.py +23 -0
- dstack/_internal/proxy/gateway/repo/__init__.py +0 -0
- dstack/_internal/proxy/gateway/repo/repo.py +121 -0
- dstack/_internal/proxy/gateway/repo/state_v1.py +164 -0
- dstack/_internal/proxy/gateway/resources/nginx/00-log-format.conf +11 -0
- dstack/_internal/proxy/gateway/resources/nginx/entrypoint.jinja2 +27 -0
- dstack/_internal/proxy/gateway/resources/nginx/router_workers.jinja2 +23 -0
- dstack/_internal/proxy/gateway/resources/nginx/service.jinja2 +105 -0
- dstack/_internal/proxy/gateway/routers/__init__.py +0 -0
- dstack/_internal/proxy/gateway/routers/auth.py +10 -0
- dstack/_internal/proxy/gateway/routers/config.py +28 -0
- dstack/_internal/proxy/gateway/routers/registry.py +124 -0
- dstack/_internal/proxy/gateway/routers/stats.py +18 -0
- dstack/_internal/proxy/gateway/schemas/__init__.py +0 -0
- dstack/_internal/proxy/gateway/schemas/common.py +5 -0
- dstack/_internal/proxy/gateway/schemas/config.py +9 -0
- dstack/_internal/proxy/gateway/schemas/registry.py +63 -0
- dstack/_internal/proxy/gateway/schemas/stats.py +15 -0
- dstack/_internal/proxy/gateway/services/__init__.py +0 -0
- dstack/_internal/proxy/gateway/services/model_routers/__init__.py +18 -0
- dstack/_internal/proxy/gateway/services/model_routers/base.py +91 -0
- dstack/_internal/proxy/gateway/services/model_routers/sglang.py +269 -0
- dstack/_internal/proxy/gateway/services/nginx.py +455 -0
- dstack/_internal/proxy/gateway/services/registry.py +426 -0
- dstack/_internal/proxy/gateway/services/server_client.py +95 -0
- dstack/_internal/proxy/gateway/services/stats.py +170 -0
- dstack/_internal/proxy/gateway/testing/__init__.py +0 -0
- dstack/_internal/proxy/gateway/testing/common.py +13 -0
- dstack/_internal/proxy/lib/__init__.py +0 -0
- dstack/_internal/proxy/lib/auth.py +7 -0
- dstack/_internal/proxy/lib/deps.py +106 -0
- dstack/_internal/proxy/lib/errors.py +14 -0
- dstack/_internal/proxy/lib/models.py +112 -0
- dstack/_internal/proxy/lib/repo.py +27 -0
- dstack/_internal/proxy/lib/routers/__init__.py +0 -0
- dstack/_internal/proxy/lib/routers/model_proxy.py +102 -0
- dstack/_internal/proxy/lib/schemas/__init__.py +0 -0
- dstack/_internal/proxy/lib/schemas/model_proxy.py +77 -0
- dstack/_internal/proxy/lib/services/__init__.py +0 -0
- dstack/_internal/proxy/lib/services/model_proxy/__init__.py +0 -0
- dstack/_internal/proxy/lib/services/model_proxy/clients/__init__.py +0 -0
- dstack/_internal/proxy/lib/services/model_proxy/clients/base.py +18 -0
- dstack/_internal/proxy/lib/services/model_proxy/clients/openai.py +67 -0
- dstack/_internal/proxy/lib/services/model_proxy/clients/tgi.py +208 -0
- dstack/_internal/proxy/lib/services/model_proxy/model_proxy.py +23 -0
- dstack/_internal/proxy/lib/services/service_connection.py +160 -0
- dstack/_internal/proxy/lib/testing/__init__.py +0 -0
- dstack/_internal/proxy/lib/testing/auth.py +11 -0
- dstack/_internal/proxy/lib/testing/common.py +51 -0
- dstack/_internal/server/__init__.py +0 -0
- dstack/_internal/server/alembic.ini +100 -0
- dstack/_internal/server/app.py +432 -0
- dstack/_internal/server/background/__init__.py +142 -0
- dstack/_internal/server/background/tasks/__init__.py +0 -0
- dstack/_internal/server/background/tasks/common.py +24 -0
- dstack/_internal/server/background/tasks/process_compute_groups.py +167 -0
- dstack/_internal/server/background/tasks/process_events.py +17 -0
- dstack/_internal/server/background/tasks/process_fleets.py +289 -0
- dstack/_internal/server/background/tasks/process_gateways.py +188 -0
- dstack/_internal/server/background/tasks/process_idle_volumes.py +145 -0
- dstack/_internal/server/background/tasks/process_instances.py +1186 -0
- dstack/_internal/server/background/tasks/process_metrics.py +172 -0
- dstack/_internal/server/background/tasks/process_placement_groups.py +104 -0
- dstack/_internal/server/background/tasks/process_probes.py +164 -0
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +150 -0
- dstack/_internal/server/background/tasks/process_running_jobs.py +1238 -0
- dstack/_internal/server/background/tasks/process_runs.py +842 -0
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +1106 -0
- dstack/_internal/server/background/tasks/process_terminating_jobs.py +108 -0
- dstack/_internal/server/background/tasks/process_volumes.py +129 -0
- dstack/_internal/server/compatibility/__init__.py +0 -0
- dstack/_internal/server/compatibility/common.py +20 -0
- dstack/_internal/server/compatibility/gpus.py +22 -0
- dstack/_internal/server/db.py +127 -0
- dstack/_internal/server/deps.py +19 -0
- dstack/_internal/server/main.py +4 -0
- dstack/_internal/server/migrations/__init__.py +0 -0
- dstack/_internal/server/migrations/env.py +112 -0
- dstack/_internal/server/migrations/script.py.mako +28 -0
- dstack/_internal/server/migrations/versions/006512f572b4_add_projects_original_name.py +38 -0
- dstack/_internal/server/migrations/versions/065588ec72b8_add_vultr_to_backendtype_enum.py +81 -0
- dstack/_internal/server/migrations/versions/06e977bc61c7_add_usermodel_deleted_and_original_name.py +45 -0
- dstack/_internal/server/migrations/versions/0e33559e16ed_update_instancestatus.py +64 -0
- dstack/_internal/server/migrations/versions/112753bc17dd_remove_nullable_fields.py +50 -0
- dstack/_internal/server/migrations/versions/1338b788b612_reverse_job_instance_relationship.py +71 -0
- dstack/_internal/server/migrations/versions/14f2cb002fc2_add_jobmodel_removed_flag.py +44 -0
- dstack/_internal/server/migrations/versions/1a48dfe44a40_rework_termination_handling.py +42 -0
- dstack/_internal/server/migrations/versions/1aa9638ad963_added_email_index.py +31 -0
- dstack/_internal/server/migrations/versions/1e3fb39ef74b_add_remote_connection_details.py +26 -0
- dstack/_internal/server/migrations/versions/1e76fb0dde87_add_jobmodel_inactivity_secs.py +32 -0
- dstack/_internal/server/migrations/versions/20166748b60c_add_jobmodel_disconnected_at.py +100 -0
- dstack/_internal/server/migrations/versions/22d74df9897e_add_events_and_event_targets.py +99 -0
- dstack/_internal/server/migrations/versions/23e01c56279a_make_blob_nullable.py +32 -0
- dstack/_internal/server/migrations/versions/2498ab323443_add_fleetmodel_consolidation_attempt_.py +44 -0
- dstack/_internal/server/migrations/versions/252d3743b641_.py +40 -0
- dstack/_internal/server/migrations/versions/25479f540245_add_probes.py +43 -0
- dstack/_internal/server/migrations/versions/27d3e55759fa_add_pools.py +152 -0
- dstack/_internal/server/migrations/versions/29826f417010_remove_instancemodel_retry_policy.py +34 -0
- dstack/_internal/server/migrations/versions/29c08c6a8cb3_.py +36 -0
- dstack/_internal/server/migrations/versions/35e90e1b0d3e_add_rolling_deployment_fields.py +42 -0
- dstack/_internal/server/migrations/versions/35f732ee4cf5_add_projectmodel_is_public.py +39 -0
- dstack/_internal/server/migrations/versions/3cf77fb8bcf1_store_repo_clone_url.py +85 -0
- dstack/_internal/server/migrations/versions/3d7f6c2ec000_add_jobmodel_registered.py +28 -0
- dstack/_internal/server/migrations/versions/3dbdce90d0e0_fix_code_uq_constraint.py +33 -0
- dstack/_internal/server/migrations/versions/48ad3ecbaea2_do_not_delete_projects_and_runs.py +46 -0
- dstack/_internal/server/migrations/versions/4ae1a5b0e7f1_add_run_list_index.py +34 -0
- dstack/_internal/server/migrations/versions/4b4319398164_introduce_runs_processing.py +144 -0
- dstack/_internal/server/migrations/versions/50dd7ea98639_index_status_columns.py +55 -0
- dstack/_internal/server/migrations/versions/51d45659d574_add_instancemodel_blocks_fields.py +43 -0
- dstack/_internal/server/migrations/versions/54a77e19c64c_add_manager_project_role.py +67 -0
- dstack/_internal/server/migrations/versions/555138b1f77f_change_instancemodel_for_asynchronous_.py +61 -0
- dstack/_internal/server/migrations/versions/58aa5162dcc3_add_gatewaymodel_configuration.py +32 -0
- dstack/_internal/server/migrations/versions/5ad8debc8fe6_fixes_for_psql.py +329 -0
- dstack/_internal/server/migrations/versions/5ec538b70e71_replace_instansestatus.py +31 -0
- dstack/_internal/server/migrations/versions/5f1707c525d2_add_filearchivemodel.py +39 -0
- dstack/_internal/server/migrations/versions/5fd659afca82_add_ix_instances_fleet_id.py +31 -0
- dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
- dstack/_internal/server/migrations/versions/63c3f19cb184_add_jobterminationreason_inactivity_.py +83 -0
- dstack/_internal/server/migrations/versions/644b8a114187_add_secretmodel.py +49 -0
- dstack/_internal/server/migrations/versions/686fb8341ea5_add_user_emails.py +32 -0
- dstack/_internal/server/migrations/versions/6c1a9d6530ee_add_jobmodel_exit_status.py +26 -0
- dstack/_internal/server/migrations/versions/706e0acc3a7d_add_runmodel_desired_replica_counts.py +26 -0
- dstack/_internal/server/migrations/versions/710e5b3fac8f_add_encryption.py +54 -0
- dstack/_internal/server/migrations/versions/728b1488b1b4_add_instance_health.py +50 -0
- dstack/_internal/server/migrations/versions/74a1f55209bd_store_enums_as_strings.py +484 -0
- dstack/_internal/server/migrations/versions/7b24b1c8eba7_add_instancemodel_last_processed_at.py +68 -0
- dstack/_internal/server/migrations/versions/7ba3b59d7ca6_add_runmodel_resubmission_attempt.py +35 -0
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/7d1ec2b920ac_add_computegroupmodel.py +91 -0
- dstack/_internal/server/migrations/versions/803c7e9ed85d_add_jobmodel_job_runtime_data.py +32 -0
- dstack/_internal/server/migrations/versions/82b32a135ea2_.py +58 -0
- dstack/_internal/server/migrations/versions/866ec1d67184_replace_retrypolicy_limit_with_.py +93 -0
- dstack/_internal/server/migrations/versions/903c91e24634_add_instances_termination_reason_message.py +34 -0
- dstack/_internal/server/migrations/versions/91a12fff6c76_add_repocredsmodel.py +43 -0
- dstack/_internal/server/migrations/versions/91ac5e543037_extend_repos_creds_column.py +36 -0
- dstack/_internal/server/migrations/versions/98cd9c8b5927_add_volumemodel.py +73 -0
- dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
- dstack/_internal/server/migrations/versions/99b4c8c954ea_add_termination_reason_message.py +71 -0
- dstack/_internal/server/migrations/versions/9eea6af28e10_added_fail_reason_for_instancemodel.py +36 -0
- dstack/_internal/server/migrations/versions/__init__.py +0 -0
- dstack/_internal/server/migrations/versions/a060e2440936_.py +206 -0
- dstack/_internal/server/migrations/versions/a751ef183f27_move_attachment_data_to_volumes_.py +34 -0
- dstack/_internal/server/migrations/versions/a7b46c073fa1_add_placementgroupmodel.py +58 -0
- dstack/_internal/server/migrations/versions/afbc600ff2b2_add_created_at_to_usermodel_and_.py +102 -0
- dstack/_internal/server/migrations/versions/b4d6ad60db08_add_instancemodel_unreachable.py +37 -0
- dstack/_internal/server/migrations/versions/b88d55c2a07d_replace_instancestatus_ready.py +21 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/migrations/versions/bca2fdf130bf_add_runmodel_priority.py +34 -0
- dstack/_internal/server/migrations/versions/bfba43f6def2_.py +32 -0
- dstack/_internal/server/migrations/versions/c00090eaef21_support_fleets.py +108 -0
- dstack/_internal/server/migrations/versions/c154eece89da_add_fields_for_async_gateway_creation.py +74 -0
- dstack/_internal/server/migrations/versions/c20626d03cfb_add_jobmetricspoint.py +43 -0
- dstack/_internal/server/migrations/versions/c48df7985d57_add_instance_termination_retries.py +38 -0
- dstack/_internal/server/migrations/versions/c83d45f9a971_replace_string_with_text.py +150 -0
- dstack/_internal/server/migrations/versions/d0bb68e48b9f_add_project_owners_and_quotas.py +106 -0
- dstack/_internal/server/migrations/versions/d3e8af4786fa_gateway_compute_flag_deleted.py +34 -0
- dstack/_internal/server/migrations/versions/d4d9dc26cf58_add_ix_jobs_run_id.py +31 -0
- dstack/_internal/server/migrations/versions/d5863798bf41_add_volumemodel_last_job_processed_at.py +40 -0
- dstack/_internal/server/migrations/versions/d6b11105f659_add_usermodel_active.py +36 -0
- dstack/_internal/server/migrations/versions/da574e93fee0_add_jobmodel_volumes_detached_at.py +40 -0
- dstack/_internal/server/migrations/versions/dfffd6a1165c_add_fields_for_gateways_behind_alb.py +36 -0
- dstack/_internal/server/migrations/versions/e2d08cd1b8d9_add_jobmodel_fleet.py +41 -0
- dstack/_internal/server/migrations/versions/e3b7db07727f_add_gatewaycomputemodel_app_updated_at.py +61 -0
- dstack/_internal/server/migrations/versions/e6391ca6a264_separate_gateways_from_compute.py +72 -0
- dstack/_internal/server/migrations/versions/ea60480f82bb_add_membermodel_member_num.py +32 -0
- dstack/_internal/server/migrations/versions/ec02a26a256c_add_runmodel_next_triggered_at.py +38 -0
- dstack/_internal/server/migrations/versions/ed0ca30e13bb_migrate_instancestatus_provisioning.py +29 -0
- dstack/_internal/server/migrations/versions/fe72c4de8376_add_gateways.py +81 -0
- dstack/_internal/server/migrations/versions/ff1d94f65b08_user_ssh_key.py +34 -0
- dstack/_internal/server/migrations/versions/ffa99edd1988_add_jobterminationreason_max_duration_.py +81 -0
- dstack/_internal/server/models.py +930 -0
- dstack/_internal/server/routers/__init__.py +0 -0
- dstack/_internal/server/routers/auth.py +34 -0
- dstack/_internal/server/routers/backends.py +142 -0
- dstack/_internal/server/routers/events.py +60 -0
- dstack/_internal/server/routers/files.py +68 -0
- dstack/_internal/server/routers/fleets.py +202 -0
- dstack/_internal/server/routers/gateways.py +109 -0
- dstack/_internal/server/routers/gpus.py +32 -0
- dstack/_internal/server/routers/instances.py +77 -0
- dstack/_internal/server/routers/logs.py +34 -0
- dstack/_internal/server/routers/metrics.py +82 -0
- dstack/_internal/server/routers/projects.py +205 -0
- dstack/_internal/server/routers/prometheus.py +35 -0
- dstack/_internal/server/routers/repos.py +118 -0
- dstack/_internal/server/routers/runs.py +216 -0
- dstack/_internal/server/routers/secrets.py +86 -0
- dstack/_internal/server/routers/server.py +19 -0
- dstack/_internal/server/routers/users.py +158 -0
- dstack/_internal/server/routers/volumes.py +122 -0
- dstack/_internal/server/schemas/__init__.py +0 -0
- dstack/_internal/server/schemas/auth.py +83 -0
- dstack/_internal/server/schemas/backends.py +16 -0
- dstack/_internal/server/schemas/common.py +9 -0
- dstack/_internal/server/schemas/events.py +211 -0
- dstack/_internal/server/schemas/files.py +5 -0
- dstack/_internal/server/schemas/fleets.py +49 -0
- dstack/_internal/server/schemas/gateways.py +31 -0
- dstack/_internal/server/schemas/gpus.py +26 -0
- dstack/_internal/server/schemas/health/__init__.py +0 -0
- dstack/_internal/server/schemas/health/dcgm.py +56 -0
- dstack/_internal/server/schemas/instances.py +47 -0
- dstack/_internal/server/schemas/logs.py +17 -0
- dstack/_internal/server/schemas/projects.py +81 -0
- dstack/_internal/server/schemas/repos.py +24 -0
- dstack/_internal/server/schemas/runner.py +269 -0
- dstack/_internal/server/schemas/runs.py +66 -0
- dstack/_internal/server/schemas/secrets.py +16 -0
- dstack/_internal/server/schemas/users.py +72 -0
- dstack/_internal/server/schemas/volumes.py +29 -0
- dstack/_internal/server/security/__init__.py +0 -0
- dstack/_internal/server/security/permissions.py +251 -0
- dstack/_internal/server/services/__init__.py +0 -0
- dstack/_internal/server/services/auth.py +77 -0
- dstack/_internal/server/services/backends/__init__.py +404 -0
- dstack/_internal/server/services/backends/handlers.py +105 -0
- dstack/_internal/server/services/compute_groups.py +22 -0
- dstack/_internal/server/services/config.py +279 -0
- dstack/_internal/server/services/docker.py +162 -0
- dstack/_internal/server/services/encryption/__init__.py +102 -0
- dstack/_internal/server/services/encryption/keys/__init__.py +0 -0
- dstack/_internal/server/services/encryption/keys/aes.py +68 -0
- dstack/_internal/server/services/encryption/keys/base.py +19 -0
- dstack/_internal/server/services/encryption/keys/identity.py +28 -0
- dstack/_internal/server/services/events.py +477 -0
- dstack/_internal/server/services/files.py +91 -0
- dstack/_internal/server/services/fleets.py +1224 -0
- dstack/_internal/server/services/gateways/__init__.py +686 -0
- dstack/_internal/server/services/gateways/client.py +209 -0
- dstack/_internal/server/services/gateways/connection.py +139 -0
- dstack/_internal/server/services/gateways/pool.py +58 -0
- dstack/_internal/server/services/gpus.py +387 -0
- dstack/_internal/server/services/instances.py +731 -0
- dstack/_internal/server/services/jobs/__init__.py +840 -0
- dstack/_internal/server/services/jobs/configurators/__init__.py +0 -0
- dstack/_internal/server/services/jobs/configurators/base.py +469 -0
- dstack/_internal/server/services/jobs/configurators/dev.py +69 -0
- dstack/_internal/server/services/jobs/configurators/extensions/__init__.py +0 -0
- dstack/_internal/server/services/jobs/configurators/extensions/base.py +15 -0
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
- dstack/_internal/server/services/jobs/configurators/extensions/vscode.py +42 -0
- dstack/_internal/server/services/jobs/configurators/extensions/windsurf.py +43 -0
- dstack/_internal/server/services/jobs/configurators/service.py +28 -0
- dstack/_internal/server/services/jobs/configurators/task.py +39 -0
- dstack/_internal/server/services/locking.py +187 -0
- dstack/_internal/server/services/logging.py +29 -0
- dstack/_internal/server/services/logs/__init__.py +122 -0
- dstack/_internal/server/services/logs/aws.py +373 -0
- dstack/_internal/server/services/logs/base.py +47 -0
- dstack/_internal/server/services/logs/filelog.py +261 -0
- dstack/_internal/server/services/logs/fluentbit.py +329 -0
- dstack/_internal/server/services/logs/gcp.py +181 -0
- dstack/_internal/server/services/metrics.py +172 -0
- dstack/_internal/server/services/offers.py +249 -0
- dstack/_internal/server/services/permissions.py +37 -0
- dstack/_internal/server/services/placement.py +234 -0
- dstack/_internal/server/services/plugins.py +109 -0
- dstack/_internal/server/services/probes.py +10 -0
- dstack/_internal/server/services/projects.py +835 -0
- dstack/_internal/server/services/prometheus/__init__.py +0 -0
- dstack/_internal/server/services/prometheus/client_metrics.py +55 -0
- dstack/_internal/server/services/prometheus/custom_metrics.py +327 -0
- dstack/_internal/server/services/proxy/__init__.py +3 -0
- dstack/_internal/server/services/proxy/auth.py +12 -0
- dstack/_internal/server/services/proxy/deps.py +18 -0
- dstack/_internal/server/services/proxy/repo.py +189 -0
- dstack/_internal/server/services/proxy/routers/__init__.py +0 -0
- dstack/_internal/server/services/proxy/routers/service_proxy.py +49 -0
- dstack/_internal/server/services/proxy/services/__init__.py +0 -0
- dstack/_internal/server/services/proxy/services/service_proxy.py +135 -0
- dstack/_internal/server/services/repos.py +362 -0
- dstack/_internal/server/services/requirements/__init__.py +0 -0
- dstack/_internal/server/services/requirements/combine.py +260 -0
- dstack/_internal/server/services/resources.py +21 -0
- dstack/_internal/server/services/runner/__init__.py +0 -0
- dstack/_internal/server/services/runner/client.py +646 -0
- dstack/_internal/server/services/runner/ssh.py +128 -0
- dstack/_internal/server/services/runs/__init__.py +1026 -0
- dstack/_internal/server/services/runs/plan.py +703 -0
- dstack/_internal/server/services/runs/replicas.py +317 -0
- dstack/_internal/server/services/runs/spec.py +191 -0
- dstack/_internal/server/services/secrets.py +245 -0
- dstack/_internal/server/services/services/__init__.py +345 -0
- dstack/_internal/server/services/services/autoscalers.py +140 -0
- dstack/_internal/server/services/services/options.py +53 -0
- dstack/_internal/server/services/ssh.py +67 -0
- dstack/_internal/server/services/storage/__init__.py +37 -0
- dstack/_internal/server/services/storage/base.py +48 -0
- dstack/_internal/server/services/storage/gcs.py +66 -0
- dstack/_internal/server/services/storage/s3.py +69 -0
- dstack/_internal/server/services/users.py +461 -0
- dstack/_internal/server/services/volumes.py +496 -0
- dstack/_internal/server/settings.py +161 -0
- dstack/_internal/server/statics/00a6e1fb461ed2929fb9.png +0 -0
- dstack/_internal/server/statics/0cae4d9f0a36034984a7.png +0 -0
- dstack/_internal/server/statics/391de232cc0e30cae513.png +0 -0
- dstack/_internal/server/statics/4e0eead8c1a73689ef9d.svg +1 -0
- dstack/_internal/server/statics/544afa2f63428c2235b0.png +0 -0
- dstack/_internal/server/statics/54a4f50f74c6b9381530.svg +7 -0
- dstack/_internal/server/statics/68dd1360a7d2611e0132.svg +4 -0
- dstack/_internal/server/statics/69544b4c81973b54a66f.png +0 -0
- dstack/_internal/server/statics/77a8b02b17af19e39266.png +0 -0
- dstack/_internal/server/statics/83a93a8871c219104367.svg +9 -0
- dstack/_internal/server/statics/8f28bb8e9999e5e6a48b.svg +4 -0
- dstack/_internal/server/statics/9124086961ab8c366bc4.svg +9 -0
- dstack/_internal/server/statics/9a9ebaeb54b025dbac0a.svg +5 -0
- dstack/_internal/server/statics/a3428392dc534f3b15c4.svg +7 -0
- dstack/_internal/server/statics/ae22625574d69361f72c.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-144x144.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-192x192.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-256x256.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-36x36.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-384x384.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-48x48.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-512x512.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-72x72.png +0 -0
- dstack/_internal/server/statics/assets/android-chrome-96x96.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-1024x1024.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-114x114.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-120x120.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-144x144.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-152x152.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-167x167.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-180x180.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-57x57.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-60x60.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-72x72.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-76x76.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon-precomposed.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-icon.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1125x2436.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1136x640.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1170x2532.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1179x2556.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1242x2208.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1242x2688.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1284x2778.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1290x2796.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1334x750.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1488x2266.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1536x2048.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1620x2160.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1640x2160.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1668x2224.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1668x2388.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-1792x828.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2048x1536.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2048x2732.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2160x1620.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2160x1640.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2208x1242.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2224x1668.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2266x1488.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2388x1668.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2436x1125.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2532x1170.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2556x1179.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2688x1242.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2732x2048.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2778x1284.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-2796x1290.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-640x1136.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-750x1334.png +0 -0
- dstack/_internal/server/statics/assets/apple-touch-startup-image-828x1792.png +0 -0
- dstack/_internal/server/statics/assets/browserconfig.xml +12 -0
- dstack/_internal/server/statics/assets/favicon-16x16.png +0 -0
- dstack/_internal/server/statics/assets/favicon-32x32.png +0 -0
- dstack/_internal/server/statics/assets/favicon-48x48.png +0 -0
- dstack/_internal/server/statics/assets/favicon.ico +0 -0
- dstack/{dashboard/statics/assets/manifest.json → _internal/server/statics/assets/manifest.webmanifest} +18 -9
- dstack/_internal/server/statics/assets/mstile-144x144.png +0 -0
- dstack/_internal/server/statics/assets/mstile-150x150.png +0 -0
- dstack/_internal/server/statics/assets/mstile-310x150.png +0 -0
- dstack/_internal/server/statics/assets/mstile-310x310.png +0 -0
- dstack/_internal/server/statics/assets/mstile-70x70.png +0 -0
- dstack/_internal/server/statics/assets/yandex-browser-50x50.png +0 -0
- dstack/_internal/server/statics/b7ae68f44193474fc578.png +0 -0
- dstack/_internal/server/statics/d2f008c75b2b5b191f3f.png +0 -0
- dstack/_internal/server/statics/d44c33e1b92e05c379fd.png +0 -0
- dstack/_internal/server/statics/dd43ff0552815179d7ab.png +0 -0
- dstack/_internal/server/statics/dd4e7166c0b9aac197d7.png +0 -0
- dstack/_internal/server/statics/e30b27916930d43d2271.png +0 -0
- dstack/_internal/server/statics/e467d7d60aae81ab198b.svg +6 -0
- dstack/_internal/server/statics/eb9b344b73818fe2b71a.png +0 -0
- dstack/_internal/server/statics/f517dd626eb964120de0.png +0 -0
- dstack/_internal/server/statics/f958aecddee5d8e3222c.png +0 -0
- dstack/_internal/server/statics/index.html +3 -0
- dstack/_internal/server/statics/logo-notext.svg +116 -0
- dstack/_internal/server/statics/main-2e6967bad9f29395eea6.css +3 -0
- dstack/_internal/server/statics/main-7dc0f6d20b8b41659acc.js +155547 -0
- dstack/_internal/server/statics/main-7dc0f6d20b8b41659acc.js.map +1 -0
- dstack/{dashboard → _internal/server}/statics/manifest.json +2 -2
- dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
- dstack/_internal/server/statics/static/media/google.b194b06fafd0a52aeb566922160ea514.svg +1 -0
- dstack/{dashboard/statics/static/media/logo.f9d7170678f68f796e270698633770ec.svg → _internal/server/statics/static/media/logo.f602feeb138844eda97c8cb641461448.svg} +8 -6
- dstack/_internal/server/statics/static/media/okta.12f178e6873a1100965f2a4dbd18fcec.svg +2 -0
- dstack/_internal/server/statics/static/media/theme.3994c817bb7dda191c1c9640dee0bf42.svg +3 -0
- dstack/_internal/server/testing/__init__.py +0 -0
- dstack/_internal/server/testing/common.py +1220 -0
- dstack/_internal/server/testing/conf.py +53 -0
- dstack/_internal/server/testing/matchers.py +31 -0
- dstack/_internal/server/utils/__init__.py +0 -0
- dstack/_internal/server/utils/common.py +55 -0
- dstack/_internal/server/utils/logging.py +51 -0
- dstack/_internal/server/utils/provisioning.py +368 -0
- dstack/_internal/server/utils/routers.py +166 -0
- dstack/_internal/server/utils/sentry_utils.py +24 -0
- dstack/_internal/settings.py +49 -0
- dstack/_internal/utils/__init__.py +0 -0
- dstack/_internal/utils/common.py +318 -0
- dstack/_internal/utils/cron.py +5 -0
- dstack/_internal/utils/crypto.py +40 -0
- dstack/_internal/utils/env.py +88 -0
- dstack/_internal/utils/event_loop.py +30 -0
- dstack/_internal/utils/files.py +69 -0
- dstack/_internal/utils/gpu.py +59 -0
- dstack/_internal/utils/hash.py +31 -0
- dstack/_internal/utils/interpolator.py +91 -0
- dstack/_internal/utils/json_schema.py +11 -0
- dstack/_internal/utils/json_utils.py +54 -0
- dstack/_internal/utils/logging.py +5 -0
- dstack/_internal/utils/nested_list.py +47 -0
- dstack/_internal/utils/network.py +50 -0
- dstack/_internal/utils/path.py +57 -0
- dstack/_internal/utils/random_names.py +258 -0
- dstack/_internal/utils/ssh.py +346 -0
- dstack/_internal/utils/tags.py +42 -0
- dstack/_internal/utils/typing.py +14 -0
- dstack/_internal/utils/version.py +22 -0
- dstack/api/__init__.py +46 -0
- dstack/api/_public/__init__.py +96 -0
- dstack/api/_public/backends.py +42 -0
- dstack/api/_public/common.py +5 -0
- dstack/api/_public/repos.py +202 -0
- dstack/api/_public/runs.py +714 -0
- dstack/api/server/__init__.py +206 -0
- dstack/api/server/_auth.py +30 -0
- dstack/api/server/_backends.py +38 -0
- dstack/api/server/_events.py +64 -0
- dstack/api/server/_files.py +18 -0
- dstack/api/server/_fleets.py +82 -0
- dstack/api/server/_gateways.py +54 -0
- dstack/api/server/_gpus.py +27 -0
- dstack/api/server/_group.py +22 -0
- dstack/api/server/_logs.py +15 -0
- dstack/api/server/_metrics.py +23 -0
- dstack/api/server/_projects.py +124 -0
- dstack/api/server/_repos.py +64 -0
- dstack/api/server/_runs.py +102 -0
- dstack/api/server/_secrets.py +36 -0
- dstack/api/server/_users.py +82 -0
- dstack/api/server/_volumes.py +39 -0
- dstack/api/server/utils.py +34 -0
- dstack/api/utils.py +105 -0
- dstack/core/__init__.py +0 -0
- dstack/plugins/__init__.py +8 -0
- dstack/plugins/_base.py +72 -0
- dstack/plugins/_models.py +8 -0
- dstack/plugins/_utils.py +19 -0
- dstack/plugins/builtin/__init__.py +0 -0
- dstack/plugins/builtin/rest_plugin/__init__.py +18 -0
- dstack/plugins/builtin/rest_plugin/_models.py +48 -0
- dstack/plugins/builtin/rest_plugin/_plugin.py +147 -0
- dstack/version.py +3 -1
- dstack-0.20.7.dist-info/METADATA +519 -0
- dstack-0.20.7.dist-info/RECORD +720 -0
- {dstack-0.0.9.dist-info → dstack-0.20.7.dist-info}/WHEEL +1 -2
- dstack-0.20.7.dist-info/entry_points.txt +2 -0
- dstack-0.20.7.dist-info/licenses/LICENSE.md +353 -0
- dstack/aws/__init__.py +0 -180
- dstack/aws/artifacts.py +0 -111
- dstack/aws/config.py +0 -40
- dstack/aws/jobs.py +0 -245
- dstack/aws/logs.py +0 -186
- dstack/aws/repos.py +0 -137
- dstack/aws/run_names.py +0 -17
- dstack/aws/runners.py +0 -693
- dstack/aws/runs.py +0 -79
- dstack/aws/secrets.py +0 -99
- dstack/aws/tags.py +0 -138
- dstack/backend.py +0 -299
- dstack/cli/app.py +0 -41
- dstack/cli/artifacts.py +0 -87
- dstack/cli/common.py +0 -57
- dstack/cli/config.py +0 -194
- dstack/cli/dashboard.py +0 -26
- dstack/cli/delete.py +0 -49
- dstack/cli/init.py +0 -33
- dstack/cli/logs.py +0 -87
- dstack/cli/main.py +0 -81
- dstack/cli/restart.py +0 -43
- dstack/cli/run.py +0 -223
- dstack/cli/schema.py +0 -46
- dstack/cli/secrets.py +0 -97
- dstack/cli/status.py +0 -140
- dstack/cli/stop.py +0 -53
- dstack/cli/tags.py +0 -100
- dstack/config.py +0 -80
- dstack/dashboard/artifacts.py +0 -26
- dstack/dashboard/logs.py +0 -73
- dstack/dashboard/main.py +0 -45
- dstack/dashboard/repos.py +0 -41
- dstack/dashboard/runs.py +0 -140
- dstack/dashboard/secrets.py +0 -53
- dstack/dashboard/statics/4d6a4e032505c1efd23c.png +0 -0
- dstack/dashboard/statics/7e018c3e5566d7c349a8.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-144x144.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-192x192.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-256x256.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-36x36.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-384x384.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-48x48.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-512x512.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-72x72.png +0 -0
- dstack/dashboard/statics/assets/android-chrome-96x96.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-1024x1024.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-114x114.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-120x120.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-144x144.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-152x152.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-167x167.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-180x180.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-57x57.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-60x60.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-72x72.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-76x76.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon-precomposed.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-icon.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1125x2436.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1136x640.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1242x2208.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1242x2688.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1334x750.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1536x2048.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1620x2160.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1668x2224.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1668x2388.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-1792x828.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2048x1536.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2048x2732.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2160x1620.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2208x1242.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2224x1668.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2388x1668.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2436x1125.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2688x1242.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-2732x2048.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-640x1136.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-750x1334.png +0 -0
- dstack/dashboard/statics/assets/apple-touch-startup-image-828x1792.png +0 -0
- dstack/dashboard/statics/assets/browserconfig.xml +0 -15
- dstack/dashboard/statics/assets/coast-228x228.png +0 -0
- dstack/dashboard/statics/assets/favicon-16x16.png +0 -0
- dstack/dashboard/statics/assets/favicon-32x32.png +0 -0
- dstack/dashboard/statics/assets/favicon-48x48.png +0 -0
- dstack/dashboard/statics/assets/favicon.ico +0 -0
- dstack/dashboard/statics/assets/firefox_app_128x128.png +0 -0
- dstack/dashboard/statics/assets/firefox_app_512x512.png +0 -0
- dstack/dashboard/statics/assets/firefox_app_60x60.png +0 -0
- dstack/dashboard/statics/assets/manifest.webapp +0 -14
- dstack/dashboard/statics/assets/mstile-144x144.png +0 -0
- dstack/dashboard/statics/assets/mstile-150x150.png +0 -0
- dstack/dashboard/statics/assets/mstile-310x150.png +0 -0
- dstack/dashboard/statics/assets/mstile-310x310.png +0 -0
- dstack/dashboard/statics/assets/mstile-70x70.png +0 -0
- dstack/dashboard/statics/assets/yandex-browser-50x50.png +0 -0
- dstack/dashboard/statics/d0f71e48806e25d72553.png +0 -0
- dstack/dashboard/statics/index.html +0 -7
- dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js +0 -3
- dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js.LICENSE.txt +0 -102
- dstack/dashboard/statics/main-1d87e34eb0454da8ebb4.js.map +0 -1
- dstack/dashboard/statics/main.css +0 -5058
- dstack/dashboard/statics/splash_thumbnail.png +0 -0
- dstack/dashboard/statics/static/media/check.3f68ffc787a15c0476793a6d18ecb71a.svg +0 -3
- dstack/dashboard/statics/static/media/chevron-down.bfd8f22c4a5db4d443e76bca3b02f334.svg +0 -3
- dstack/dashboard/statics/static/media/chevron-up.bade0c5d82d741cead615813264140c9.svg +0 -3
- dstack/dashboard/statics/static/media/clock.583b744f29b9d143718a55e7c35fe38e.svg +0 -3
- dstack/dashboard/statics/static/media/close.a8bb9e47361b03a3b5084dad676ba1da.svg +0 -3
- dstack/dashboard/statics/static/media/content-copy.73f5f2a175094757758e315243a4111e.svg +0 -3
- dstack/dashboard/statics/static/media/delete-outline.6a8abf4e4f9cb777781967efd56efe9b.svg +0 -3
- dstack/dashboard/statics/static/media/dots-vertical.82fc618192e0c7dc4d615ff93269246a.svg +0 -3
- dstack/dashboard/statics/static/media/earth.1ad57c7f59f4be5c8bb2fa00439c3149.svg +0 -3
- dstack/dashboard/statics/static/media/email.320bc3af24a5f1bb41ebd85f66a5dd70.svg +0 -3
- dstack/dashboard/statics/static/media/external-link.99b88e699c15afb820a1779d9a2261ed.svg +0 -3
- dstack/dashboard/statics/static/media/eye-off-outline.5b4afb7ad624a44dd307518ff93d1faa.svg +0 -3
- dstack/dashboard/statics/static/media/eye-outline.ca41708feaaed1edb15c5fff021fbafe.svg +0 -3
- dstack/dashboard/statics/static/media/file-download-outline.3634b41923ba79b297ff294ef898661c.svg +0 -3
- dstack/dashboard/statics/static/media/folder-outline.33378387af61821dd1207e4b2d061a07.svg +0 -3
- dstack/dashboard/statics/static/media/github-circle.1bb85d171c31a3c2eebad07319377171.svg +0 -3
- dstack/dashboard/statics/static/media/infinity.915f92939afc0a37f94adba211ceb172.svg +0 -3
- dstack/dashboard/statics/static/media/layers.b4b02cea267a617d7aa44c2719250c89.svg +0 -3
- dstack/dashboard/statics/static/media/linkedin.1c52fae553eee54397f0e63a79455a5e.svg +0 -3
- dstack/dashboard/statics/static/media/loading.e466be7b2c1f0ac9e7e51ca929d0e37d.svg +0 -3
- dstack/dashboard/statics/static/media/lock.4a4c7768d0fa60c716609ddc483470ef.svg +0 -3
- dstack/dashboard/statics/static/media/magnify.0c803314d039d21f3cb1504ccd1437a4.svg +0 -3
- dstack/dashboard/statics/static/media/mark.3f68ffc787a15c0476793a6d18ecb71a.svg +0 -3
- dstack/dashboard/statics/static/media/menu-close.3ee84714181017c6ff837830297c8437.svg +0 -3
- dstack/dashboard/statics/static/media/menu.922f81e0972fbcbb5adcd8def20c86a3.svg +0 -3
- dstack/dashboard/statics/static/media/pencil.f706a3b9dcbff4959a91bf72e1e6324f.svg +0 -3
- dstack/dashboard/statics/static/media/refresh.a80edb948e98b322cd73b67814a57a48.svg +0 -3
- dstack/dashboard/statics/static/media/shape-plus.63b093c7f4b44c3def774f30fcfbceca.svg +0 -3
- dstack/dashboard/statics/static/media/slack.ec2fca99c6b944950ac65404ddd26880.svg +0 -4
- dstack/dashboard/statics/static/media/small-logo.b9cc8d09f646a553e65fa336dafd8b10.svg +0 -116
- dstack/dashboard/statics/static/media/source-branch.b8d22cfc42a7bed81f0fc08130818e85.svg +0 -3
- dstack/dashboard/statics/static/media/source-commit.be2bb53c081b9b6836adffccc0b8d3e6.svg +0 -3
- dstack/dashboard/statics/static/media/stop.11488ff1437ad929476be8924a3b7075.svg +0 -3
- dstack/dashboard/statics/static/media/tag-minus.15680a815b0b8d027e973c84832c05e6.svg +0 -3
- dstack/dashboard/statics/static/media/tag-outline.19b0bf86a8afd7d6d9c716e9a91d94ca.svg +0 -3
- dstack/dashboard/statics/static/media/twitter.4af18861c84a2f3044c7546b55d5739c.svg +0 -3
- dstack/dashboard/tags.py +0 -119
- dstack/jobs.py +0 -255
- dstack/providers/__init__.py +0 -316
- dstack/providers/_python/main.py +0 -88
- dstack/providers/_tensorboard/main.py +0 -93
- dstack/providers/_torchrun/main.py +0 -121
- dstack/providers/bash/main.py +0 -90
- dstack/providers/code/main.py +0 -95
- dstack/providers/docker/main.py +0 -79
- dstack/providers/lab/main.py +0 -95
- dstack/providers/notebook/main.py +0 -90
- dstack/random_name.py +0 -29
- dstack/repo.py +0 -135
- dstack/runners.py +0 -35
- dstack/util.py +0 -15
- dstack-0.0.9.dist-info/METADATA +0 -176
- dstack-0.0.9.dist-info/RECORD +0 -179
- dstack-0.0.9.dist-info/entry_points.txt +0 -3
- dstack-0.0.9.dist-info/top_level.txt +0 -2
- tests/test_config.py +0 -70
- /dstack/{cli → _internal}/__init__.py +0 -0
- /dstack/{dashboard → _internal/cli}/__init__.py +0 -0
- /dstack/{providers/_python → _internal/cli/models}/__init__.py +0 -0
- /dstack/{providers/_tensorboard → _internal/cli/services}/__init__.py +0 -0
- /dstack/{providers/_torchrun → _internal/cli/utils}/__init__.py +0 -0
- /dstack/{providers/bash → _internal/core}/__init__.py +0 -0
- /dstack/{providers/code → _internal/core/backends}/__init__.py +0 -0
- /dstack/{providers/docker → _internal/core/backends/aws}/__init__.py +0 -0
- /dstack/{providers/lab → _internal/core/backends/azure}/__init__.py +0 -0
- /dstack/{providers/notebook → _internal/core/backends/base}/__init__.py +0 -0
- {tests → dstack/_internal/core/backends/cloudrift}/__init__.py +0 -0
- /dstack/{dashboard → _internal/server}/statics/assets/yandex-browser-manifest.json +0 -0
- /dstack/{dashboard → _internal/server}/statics/robots.txt +0 -0
|
@@ -0,0 +1,1123 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import string
|
|
3
|
+
from collections import Counter
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from pathlib import PurePosixPath
|
|
6
|
+
from typing import Annotated, Any, Dict, List, Literal, Optional, Union
|
|
7
|
+
|
|
8
|
+
import orjson
|
|
9
|
+
from pydantic import Field, ValidationError, conint, constr, root_validator, validator
|
|
10
|
+
from typing_extensions import Self
|
|
11
|
+
|
|
12
|
+
from dstack._internal.core.errors import ConfigurationError
|
|
13
|
+
from dstack._internal.core.models.common import (
|
|
14
|
+
CoreConfig,
|
|
15
|
+
CoreModel,
|
|
16
|
+
Duration,
|
|
17
|
+
RegistryAuth,
|
|
18
|
+
generate_dual_core_model,
|
|
19
|
+
)
|
|
20
|
+
from dstack._internal.core.models.envs import Env
|
|
21
|
+
from dstack._internal.core.models.files import FilePathMapping
|
|
22
|
+
from dstack._internal.core.models.fleets import FleetConfiguration
|
|
23
|
+
from dstack._internal.core.models.gateways import GatewayConfiguration
|
|
24
|
+
from dstack._internal.core.models.profiles import (
|
|
25
|
+
ProfileParams,
|
|
26
|
+
ProfileParamsConfig,
|
|
27
|
+
parse_duration,
|
|
28
|
+
parse_off_duration,
|
|
29
|
+
)
|
|
30
|
+
from dstack._internal.core.models.resources import Range, ResourcesSpec
|
|
31
|
+
from dstack._internal.core.models.services import AnyModel, OpenAIChatModel
|
|
32
|
+
from dstack._internal.core.models.unix import UnixUser
|
|
33
|
+
from dstack._internal.core.models.volumes import MountPoint, VolumeConfiguration, parse_mount_point
|
|
34
|
+
from dstack._internal.core.services import is_valid_replica_group_name
|
|
35
|
+
from dstack._internal.utils.common import has_duplicates, list_enum_values_for_annotation
|
|
36
|
+
from dstack._internal.utils.json_schema import add_extra_schema_types
|
|
37
|
+
from dstack._internal.utils.json_utils import (
|
|
38
|
+
pydantic_orjson_dumps_with_indent,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
CommandsList = List[str]
|
|
42
|
+
ValidPort = conint(gt=0, le=65536)
|
|
43
|
+
MAX_INT64 = 2**63 - 1
|
|
44
|
+
SERVICE_HTTPS_DEFAULT = True
|
|
45
|
+
STRIP_PREFIX_DEFAULT = True
|
|
46
|
+
RUN_PRIOTIRY_MIN = 0
|
|
47
|
+
RUN_PRIOTIRY_MAX = 100
|
|
48
|
+
RUN_PRIORITY_DEFAULT = 0
|
|
49
|
+
LEGACY_REPO_DIR = "/workflow"
|
|
50
|
+
MIN_PROBE_TIMEOUT = 1
|
|
51
|
+
MIN_PROBE_INTERVAL = 1
|
|
52
|
+
DEFAULT_PROBE_URL = "/"
|
|
53
|
+
DEFAULT_PROBE_TIMEOUT = 10
|
|
54
|
+
DEFAULT_PROBE_INTERVAL = 15
|
|
55
|
+
DEFAULT_PROBE_READY_AFTER = 1
|
|
56
|
+
DEFAULT_PROBE_METHOD = "get"
|
|
57
|
+
MAX_PROBE_URL_LEN = 2048
|
|
58
|
+
DEFAULT_REPLICA_GROUP_NAME = "0"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class RunConfigurationType(str, Enum):
|
|
62
|
+
DEV_ENVIRONMENT = "dev-environment"
|
|
63
|
+
TASK = "task"
|
|
64
|
+
SERVICE = "service"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class PythonVersion(str, Enum):
|
|
68
|
+
PY39 = "3.9"
|
|
69
|
+
PY310 = "3.10"
|
|
70
|
+
PY311 = "3.11"
|
|
71
|
+
PY312 = "3.12"
|
|
72
|
+
PY313 = "3.13"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class PortMapping(CoreModel):
|
|
76
|
+
local_port: Optional[ValidPort] = None
|
|
77
|
+
container_port: ValidPort
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def parse(cls, v: str) -> "PortMapping":
|
|
81
|
+
"""
|
|
82
|
+
Possible values:
|
|
83
|
+
- 8080
|
|
84
|
+
- 80:8080
|
|
85
|
+
- *:8080
|
|
86
|
+
"""
|
|
87
|
+
r = re.search(r"^(?:(\d+|\*):)?(\d+)?$", v)
|
|
88
|
+
if not r:
|
|
89
|
+
raise ValueError(v)
|
|
90
|
+
local_port, container_port = r.groups()
|
|
91
|
+
if local_port is None: # identity mapping by default
|
|
92
|
+
local_port = int(container_port)
|
|
93
|
+
elif local_port == "*":
|
|
94
|
+
local_port = None
|
|
95
|
+
else:
|
|
96
|
+
local_port = int(local_port)
|
|
97
|
+
return PortMapping(local_port=local_port, container_port=int(container_port))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class RepoExistsAction(str, Enum):
|
|
101
|
+
# Don't try to check out, terminate the run with an error (the default action since 0.20.0)
|
|
102
|
+
ERROR = "error"
|
|
103
|
+
# Don't try to check out, skip the repo (the logic hardcoded in the pre-0.20.0 runner)
|
|
104
|
+
SKIP = "skip"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class RepoSpec(CoreModel):
|
|
108
|
+
local_path: Annotated[
|
|
109
|
+
Optional[str],
|
|
110
|
+
Field(
|
|
111
|
+
description=(
|
|
112
|
+
"The path to the Git repo on the user's machine. Relative paths are resolved"
|
|
113
|
+
" relative to the parent directory of the the configuration file."
|
|
114
|
+
" Mutually exclusive with `url`"
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
] = None
|
|
118
|
+
url: Annotated[
|
|
119
|
+
Optional[str],
|
|
120
|
+
Field(description="The Git repo URL. Mutually exclusive with `local_path`"),
|
|
121
|
+
] = None
|
|
122
|
+
branch: Annotated[
|
|
123
|
+
Optional[str],
|
|
124
|
+
Field(
|
|
125
|
+
description=(
|
|
126
|
+
"The repo branch. Defaults to the active branch for local paths"
|
|
127
|
+
" and the default branch for URLs"
|
|
128
|
+
)
|
|
129
|
+
),
|
|
130
|
+
] = None
|
|
131
|
+
hash: Annotated[
|
|
132
|
+
Optional[str],
|
|
133
|
+
Field(description="The commit hash"),
|
|
134
|
+
] = None
|
|
135
|
+
path: Annotated[
|
|
136
|
+
str,
|
|
137
|
+
Field(
|
|
138
|
+
description=(
|
|
139
|
+
"The repo path inside the run container. Relative paths are resolved"
|
|
140
|
+
" relative to the working directory"
|
|
141
|
+
)
|
|
142
|
+
),
|
|
143
|
+
] = "."
|
|
144
|
+
if_exists: Annotated[
|
|
145
|
+
RepoExistsAction,
|
|
146
|
+
Field(
|
|
147
|
+
description=(
|
|
148
|
+
"The action to be taken if `path` exists and is not empty."
|
|
149
|
+
f" One of: {list_enum_values_for_annotation(RepoExistsAction)}"
|
|
150
|
+
),
|
|
151
|
+
),
|
|
152
|
+
] = RepoExistsAction.ERROR
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def parse(cls, v: str) -> Self:
|
|
156
|
+
is_url = False
|
|
157
|
+
parts = v.split(":")
|
|
158
|
+
if len(parts) > 1:
|
|
159
|
+
# Git repo, git@github.com:dstackai/dstack.git or https://github.com/dstackai/dstack
|
|
160
|
+
if "@" in parts[0] or parts[1].startswith("//"):
|
|
161
|
+
parts = [f"{parts[0]}:{parts[1]}", *parts[2:]]
|
|
162
|
+
is_url = True
|
|
163
|
+
# Windows path, e.g., `C:\path\to`, 'c:/path/to'
|
|
164
|
+
elif (
|
|
165
|
+
len(parts[0]) == 1
|
|
166
|
+
and parts[0] in string.ascii_letters
|
|
167
|
+
and parts[1][:1] in ["\\", "/"]
|
|
168
|
+
):
|
|
169
|
+
parts = [f"{parts[0]}:{parts[1]}", *parts[2:]]
|
|
170
|
+
if len(parts) == 1:
|
|
171
|
+
if is_url:
|
|
172
|
+
return cls(url=parts[0])
|
|
173
|
+
return cls(local_path=parts[0])
|
|
174
|
+
if len(parts) == 2:
|
|
175
|
+
if is_url:
|
|
176
|
+
return cls(url=parts[0], path=parts[1])
|
|
177
|
+
return cls(local_path=parts[0], path=parts[1])
|
|
178
|
+
raise ValueError(f"Invalid repo: {v}")
|
|
179
|
+
|
|
180
|
+
@root_validator
|
|
181
|
+
def validate_local_path_or_url(cls, values):
|
|
182
|
+
if values["local_path"] and values["url"]:
|
|
183
|
+
raise ValueError("`local_path` and `url` are mutually exclusive")
|
|
184
|
+
if not values["local_path"] and not values["url"]:
|
|
185
|
+
raise ValueError("Either `local_path` or `url` must be specified")
|
|
186
|
+
return values
|
|
187
|
+
|
|
188
|
+
@validator("path")
|
|
189
|
+
def validate_path(cls, v: Optional[str]) -> Optional[str]:
|
|
190
|
+
if v is None:
|
|
191
|
+
return v
|
|
192
|
+
if v.startswith("~") and PurePosixPath(v).parts[0] != "~":
|
|
193
|
+
raise ValueError("`~username` syntax is not supported")
|
|
194
|
+
return v
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class ScalingSpec(CoreModel):
|
|
198
|
+
metric: Annotated[
|
|
199
|
+
Literal["rps"],
|
|
200
|
+
Field(
|
|
201
|
+
description="The target metric to track. Currently, the only supported value is `rps` "
|
|
202
|
+
"(meaning requests per second)"
|
|
203
|
+
),
|
|
204
|
+
]
|
|
205
|
+
target: Annotated[
|
|
206
|
+
float,
|
|
207
|
+
Field(
|
|
208
|
+
description="The target value of the metric. "
|
|
209
|
+
"The number of replicas is calculated based on this number and automatically adjusts "
|
|
210
|
+
"(scales up or down) as this metric changes",
|
|
211
|
+
gt=0,
|
|
212
|
+
),
|
|
213
|
+
]
|
|
214
|
+
scale_up_delay: Annotated[
|
|
215
|
+
Duration, Field(description="The delay in seconds before scaling up")
|
|
216
|
+
] = Duration.parse("5m")
|
|
217
|
+
scale_down_delay: Annotated[
|
|
218
|
+
Duration, Field(description="The delay in seconds before scaling down")
|
|
219
|
+
] = Duration.parse("10m")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class IPAddressPartitioningKey(CoreModel):
|
|
223
|
+
type: Annotated[Literal["ip_address"], Field(description="Partitioning type")] = "ip_address"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class HeaderPartitioningKey(CoreModel):
|
|
227
|
+
type: Annotated[Literal["header"], Field(description="Partitioning type")] = "header"
|
|
228
|
+
header: Annotated[
|
|
229
|
+
str,
|
|
230
|
+
Field(
|
|
231
|
+
description="Name of the header to use for partitioning",
|
|
232
|
+
regex=r"^[a-zA-Z0-9-_]+$", # prevent Nginx config injection
|
|
233
|
+
max_length=500, # chosen randomly, Nginx limit is higher
|
|
234
|
+
),
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class RateLimit(CoreModel):
|
|
239
|
+
prefix: Annotated[
|
|
240
|
+
str,
|
|
241
|
+
Field(
|
|
242
|
+
description=(
|
|
243
|
+
"URL path prefix to which this limit is applied."
|
|
244
|
+
" If an incoming request matches several prefixes, the longest prefix is applied"
|
|
245
|
+
),
|
|
246
|
+
max_length=4094, # Nginx limit
|
|
247
|
+
regex=r"^/[^\s\\{}]*$", # prevent Nginx config injection
|
|
248
|
+
),
|
|
249
|
+
] = "/"
|
|
250
|
+
key: Annotated[
|
|
251
|
+
Union[IPAddressPartitioningKey, HeaderPartitioningKey],
|
|
252
|
+
Field(
|
|
253
|
+
discriminator="type",
|
|
254
|
+
description=(
|
|
255
|
+
"The partitioning key. Each incoming request belongs to a partition"
|
|
256
|
+
" and rate limits are applied per partition."
|
|
257
|
+
" Defaults to partitioning by client IP address"
|
|
258
|
+
),
|
|
259
|
+
),
|
|
260
|
+
] = IPAddressPartitioningKey()
|
|
261
|
+
rps: Annotated[
|
|
262
|
+
float,
|
|
263
|
+
Field(
|
|
264
|
+
description=(
|
|
265
|
+
"Max allowed number of requests per second."
|
|
266
|
+
" Requests are tracked at millisecond granularity."
|
|
267
|
+
" For example, `rps: 10` means at most 1 request per 100ms"
|
|
268
|
+
),
|
|
269
|
+
# should fit into Nginx limits after being converted to requests per minute
|
|
270
|
+
ge=1 / 60,
|
|
271
|
+
le=MAX_INT64 // 60,
|
|
272
|
+
),
|
|
273
|
+
]
|
|
274
|
+
burst: Annotated[
|
|
275
|
+
int,
|
|
276
|
+
Field(
|
|
277
|
+
ge=0,
|
|
278
|
+
le=MAX_INT64, # Nginx limit
|
|
279
|
+
description=(
|
|
280
|
+
"Max number of requests that can be passed to the service ahead of the rate limit"
|
|
281
|
+
),
|
|
282
|
+
),
|
|
283
|
+
] = 0
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
HTTPMethod = Literal["get", "post", "put", "delete", "patch", "head"]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class HTTPHeaderSpec(CoreModel):
|
|
290
|
+
name: Annotated[
|
|
291
|
+
str,
|
|
292
|
+
Field(
|
|
293
|
+
description="The name of the HTTP header",
|
|
294
|
+
min_length=1,
|
|
295
|
+
max_length=256,
|
|
296
|
+
),
|
|
297
|
+
]
|
|
298
|
+
value: Annotated[
|
|
299
|
+
str,
|
|
300
|
+
Field(
|
|
301
|
+
description="The value of the HTTP header",
|
|
302
|
+
min_length=1,
|
|
303
|
+
max_length=2048,
|
|
304
|
+
),
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class ProbeConfigConfig(CoreConfig):
|
|
309
|
+
@staticmethod
|
|
310
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
311
|
+
add_extra_schema_types(
|
|
312
|
+
schema["properties"]["timeout"],
|
|
313
|
+
extra_types=[{"type": "string"}],
|
|
314
|
+
)
|
|
315
|
+
add_extra_schema_types(
|
|
316
|
+
schema["properties"]["interval"],
|
|
317
|
+
extra_types=[{"type": "string"}],
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class ProbeConfig(generate_dual_core_model(ProbeConfigConfig)):
|
|
322
|
+
type: Literal["http"] # expect other probe types in the future, namely `exec`
|
|
323
|
+
url: Annotated[
|
|
324
|
+
Optional[str], Field(description=f"The URL to request. Defaults to `{DEFAULT_PROBE_URL}`")
|
|
325
|
+
] = None
|
|
326
|
+
method: Annotated[
|
|
327
|
+
Optional[HTTPMethod],
|
|
328
|
+
Field(
|
|
329
|
+
description=(
|
|
330
|
+
"The HTTP method to use for the probe (e.g., `get`, `post`, etc.)."
|
|
331
|
+
f" Defaults to `{DEFAULT_PROBE_METHOD}`"
|
|
332
|
+
)
|
|
333
|
+
),
|
|
334
|
+
] = None
|
|
335
|
+
headers: Annotated[
|
|
336
|
+
list[HTTPHeaderSpec],
|
|
337
|
+
Field(description="A list of HTTP headers to include in the request", max_items=16),
|
|
338
|
+
] = []
|
|
339
|
+
body: Annotated[
|
|
340
|
+
Optional[str],
|
|
341
|
+
Field(
|
|
342
|
+
description="The HTTP request body to send with the probe",
|
|
343
|
+
min_length=1,
|
|
344
|
+
max_length=2048,
|
|
345
|
+
),
|
|
346
|
+
] = None
|
|
347
|
+
timeout: Annotated[
|
|
348
|
+
Optional[int],
|
|
349
|
+
Field(
|
|
350
|
+
description=(
|
|
351
|
+
f"Maximum amount of time the HTTP request is allowed to take. Defaults to `{DEFAULT_PROBE_TIMEOUT}s`"
|
|
352
|
+
)
|
|
353
|
+
),
|
|
354
|
+
] = None
|
|
355
|
+
interval: Annotated[
|
|
356
|
+
Optional[int],
|
|
357
|
+
Field(
|
|
358
|
+
description=(
|
|
359
|
+
"Minimum amount of time between the end of one probe execution"
|
|
360
|
+
f" and the start of the next. Defaults to `{DEFAULT_PROBE_INTERVAL}s`"
|
|
361
|
+
)
|
|
362
|
+
),
|
|
363
|
+
] = None
|
|
364
|
+
ready_after: Annotated[
|
|
365
|
+
Optional[int],
|
|
366
|
+
Field(
|
|
367
|
+
ge=1,
|
|
368
|
+
description=(
|
|
369
|
+
"The number of consecutive successful probe executions required for the replica"
|
|
370
|
+
" to be considered ready. Used during rolling deployments."
|
|
371
|
+
f" Defaults to `{DEFAULT_PROBE_READY_AFTER}`"
|
|
372
|
+
),
|
|
373
|
+
),
|
|
374
|
+
] = None
|
|
375
|
+
|
|
376
|
+
@validator("timeout", pre=True)
|
|
377
|
+
def parse_timeout(cls, v: Optional[Union[int, str]]) -> Optional[int]:
|
|
378
|
+
if v is None:
|
|
379
|
+
return v
|
|
380
|
+
parsed = parse_duration(v)
|
|
381
|
+
if parsed < MIN_PROBE_TIMEOUT:
|
|
382
|
+
raise ValueError(f"Probe timeout cannot be shorter than {MIN_PROBE_TIMEOUT}s")
|
|
383
|
+
return parsed
|
|
384
|
+
|
|
385
|
+
@validator("interval", pre=True)
|
|
386
|
+
def parse_interval(cls, v: Optional[Union[int, str]]) -> Optional[int]:
|
|
387
|
+
if v is None:
|
|
388
|
+
return v
|
|
389
|
+
parsed = parse_duration(v)
|
|
390
|
+
if parsed < MIN_PROBE_INTERVAL:
|
|
391
|
+
raise ValueError(f"Probe interval cannot be shorter than {MIN_PROBE_INTERVAL}s")
|
|
392
|
+
return parsed
|
|
393
|
+
|
|
394
|
+
@validator("url")
|
|
395
|
+
def validate_url(cls, v: Optional[str]) -> Optional[str]:
|
|
396
|
+
if v is None:
|
|
397
|
+
return v
|
|
398
|
+
if not v.startswith("/"):
|
|
399
|
+
raise ValueError("Must start with `/`")
|
|
400
|
+
if len(v) > MAX_PROBE_URL_LEN:
|
|
401
|
+
raise ValueError(f"Cannot be longer than {MAX_PROBE_URL_LEN} characters")
|
|
402
|
+
if not v.isprintable():
|
|
403
|
+
raise ValueError("Cannot contain non-printable characters")
|
|
404
|
+
return v
|
|
405
|
+
|
|
406
|
+
@root_validator
|
|
407
|
+
def validate_body_matches_method(cls, values):
|
|
408
|
+
method: HTTPMethod = values["method"]
|
|
409
|
+
if values["body"] is not None and method in ["get", "head"]:
|
|
410
|
+
raise ValueError(f"Cannot set request body for the `{method}` method")
|
|
411
|
+
return values
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class BaseRunConfigurationConfig(CoreConfig):
|
|
415
|
+
@staticmethod
|
|
416
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
417
|
+
add_extra_schema_types(
|
|
418
|
+
schema["properties"]["volumes"]["items"],
|
|
419
|
+
extra_types=[{"type": "string"}],
|
|
420
|
+
)
|
|
421
|
+
add_extra_schema_types(
|
|
422
|
+
schema["properties"]["files"]["items"],
|
|
423
|
+
extra_types=[{"type": "string"}],
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
class BaseRunConfiguration(CoreModel):
|
|
428
|
+
type: Literal["none"]
|
|
429
|
+
name: Annotated[
|
|
430
|
+
Optional[str],
|
|
431
|
+
Field(description="The run name. If not specified, a random name is generated"),
|
|
432
|
+
] = None
|
|
433
|
+
image: Annotated[Optional[str], Field(description="The name of the Docker image to run")] = (
|
|
434
|
+
None
|
|
435
|
+
)
|
|
436
|
+
user: Annotated[
|
|
437
|
+
Optional[str],
|
|
438
|
+
Field(
|
|
439
|
+
description=(
|
|
440
|
+
"The user inside the container, `user_name_or_id[:group_name_or_id]`"
|
|
441
|
+
" (e.g., `ubuntu`, `1000:1000`). Defaults to the default user from the `image`"
|
|
442
|
+
)
|
|
443
|
+
),
|
|
444
|
+
] = None
|
|
445
|
+
privileged: Annotated[bool, Field(description="Run the container in privileged mode")] = False
|
|
446
|
+
entrypoint: Annotated[Optional[str], Field(description="The Docker entrypoint")] = None
|
|
447
|
+
working_dir: Annotated[
|
|
448
|
+
Optional[str],
|
|
449
|
+
Field(
|
|
450
|
+
description=(
|
|
451
|
+
"The absolute path to the working directory inside the container."
|
|
452
|
+
" Defaults to the `image`'s default working directory"
|
|
453
|
+
),
|
|
454
|
+
),
|
|
455
|
+
] = None
|
|
456
|
+
# deprecated since 0.18.31; has no effect
|
|
457
|
+
home_dir: str = "/root"
|
|
458
|
+
registry_auth: Annotated[
|
|
459
|
+
Optional[RegistryAuth], Field(description="Credentials for pulling a private Docker image")
|
|
460
|
+
] = None
|
|
461
|
+
python: Annotated[
|
|
462
|
+
Optional[PythonVersion],
|
|
463
|
+
Field(
|
|
464
|
+
description="The major version of Python. Mutually exclusive with `image` and `docker`"
|
|
465
|
+
),
|
|
466
|
+
] = None
|
|
467
|
+
nvcc: Annotated[
|
|
468
|
+
Optional[bool],
|
|
469
|
+
Field(
|
|
470
|
+
description="Use image with NVIDIA CUDA Compiler (NVCC) included. Mutually exclusive with `image` and `docker`"
|
|
471
|
+
),
|
|
472
|
+
] = None
|
|
473
|
+
single_branch: Annotated[
|
|
474
|
+
Optional[bool],
|
|
475
|
+
Field(
|
|
476
|
+
description=(
|
|
477
|
+
"Whether to clone and track only the current branch or all remote branches."
|
|
478
|
+
" Relevant only when using remote Git repos."
|
|
479
|
+
" Defaults to `false` for dev environments and to `true` for tasks and services"
|
|
480
|
+
)
|
|
481
|
+
),
|
|
482
|
+
] = None
|
|
483
|
+
env: Annotated[
|
|
484
|
+
Env,
|
|
485
|
+
Field(description="The mapping or the list of environment variables"),
|
|
486
|
+
] = Env()
|
|
487
|
+
shell: Annotated[
|
|
488
|
+
Optional[str],
|
|
489
|
+
Field(
|
|
490
|
+
description=(
|
|
491
|
+
"The shell used to run commands."
|
|
492
|
+
" Allowed values are `sh`, `bash`, or an absolute path, e.g., `/usr/bin/zsh`."
|
|
493
|
+
" Defaults to `/bin/sh` if the `image` is specified, `/bin/bash` otherwise"
|
|
494
|
+
)
|
|
495
|
+
),
|
|
496
|
+
] = None
|
|
497
|
+
resources: Annotated[
|
|
498
|
+
ResourcesSpec, Field(description="The resources requirements to run the configuration")
|
|
499
|
+
] = ResourcesSpec()
|
|
500
|
+
priority: Annotated[
|
|
501
|
+
Optional[int],
|
|
502
|
+
Field(
|
|
503
|
+
ge=RUN_PRIOTIRY_MIN,
|
|
504
|
+
le=RUN_PRIOTIRY_MAX,
|
|
505
|
+
description=(
|
|
506
|
+
f"The priority of the run, an integer between `{RUN_PRIOTIRY_MIN}` and `{RUN_PRIOTIRY_MAX}`."
|
|
507
|
+
" `dstack` tries to provision runs with higher priority first."
|
|
508
|
+
f" Defaults to `{RUN_PRIORITY_DEFAULT}`"
|
|
509
|
+
),
|
|
510
|
+
),
|
|
511
|
+
] = None
|
|
512
|
+
volumes: Annotated[List[MountPoint], Field(description="The volumes mount points")] = []
|
|
513
|
+
docker: Annotated[
|
|
514
|
+
Optional[bool],
|
|
515
|
+
Field(
|
|
516
|
+
description="Use Docker inside the container. Mutually exclusive with `image`, `python`, and `nvcc`. Overrides `privileged`"
|
|
517
|
+
),
|
|
518
|
+
] = None
|
|
519
|
+
repos: Annotated[
|
|
520
|
+
list[RepoSpec],
|
|
521
|
+
Field(description="The list of Git repos"),
|
|
522
|
+
] = []
|
|
523
|
+
files: Annotated[
|
|
524
|
+
list[FilePathMapping],
|
|
525
|
+
Field(description="The local to container file path mappings"),
|
|
526
|
+
] = []
|
|
527
|
+
# deprecated since 0.18.31; task, service -- no effect; dev-environment -- executed right before `init`
|
|
528
|
+
setup: CommandsList = []
|
|
529
|
+
|
|
530
|
+
@validator("python", pre=True, always=True)
|
|
531
|
+
def convert_python(cls, v, values) -> Optional[PythonVersion]:
|
|
532
|
+
if v is not None and values.get("image"):
|
|
533
|
+
raise KeyError("`image` and `python` are mutually exclusive fields")
|
|
534
|
+
if isinstance(v, float):
|
|
535
|
+
v = str(v)
|
|
536
|
+
if v == "3.1":
|
|
537
|
+
v = "3.10"
|
|
538
|
+
if isinstance(v, str):
|
|
539
|
+
return PythonVersion(v)
|
|
540
|
+
return v
|
|
541
|
+
|
|
542
|
+
@validator("docker", pre=True, always=True)
|
|
543
|
+
def _docker(cls, v, values) -> Optional[bool]:
|
|
544
|
+
if v is True and values.get("image"):
|
|
545
|
+
raise KeyError("`image` and `docker` are mutually exclusive fields")
|
|
546
|
+
if v is True and values.get("python"):
|
|
547
|
+
raise KeyError("`python` and `docker` are mutually exclusive fields")
|
|
548
|
+
if v is True and values.get("nvcc"):
|
|
549
|
+
raise KeyError("`nvcc` and `docker` are mutually exclusive fields")
|
|
550
|
+
# Ideally, we'd like to also prohibit privileged=False when docker=True,
|
|
551
|
+
# but it's not possible to do so without breaking backwards compatibility.
|
|
552
|
+
return v
|
|
553
|
+
|
|
554
|
+
@validator("volumes", each_item=True, pre=True)
|
|
555
|
+
def convert_volumes(cls, v: Union[MountPoint, str]) -> MountPoint:
|
|
556
|
+
if isinstance(v, str):
|
|
557
|
+
return parse_mount_point(v)
|
|
558
|
+
return v
|
|
559
|
+
|
|
560
|
+
@validator("files", each_item=True, pre=True)
|
|
561
|
+
def convert_files(cls, v: Union[FilePathMapping, str]) -> FilePathMapping:
|
|
562
|
+
if isinstance(v, str):
|
|
563
|
+
return FilePathMapping.parse(v)
|
|
564
|
+
return v
|
|
565
|
+
|
|
566
|
+
@validator("repos", pre=True, each_item=True)
|
|
567
|
+
def convert_repos(cls, v: Union[RepoSpec, str]) -> RepoSpec:
|
|
568
|
+
if isinstance(v, str):
|
|
569
|
+
return RepoSpec.parse(v)
|
|
570
|
+
return v
|
|
571
|
+
|
|
572
|
+
@validator("repos")
|
|
573
|
+
def validate_repos(cls, v) -> RepoSpec:
|
|
574
|
+
if len(v) > 1:
|
|
575
|
+
raise ValueError("A maximum of one repo is currently supported")
|
|
576
|
+
return v
|
|
577
|
+
|
|
578
|
+
@validator("user")
|
|
579
|
+
def validate_user(cls, v) -> Optional[str]:
|
|
580
|
+
if v is None:
|
|
581
|
+
return None
|
|
582
|
+
UnixUser.parse(v)
|
|
583
|
+
return v
|
|
584
|
+
|
|
585
|
+
@validator("shell")
|
|
586
|
+
def validate_shell(cls, v) -> Optional[str]:
|
|
587
|
+
if v is None:
|
|
588
|
+
return None
|
|
589
|
+
if v in ["sh", "bash"]:
|
|
590
|
+
return v
|
|
591
|
+
path = PurePosixPath(v)
|
|
592
|
+
if path.is_absolute():
|
|
593
|
+
return v
|
|
594
|
+
raise ValueError("The value must be `sh`, `bash`, or an absolute path")
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
class ConfigurationWithPortsParams(CoreModel):
|
|
598
|
+
ports: Annotated[
|
|
599
|
+
List[Union[ValidPort, constr(regex=r"^(?:[0-9]+|\*):[0-9]+$"), PortMapping]],
|
|
600
|
+
Field(description="Port numbers/mapping to expose"),
|
|
601
|
+
] = []
|
|
602
|
+
|
|
603
|
+
@validator("ports", each_item=True)
|
|
604
|
+
def convert_ports(cls, v) -> PortMapping:
|
|
605
|
+
if isinstance(v, int):
|
|
606
|
+
return PortMapping(local_port=v, container_port=v)
|
|
607
|
+
elif isinstance(v, str):
|
|
608
|
+
return PortMapping.parse(v)
|
|
609
|
+
return v
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
class ConfigurationWithCommandsParams(CoreModel):
|
|
613
|
+
commands: Annotated[CommandsList, Field(description="The shell commands to run")] = []
|
|
614
|
+
|
|
615
|
+
@root_validator
|
|
616
|
+
def check_image_or_commands_present(cls, values):
|
|
617
|
+
# If replicas is list, skip validation - commands come from replica groups
|
|
618
|
+
replicas = values.get("replicas")
|
|
619
|
+
if isinstance(replicas, list):
|
|
620
|
+
return values
|
|
621
|
+
|
|
622
|
+
if not values.get("commands") and not values.get("image"):
|
|
623
|
+
raise ValueError("Either `commands` or `image` must be set")
|
|
624
|
+
return values
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
class DevEnvironmentConfigurationParams(CoreModel):
|
|
628
|
+
ide: Annotated[
|
|
629
|
+
Union[Literal["vscode"], Literal["cursor"], Literal["windsurf"]],
|
|
630
|
+
Field(
|
|
631
|
+
description="The IDE to run. Supported values include `vscode`, `cursor`, and `windsurf`"
|
|
632
|
+
),
|
|
633
|
+
]
|
|
634
|
+
version: Annotated[
|
|
635
|
+
Optional[str],
|
|
636
|
+
Field(
|
|
637
|
+
description="The version of the IDE. For `windsurf`, the version is in the format `version@commit`"
|
|
638
|
+
),
|
|
639
|
+
] = None
|
|
640
|
+
init: Annotated[CommandsList, Field(description="The shell commands to run on startup")] = []
|
|
641
|
+
inactivity_duration: Annotated[
|
|
642
|
+
Optional[Union[Literal["off"], int, bool, str]],
|
|
643
|
+
Field(
|
|
644
|
+
description=(
|
|
645
|
+
"The maximum amount of time the dev environment can be inactive"
|
|
646
|
+
" (e.g., `2h`, `1d`, etc)."
|
|
647
|
+
" After it elapses, the dev environment is automatically stopped."
|
|
648
|
+
" Inactivity is defined as the absence of SSH connections to the"
|
|
649
|
+
" dev environment, including VS Code connections, `ssh <run name>`"
|
|
650
|
+
" shells, and attached `dstack apply` or `dstack attach` commands."
|
|
651
|
+
" Use `off` for unlimited duration. Can be updated in-place."
|
|
652
|
+
" Defaults to `off`"
|
|
653
|
+
)
|
|
654
|
+
),
|
|
655
|
+
] = None
|
|
656
|
+
|
|
657
|
+
@validator("inactivity_duration", pre=True, allow_reuse=True)
|
|
658
|
+
def parse_inactivity_duration(
|
|
659
|
+
cls, v: Optional[Union[Literal["off"], int, bool, str]]
|
|
660
|
+
) -> Optional[int]:
|
|
661
|
+
v = parse_off_duration(v)
|
|
662
|
+
if isinstance(v, int):
|
|
663
|
+
return v
|
|
664
|
+
return None
|
|
665
|
+
|
|
666
|
+
@root_validator
|
|
667
|
+
def validate_windsurf_version_format(cls, values):
|
|
668
|
+
ide = values.get("ide")
|
|
669
|
+
version = values.get("version")
|
|
670
|
+
if ide == "windsurf" and version:
|
|
671
|
+
# Validate format: version@commit
|
|
672
|
+
if not re.match(r"^.+@[a-f0-9]+$", version):
|
|
673
|
+
raise ValueError(
|
|
674
|
+
f"Invalid Windsurf version format: `{version}`. "
|
|
675
|
+
"Expected format: `version@commit` (e.g., `1.106.0@8951cd3ad688e789573d7f51750d67ae4a0bea7d`)"
|
|
676
|
+
)
|
|
677
|
+
return values
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
class DevEnvironmentConfigurationConfig(
|
|
681
|
+
ProfileParamsConfig,
|
|
682
|
+
BaseRunConfigurationConfig,
|
|
683
|
+
):
|
|
684
|
+
@staticmethod
|
|
685
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
686
|
+
ProfileParamsConfig.schema_extra(schema)
|
|
687
|
+
BaseRunConfigurationConfig.schema_extra(schema)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
class DevEnvironmentConfiguration(
|
|
691
|
+
ProfileParams,
|
|
692
|
+
BaseRunConfiguration,
|
|
693
|
+
ConfigurationWithPortsParams,
|
|
694
|
+
DevEnvironmentConfigurationParams,
|
|
695
|
+
generate_dual_core_model(DevEnvironmentConfigurationConfig),
|
|
696
|
+
):
|
|
697
|
+
type: Literal["dev-environment"] = "dev-environment"
|
|
698
|
+
|
|
699
|
+
@validator("entrypoint")
|
|
700
|
+
def validate_entrypoint(cls, v: Optional[str]) -> Optional[str]:
|
|
701
|
+
if v is not None:
|
|
702
|
+
raise ValueError("entrypoint is not supported for dev-environment")
|
|
703
|
+
return v
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
class TaskConfigurationParams(CoreModel):
|
|
707
|
+
nodes: Annotated[int, Field(description="Number of nodes", ge=1)] = 1
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
class TaskConfigurationConfig(
|
|
711
|
+
ProfileParamsConfig,
|
|
712
|
+
BaseRunConfigurationConfig,
|
|
713
|
+
):
|
|
714
|
+
@staticmethod
|
|
715
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
716
|
+
ProfileParamsConfig.schema_extra(schema)
|
|
717
|
+
BaseRunConfigurationConfig.schema_extra(schema)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
class TaskConfiguration(
|
|
721
|
+
ProfileParams,
|
|
722
|
+
BaseRunConfiguration,
|
|
723
|
+
ConfigurationWithCommandsParams,
|
|
724
|
+
ConfigurationWithPortsParams,
|
|
725
|
+
TaskConfigurationParams,
|
|
726
|
+
generate_dual_core_model(TaskConfigurationConfig),
|
|
727
|
+
):
|
|
728
|
+
type: Literal["task"] = "task"
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
class ServiceConfigurationParamsConfig(CoreConfig):
|
|
732
|
+
@staticmethod
|
|
733
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
734
|
+
add_extra_schema_types(
|
|
735
|
+
schema["properties"]["replicas"],
|
|
736
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
737
|
+
)
|
|
738
|
+
add_extra_schema_types(
|
|
739
|
+
schema["properties"]["model"],
|
|
740
|
+
extra_types=[{"type": "string"}],
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
def _validate_replica_range(v: Range[int]) -> Range[int]:
|
|
745
|
+
"""Validate a Range[int] used for replica counts."""
|
|
746
|
+
if v.max is None:
|
|
747
|
+
raise ValueError("The maximum number of replicas is required")
|
|
748
|
+
if v.min is None:
|
|
749
|
+
v.min = 0
|
|
750
|
+
if v.min < 0:
|
|
751
|
+
raise ValueError("The minimum number of replicas must be greater than or equal to 0")
|
|
752
|
+
return v
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
class ReplicaGroup(CoreModel):
|
|
756
|
+
name: Annotated[
|
|
757
|
+
Optional[str],
|
|
758
|
+
Field(
|
|
759
|
+
description="The name of the replica group. If not provided, defaults to '0', '1', etc. based on position."
|
|
760
|
+
),
|
|
761
|
+
]
|
|
762
|
+
count: Annotated[
|
|
763
|
+
Range[int],
|
|
764
|
+
Field(
|
|
765
|
+
description="The number of replicas. Can be a number (e.g. `2`) or a range (`0..4` or `1..8`). "
|
|
766
|
+
"If it's a range, the `scaling` property is required"
|
|
767
|
+
),
|
|
768
|
+
]
|
|
769
|
+
scaling: Annotated[
|
|
770
|
+
Optional[ScalingSpec],
|
|
771
|
+
Field(description="The auto-scaling rules. Required if `count` is set to a range"),
|
|
772
|
+
] = None
|
|
773
|
+
|
|
774
|
+
resources: Annotated[
|
|
775
|
+
ResourcesSpec,
|
|
776
|
+
Field(description="The resources requirements for replicas in this group"),
|
|
777
|
+
] = ResourcesSpec()
|
|
778
|
+
|
|
779
|
+
commands: Annotated[
|
|
780
|
+
CommandsList,
|
|
781
|
+
Field(description="The shell commands to run for replicas in this group"),
|
|
782
|
+
] = []
|
|
783
|
+
|
|
784
|
+
@validator("name")
|
|
785
|
+
def validate_name(cls, v: Optional[str]) -> Optional[str]:
|
|
786
|
+
if v is not None:
|
|
787
|
+
if not is_valid_replica_group_name(v):
|
|
788
|
+
raise ValueError("Resource name should match regex '^[a-z0-9][a-z0-9-]{0,39}$'")
|
|
789
|
+
return v
|
|
790
|
+
|
|
791
|
+
@validator("count")
|
|
792
|
+
def convert_count(cls, v: Range[int]) -> Range[int]:
|
|
793
|
+
return _validate_replica_range(v)
|
|
794
|
+
|
|
795
|
+
@root_validator()
|
|
796
|
+
def validate_scaling(cls, values):
|
|
797
|
+
scaling = values.get("scaling")
|
|
798
|
+
count = values.get("count")
|
|
799
|
+
if count and count.min != count.max and not scaling:
|
|
800
|
+
raise ValueError("When you set `count` to a range, ensure to specify `scaling`.")
|
|
801
|
+
if count and count.min == count.max and scaling:
|
|
802
|
+
raise ValueError("To use `scaling`, `count` must be set to a range.")
|
|
803
|
+
return values
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
class ServiceConfigurationParams(CoreModel):
|
|
807
|
+
port: Annotated[
|
|
808
|
+
# NOTE: it's a PortMapping for historical reasons. Only `port.container_port` is used.
|
|
809
|
+
Union[ValidPort, constr(regex=r"^[0-9]+:[0-9]+$"), PortMapping],
|
|
810
|
+
Field(description="The port the application listens on"),
|
|
811
|
+
]
|
|
812
|
+
gateway: Annotated[
|
|
813
|
+
Optional[Union[bool, str]],
|
|
814
|
+
Field(
|
|
815
|
+
description=(
|
|
816
|
+
"The name of the gateway. Specify boolean `false` to run without a gateway."
|
|
817
|
+
" Specify boolean `true` to run with the default gateway."
|
|
818
|
+
" Omit to run with the default gateway if there is one, or without a gateway otherwise"
|
|
819
|
+
),
|
|
820
|
+
),
|
|
821
|
+
] = None
|
|
822
|
+
strip_prefix: Annotated[
|
|
823
|
+
bool,
|
|
824
|
+
Field(
|
|
825
|
+
description=(
|
|
826
|
+
"Strip the `/proxy/services/<project name>/<run name>/` path prefix"
|
|
827
|
+
" when forwarding requests to the service. Only takes effect"
|
|
828
|
+
" when running the service without a gateway"
|
|
829
|
+
)
|
|
830
|
+
),
|
|
831
|
+
] = STRIP_PREFIX_DEFAULT
|
|
832
|
+
model: Annotated[
|
|
833
|
+
Optional[AnyModel],
|
|
834
|
+
Field(
|
|
835
|
+
description=(
|
|
836
|
+
"Mapping of the model for the OpenAI-compatible endpoint provided by `dstack`."
|
|
837
|
+
" Can be a full model format definition or just a model name."
|
|
838
|
+
" If it's a name, the service is expected to expose an OpenAI-compatible"
|
|
839
|
+
" API at the `/v1` path"
|
|
840
|
+
)
|
|
841
|
+
),
|
|
842
|
+
] = None
|
|
843
|
+
https: Annotated[bool, Field(description="Enable HTTPS if running with a gateway")] = (
|
|
844
|
+
SERVICE_HTTPS_DEFAULT
|
|
845
|
+
)
|
|
846
|
+
auth: Annotated[bool, Field(description="Enable the authorization")] = True
|
|
847
|
+
|
|
848
|
+
scaling: Annotated[
|
|
849
|
+
Optional[ScalingSpec],
|
|
850
|
+
Field(description="The auto-scaling rules. Required if `replicas` is set to a range"),
|
|
851
|
+
] = None
|
|
852
|
+
rate_limits: Annotated[list[RateLimit], Field(description="Rate limiting rules")] = []
|
|
853
|
+
probes: Annotated[
|
|
854
|
+
list[ProbeConfig],
|
|
855
|
+
Field(description="List of probes used to determine job health"),
|
|
856
|
+
] = []
|
|
857
|
+
|
|
858
|
+
replicas: Annotated[
|
|
859
|
+
Optional[Union[List[ReplicaGroup], Range[int]]],
|
|
860
|
+
Field(
|
|
861
|
+
description=(
|
|
862
|
+
"The number of replicas or a list of replica groups. "
|
|
863
|
+
"Can be an integer (e.g., `2`), a range (e.g., `0..4`), or a list of replica groups. "
|
|
864
|
+
"Each replica group defines replicas with shared configuration "
|
|
865
|
+
"(commands, resources, scaling). "
|
|
866
|
+
"When `replicas` is a list of replica groups, top-level `scaling`, `commands`, "
|
|
867
|
+
"and `resources` are not allowed and must be specified in each replica group instead. "
|
|
868
|
+
)
|
|
869
|
+
),
|
|
870
|
+
] = None
|
|
871
|
+
|
|
872
|
+
@validator("port")
|
|
873
|
+
def convert_port(cls, v) -> PortMapping:
|
|
874
|
+
if isinstance(v, int):
|
|
875
|
+
return PortMapping(local_port=80, container_port=v)
|
|
876
|
+
elif isinstance(v, str):
|
|
877
|
+
return PortMapping.parse(v)
|
|
878
|
+
return v
|
|
879
|
+
|
|
880
|
+
@validator("model", pre=True)
|
|
881
|
+
def convert_model(cls, v: Optional[Union[AnyModel, str]]) -> Optional[AnyModel]:
|
|
882
|
+
if isinstance(v, str):
|
|
883
|
+
return OpenAIChatModel(type="chat", name=v, format="openai")
|
|
884
|
+
return v
|
|
885
|
+
|
|
886
|
+
@validator("rate_limits")
|
|
887
|
+
def validate_rate_limits(cls, v: list[RateLimit]) -> list[RateLimit]:
|
|
888
|
+
counts = Counter(limit.prefix for limit in v)
|
|
889
|
+
duplicates = [prefix for prefix, count in counts.items() if count > 1]
|
|
890
|
+
if duplicates:
|
|
891
|
+
raise ValueError(
|
|
892
|
+
f"Prefixes {duplicates} are used more than once."
|
|
893
|
+
" Each rate limit should have a unique path prefix"
|
|
894
|
+
)
|
|
895
|
+
return v
|
|
896
|
+
|
|
897
|
+
@validator("probes")
|
|
898
|
+
def validate_probes(cls, v: list[ProbeConfig]) -> list[ProbeConfig]:
|
|
899
|
+
if has_duplicates(v):
|
|
900
|
+
# Using a custom validator instead of Field(unique_items=True) to avoid Pydantic bug:
|
|
901
|
+
# https://github.com/pydantic/pydantic/issues/3765
|
|
902
|
+
# Because of the bug, our gen_schema_reference.py fails to determine the type of
|
|
903
|
+
# ServiceConfiguration.probes and insert the correct hyperlink.
|
|
904
|
+
raise ValueError("Probes must be unique")
|
|
905
|
+
return v
|
|
906
|
+
|
|
907
|
+
@validator("replicas")
|
|
908
|
+
def validate_replicas(
|
|
909
|
+
cls, v: Optional[Union[Range[int], List[ReplicaGroup]]]
|
|
910
|
+
) -> Optional[Union[Range[int], List[ReplicaGroup]]]:
|
|
911
|
+
if v is None:
|
|
912
|
+
return v
|
|
913
|
+
if isinstance(v, Range):
|
|
914
|
+
return _validate_replica_range(v)
|
|
915
|
+
|
|
916
|
+
if isinstance(v, list):
|
|
917
|
+
if not v:
|
|
918
|
+
raise ValueError("`replicas` cannot be an empty list")
|
|
919
|
+
|
|
920
|
+
# Assign default names to groups without names
|
|
921
|
+
for index, group in enumerate(v):
|
|
922
|
+
if group.name is None:
|
|
923
|
+
group.name = str(index)
|
|
924
|
+
|
|
925
|
+
# Check for duplicate names
|
|
926
|
+
names = [group.name for group in v]
|
|
927
|
+
if len(names) != len(set(names)):
|
|
928
|
+
duplicates = [name for name in set(names) if names.count(name) > 1]
|
|
929
|
+
raise ValueError(
|
|
930
|
+
f"Duplicate replica group names found: {duplicates}. "
|
|
931
|
+
"Each replica group must have a unique name."
|
|
932
|
+
)
|
|
933
|
+
return v
|
|
934
|
+
|
|
935
|
+
@root_validator()
|
|
936
|
+
def validate_scaling(cls, values):
|
|
937
|
+
scaling = values.get("scaling")
|
|
938
|
+
replicas = values.get("replicas")
|
|
939
|
+
|
|
940
|
+
if isinstance(replicas, Range):
|
|
941
|
+
if replicas and replicas.min != replicas.max and not scaling:
|
|
942
|
+
raise ValueError(
|
|
943
|
+
"When you set `replicas` to a range, ensure to specify `scaling`."
|
|
944
|
+
)
|
|
945
|
+
if replicas and replicas.min == replicas.max and scaling:
|
|
946
|
+
raise ValueError("To use `scaling`, `replicas` must be set to a range.")
|
|
947
|
+
return values
|
|
948
|
+
|
|
949
|
+
@root_validator()
|
|
950
|
+
def validate_top_level_properties_with_replica_groups(cls, values):
|
|
951
|
+
"""
|
|
952
|
+
When replicas is a list of ReplicaGroup, forbid top-level scaling, commands, and resources
|
|
953
|
+
"""
|
|
954
|
+
replicas = values.get("replicas")
|
|
955
|
+
|
|
956
|
+
if not isinstance(replicas, list):
|
|
957
|
+
return values
|
|
958
|
+
|
|
959
|
+
scaling = values.get("scaling")
|
|
960
|
+
if scaling is not None:
|
|
961
|
+
raise ValueError(
|
|
962
|
+
"Top-level `scaling` is not allowed when `replicas` is a list. "
|
|
963
|
+
"Specify `scaling` in each replica group instead."
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
commands = values.get("commands", [])
|
|
967
|
+
if commands:
|
|
968
|
+
raise ValueError(
|
|
969
|
+
"Top-level `commands` is not allowed when `replicas` is a list. "
|
|
970
|
+
"Specify `commands` in each replica group instead."
|
|
971
|
+
)
|
|
972
|
+
|
|
973
|
+
resources = values.get("resources")
|
|
974
|
+
|
|
975
|
+
default_resources = ResourcesSpec()
|
|
976
|
+
if resources and resources.dict() != default_resources.dict():
|
|
977
|
+
raise ValueError(
|
|
978
|
+
"Top-level `resources` is not allowed when `replicas` is a list. "
|
|
979
|
+
"Specify `resources` in each replica group instead."
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
return values
|
|
983
|
+
|
|
984
|
+
@root_validator()
|
|
985
|
+
def validate_replica_groups_have_commands_or_image(cls, values):
|
|
986
|
+
"""
|
|
987
|
+
When replicas is a list, ensure each ReplicaGroup has commands OR service has image.
|
|
988
|
+
"""
|
|
989
|
+
replicas = values.get("replicas")
|
|
990
|
+
image = values.get("image")
|
|
991
|
+
|
|
992
|
+
if not isinstance(replicas, list):
|
|
993
|
+
return values
|
|
994
|
+
|
|
995
|
+
for group in replicas:
|
|
996
|
+
if not group.commands and not image:
|
|
997
|
+
raise ValueError(
|
|
998
|
+
f"Replica group '{group.name}' has no commands. "
|
|
999
|
+
"Either set `commands` in the replica group or set `image` at the service level."
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
return values
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
class ServiceConfigurationConfig(
|
|
1006
|
+
ProfileParamsConfig,
|
|
1007
|
+
BaseRunConfigurationConfig,
|
|
1008
|
+
ServiceConfigurationParamsConfig,
|
|
1009
|
+
):
|
|
1010
|
+
@staticmethod
|
|
1011
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
1012
|
+
ProfileParamsConfig.schema_extra(schema)
|
|
1013
|
+
BaseRunConfigurationConfig.schema_extra(schema)
|
|
1014
|
+
ServiceConfigurationParamsConfig.schema_extra(schema)
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
class ServiceConfiguration(
|
|
1018
|
+
ProfileParams,
|
|
1019
|
+
BaseRunConfiguration,
|
|
1020
|
+
ConfigurationWithCommandsParams,
|
|
1021
|
+
ServiceConfigurationParams,
|
|
1022
|
+
generate_dual_core_model(ServiceConfigurationConfig),
|
|
1023
|
+
):
|
|
1024
|
+
type: Literal["service"] = "service"
|
|
1025
|
+
|
|
1026
|
+
@property
|
|
1027
|
+
def replica_groups(self) -> List[ReplicaGroup]:
|
|
1028
|
+
if self.replicas is None:
|
|
1029
|
+
return [
|
|
1030
|
+
ReplicaGroup(
|
|
1031
|
+
name=DEFAULT_REPLICA_GROUP_NAME,
|
|
1032
|
+
count=Range[int](min=1, max=1),
|
|
1033
|
+
commands=self.commands,
|
|
1034
|
+
resources=self.resources,
|
|
1035
|
+
scaling=self.scaling,
|
|
1036
|
+
)
|
|
1037
|
+
]
|
|
1038
|
+
if isinstance(self.replicas, list):
|
|
1039
|
+
return self.replicas
|
|
1040
|
+
if isinstance(self.replicas, Range):
|
|
1041
|
+
return [
|
|
1042
|
+
ReplicaGroup(
|
|
1043
|
+
name=DEFAULT_REPLICA_GROUP_NAME,
|
|
1044
|
+
count=self.replicas,
|
|
1045
|
+
commands=self.commands,
|
|
1046
|
+
resources=self.resources,
|
|
1047
|
+
scaling=self.scaling,
|
|
1048
|
+
)
|
|
1049
|
+
]
|
|
1050
|
+
raise ValueError(
|
|
1051
|
+
f"Invalid replicas type: {type(self.replicas)}. Expected None, Range[int], or List[ReplicaGroup]"
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
AnyRunConfiguration = Union[DevEnvironmentConfiguration, TaskConfiguration, ServiceConfiguration]
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
class RunConfiguration(CoreModel):
|
|
1059
|
+
__root__: Annotated[
|
|
1060
|
+
AnyRunConfiguration,
|
|
1061
|
+
Field(discriminator="type"),
|
|
1062
|
+
]
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
def parse_run_configuration(data: dict) -> AnyRunConfiguration:
|
|
1066
|
+
try:
|
|
1067
|
+
conf = RunConfiguration.parse_obj(data).__root__
|
|
1068
|
+
except ValidationError as e:
|
|
1069
|
+
raise ConfigurationError(e)
|
|
1070
|
+
return conf
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
class ApplyConfigurationType(str, Enum):
|
|
1074
|
+
DEV_ENVIRONMENT = "dev-environment"
|
|
1075
|
+
TASK = "task"
|
|
1076
|
+
SERVICE = "service"
|
|
1077
|
+
FLEET = "fleet"
|
|
1078
|
+
GATEWAY = "gateway"
|
|
1079
|
+
VOLUME = "volume"
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
AnyApplyConfiguration = Union[
|
|
1083
|
+
AnyRunConfiguration,
|
|
1084
|
+
FleetConfiguration,
|
|
1085
|
+
GatewayConfiguration,
|
|
1086
|
+
VolumeConfiguration,
|
|
1087
|
+
]
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
class ApplyConfiguration(CoreModel):
|
|
1091
|
+
__root__: Annotated[
|
|
1092
|
+
AnyApplyConfiguration,
|
|
1093
|
+
Field(discriminator="type"),
|
|
1094
|
+
]
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
def parse_apply_configuration(data: dict) -> AnyApplyConfiguration:
|
|
1098
|
+
try:
|
|
1099
|
+
conf = ApplyConfiguration.parse_obj(data).__root__
|
|
1100
|
+
except ValidationError as e:
|
|
1101
|
+
raise ConfigurationError(e)
|
|
1102
|
+
return conf
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
AnyDstackConfiguration = AnyApplyConfiguration
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
class DstackConfiguration(CoreModel):
|
|
1109
|
+
__root__: Annotated[
|
|
1110
|
+
AnyDstackConfiguration,
|
|
1111
|
+
Field(discriminator="type"),
|
|
1112
|
+
]
|
|
1113
|
+
|
|
1114
|
+
class Config(CoreConfig):
|
|
1115
|
+
json_loads = orjson.loads
|
|
1116
|
+
json_dumps = pydantic_orjson_dumps_with_indent
|
|
1117
|
+
|
|
1118
|
+
@staticmethod
|
|
1119
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
1120
|
+
schema["$schema"] = "http://json-schema.org/draft-07/schema#"
|
|
1121
|
+
# Allow additionalProperties so that vscode and others not supporting
|
|
1122
|
+
# top-level oneOf do not warn about properties being invalid.
|
|
1123
|
+
schema["additionalProperties"] = True
|