dstack 0.18.43__py3-none-any.whl → 0.19.0rc1__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/gateway.py +15 -3
- dstack/_internal/cli/commands/logs.py +0 -22
- dstack/_internal/cli/commands/stats.py +8 -17
- dstack/_internal/cli/main.py +1 -5
- dstack/_internal/cli/services/configurators/fleet.py +4 -39
- dstack/_internal/cli/services/configurators/run.py +22 -20
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- dstack/_internal/cli/utils/run.py +11 -0
- dstack/_internal/core/backends/__init__.py +56 -39
- dstack/_internal/core/backends/aws/__init__.py +0 -25
- dstack/_internal/core/backends/aws/auth.py +1 -10
- dstack/_internal/core/backends/aws/backend.py +26 -0
- dstack/_internal/core/backends/aws/compute.py +21 -45
- dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
- dstack/_internal/core/backends/aws/models.py +135 -0
- dstack/_internal/core/backends/aws/resources.py +1 -1
- dstack/_internal/core/backends/azure/__init__.py +0 -20
- dstack/_internal/core/backends/azure/auth.py +2 -11
- dstack/_internal/core/backends/azure/backend.py +21 -0
- dstack/_internal/core/backends/azure/compute.py +14 -28
- dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
- dstack/_internal/core/backends/azure/models.py +89 -0
- dstack/_internal/core/backends/base/__init__.py +0 -12
- dstack/_internal/core/backends/base/backend.py +18 -0
- dstack/_internal/core/backends/base/compute.py +153 -33
- dstack/_internal/core/backends/base/configurator.py +105 -0
- dstack/_internal/core/backends/base/models.py +14 -0
- dstack/_internal/core/backends/configurators.py +138 -0
- dstack/_internal/core/backends/cudo/__init__.py +0 -15
- dstack/_internal/core/backends/cudo/backend.py +16 -0
- dstack/_internal/core/backends/cudo/compute.py +8 -26
- dstack/_internal/core/backends/cudo/configurator.py +72 -0
- dstack/_internal/core/backends/cudo/models.py +37 -0
- dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
- dstack/_internal/core/backends/datacrunch/backend.py +16 -0
- dstack/_internal/core/backends/datacrunch/compute.py +8 -25
- dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
- dstack/_internal/core/backends/datacrunch/models.py +38 -0
- dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
- dstack/_internal/core/backends/gcp/__init__.py +0 -16
- dstack/_internal/core/backends/gcp/auth.py +2 -11
- dstack/_internal/core/backends/gcp/backend.py +17 -0
- dstack/_internal/core/backends/gcp/compute.py +14 -44
- dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
- dstack/_internal/core/backends/gcp/models.py +125 -0
- dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
- dstack/_internal/core/backends/kubernetes/backend.py +16 -0
- dstack/_internal/core/backends/kubernetes/compute.py +16 -5
- dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
- dstack/_internal/core/backends/kubernetes/models.py +72 -0
- dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
- dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
- dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
- dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
- dstack/_internal/core/backends/lambdalabs/models.py +37 -0
- dstack/_internal/core/backends/local/__init__.py +0 -13
- dstack/_internal/core/backends/local/backend.py +14 -0
- dstack/_internal/core/backends/local/compute.py +16 -2
- dstack/_internal/core/backends/models.py +128 -0
- dstack/_internal/core/backends/oci/__init__.py +0 -15
- dstack/_internal/core/backends/oci/auth.py +1 -5
- dstack/_internal/core/backends/oci/backend.py +16 -0
- dstack/_internal/core/backends/oci/compute.py +9 -23
- dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
- dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
- dstack/_internal/core/backends/oci/region.py +1 -1
- dstack/_internal/core/backends/runpod/__init__.py +0 -15
- dstack/_internal/core/backends/runpod/backend.py +16 -0
- dstack/_internal/core/backends/runpod/compute.py +28 -6
- dstack/_internal/core/backends/runpod/configurator.py +59 -0
- dstack/_internal/core/backends/runpod/models.py +54 -0
- dstack/_internal/core/backends/template/__init__.py +0 -0
- dstack/_internal/core/backends/tensordock/__init__.py +0 -15
- dstack/_internal/core/backends/tensordock/backend.py +16 -0
- dstack/_internal/core/backends/tensordock/compute.py +8 -27
- dstack/_internal/core/backends/tensordock/configurator.py +68 -0
- dstack/_internal/core/backends/tensordock/models.py +38 -0
- dstack/_internal/core/backends/vastai/__init__.py +0 -15
- dstack/_internal/core/backends/vastai/backend.py +16 -0
- dstack/_internal/core/backends/vastai/compute.py +2 -2
- dstack/_internal/core/backends/vastai/configurator.py +66 -0
- dstack/_internal/core/backends/vastai/models.py +37 -0
- dstack/_internal/core/backends/vultr/__init__.py +0 -15
- dstack/_internal/core/backends/vultr/backend.py +16 -0
- dstack/_internal/core/backends/vultr/compute.py +10 -24
- dstack/_internal/core/backends/vultr/configurator.py +64 -0
- dstack/_internal/core/backends/vultr/models.py +34 -0
- dstack/_internal/core/models/backends/__init__.py +0 -184
- dstack/_internal/core/models/backends/base.py +0 -19
- dstack/_internal/core/models/configurations.py +22 -16
- dstack/_internal/core/models/envs.py +4 -3
- dstack/_internal/core/models/fleets.py +17 -22
- dstack/_internal/core/models/gateways.py +3 -3
- dstack/_internal/core/models/instances.py +24 -0
- dstack/_internal/core/models/profiles.py +85 -45
- dstack/_internal/core/models/projects.py +1 -1
- dstack/_internal/core/models/repos/base.py +0 -5
- dstack/_internal/core/models/repos/local.py +3 -3
- dstack/_internal/core/models/repos/remote.py +26 -12
- dstack/_internal/core/models/repos/virtual.py +1 -1
- dstack/_internal/core/models/resources.py +45 -76
- dstack/_internal/core/models/runs.py +21 -19
- dstack/_internal/core/models/volumes.py +1 -3
- dstack/_internal/core/services/profiles.py +7 -16
- dstack/_internal/core/services/repos.py +0 -4
- dstack/_internal/server/app.py +11 -4
- dstack/_internal/server/background/__init__.py +10 -0
- dstack/_internal/server/background/tasks/process_gateways.py +4 -8
- dstack/_internal/server/background/tasks/process_instances.py +14 -9
- dstack/_internal/server/background/tasks/process_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_placement_groups.py +5 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +135 -0
- dstack/_internal/server/background/tasks/process_running_jobs.py +80 -24
- dstack/_internal/server/background/tasks/process_runs.py +1 -0
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +20 -38
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +59 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +31 -10
- dstack/_internal/server/routers/prometheus.py +36 -0
- dstack/_internal/server/routers/repos.py +1 -2
- dstack/_internal/server/routers/runs.py +13 -59
- dstack/_internal/server/schemas/gateways.py +14 -23
- dstack/_internal/server/schemas/projects.py +7 -2
- dstack/_internal/server/schemas/repos.py +2 -38
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/schemas/runs.py +1 -24
- dstack/_internal/server/security/permissions.py +1 -1
- dstack/_internal/server/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +53 -567
- dstack/_internal/server/services/fleets.py +9 -103
- dstack/_internal/server/services/gateways/__init__.py +13 -4
- dstack/_internal/server/services/{pools.py → instances.py} +22 -329
- dstack/_internal/server/services/jobs/__init__.py +9 -6
- dstack/_internal/server/services/jobs/configurators/base.py +25 -1
- dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
- dstack/_internal/server/services/metrics.py +131 -72
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +245 -0
- dstack/_internal/server/services/runner/client.py +14 -3
- dstack/_internal/server/services/runs.py +67 -31
- dstack/_internal/server/services/volumes.py +9 -4
- dstack/_internal/server/settings.py +3 -0
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js → main-4fd5a4770eff59325ee3.js} +68 -15
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js.map → main-4fd5a4770eff59325ee3.js.map} +1 -1
- dstack/_internal/server/statics/{main-7510e71dfa9749a4e70e.css → main-da9f8c06a69c20dac23e.css} +1 -1
- dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
- dstack/_internal/server/testing/common.py +75 -32
- dstack/_internal/utils/json_schema.py +6 -0
- dstack/_internal/utils/ssh.py +2 -1
- dstack/api/__init__.py +4 -0
- dstack/api/_public/__init__.py +16 -20
- dstack/api/_public/backends.py +1 -1
- dstack/api/_public/repos.py +36 -36
- dstack/api/_public/runs.py +170 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -55
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -96
- dstack/api/server/_volumes.py +10 -5
- dstack/api/utils.py +3 -0
- dstack/version.py +1 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/METADATA +10 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/RECORD +229 -206
- tests/_internal/cli/services/configurators/test_profile.py +6 -6
- tests/_internal/core/backends/aws/test_configurator.py +35 -0
- tests/_internal/core/backends/aws/test_resources.py +1 -1
- tests/_internal/core/backends/azure/test_configurator.py +61 -0
- tests/_internal/core/backends/cudo/__init__.py +0 -0
- tests/_internal/core/backends/cudo/test_configurator.py +37 -0
- tests/_internal/core/backends/datacrunch/__init__.py +0 -0
- tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
- tests/_internal/core/backends/gcp/test_configurator.py +42 -0
- tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
- tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
- tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
- tests/_internal/core/backends/oci/test_configurator.py +55 -0
- tests/_internal/core/backends/runpod/__init__.py +0 -0
- tests/_internal/core/backends/runpod/test_configurator.py +33 -0
- tests/_internal/core/backends/tensordock/__init__.py +0 -0
- tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
- tests/_internal/core/backends/vastai/__init__.py +0 -0
- tests/_internal/core/backends/vastai/test_configurator.py +33 -0
- tests/_internal/core/backends/vultr/__init__.py +0 -0
- tests/_internal/core/backends/vultr/test_configurator.py +33 -0
- tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
- tests/_internal/server/background/tasks/test_process_instances.py +49 -48
- tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
- tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +186 -0
- tests/_internal/server/background/tasks/test_process_running_jobs.py +123 -19
- tests/_internal/server/background/tasks/test_process_runs.py +8 -22
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
- tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
- tests/_internal/server/routers/test_backends.py +6 -764
- tests/_internal/server/routers/test_fleets.py +2 -26
- tests/_internal/server/routers/test_gateways.py +27 -3
- tests/_internal/server/routers/test_instances.py +0 -10
- tests/_internal/server/routers/test_metrics.py +42 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +333 -0
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +83 -275
- tests/_internal/server/routers/test_volumes.py +2 -3
- tests/_internal/server/services/backends/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
- tests/_internal/server/services/test_config.py +7 -4
- tests/_internal/server/services/test_fleets.py +1 -4
- tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
- tests/_internal/server/services/test_metrics.py +167 -0
- tests/_internal/server/services/test_repos.py +1 -14
- tests/_internal/server/services/test_runs.py +0 -4
- dstack/_internal/cli/commands/pool.py +0 -581
- dstack/_internal/cli/commands/run.py +0 -75
- dstack/_internal/core/backends/aws/config.py +0 -18
- dstack/_internal/core/backends/azure/config.py +0 -12
- dstack/_internal/core/backends/base/config.py +0 -5
- dstack/_internal/core/backends/cudo/config.py +0 -9
- dstack/_internal/core/backends/datacrunch/config.py +0 -9
- dstack/_internal/core/backends/gcp/config.py +0 -22
- dstack/_internal/core/backends/kubernetes/config.py +0 -6
- dstack/_internal/core/backends/lambdalabs/config.py +0 -9
- dstack/_internal/core/backends/nebius/__init__.py +0 -15
- dstack/_internal/core/backends/nebius/api_client.py +0 -319
- dstack/_internal/core/backends/nebius/compute.py +0 -220
- dstack/_internal/core/backends/nebius/config.py +0 -6
- dstack/_internal/core/backends/nebius/types.py +0 -37
- dstack/_internal/core/backends/oci/config.py +0 -6
- dstack/_internal/core/backends/runpod/config.py +0 -9
- dstack/_internal/core/backends/tensordock/config.py +0 -9
- dstack/_internal/core/backends/vastai/config.py +0 -6
- dstack/_internal/core/backends/vultr/config.py +0 -9
- dstack/_internal/core/models/backends/aws.py +0 -86
- dstack/_internal/core/models/backends/azure.py +0 -68
- dstack/_internal/core/models/backends/cudo.py +0 -43
- dstack/_internal/core/models/backends/datacrunch.py +0 -44
- dstack/_internal/core/models/backends/gcp.py +0 -67
- dstack/_internal/core/models/backends/kubernetes.py +0 -40
- dstack/_internal/core/models/backends/lambdalabs.py +0 -43
- dstack/_internal/core/models/backends/nebius.py +0 -54
- dstack/_internal/core/models/backends/runpod.py +0 -40
- dstack/_internal/core/models/backends/tensordock.py +0 -44
- dstack/_internal/core/models/backends/vastai.py +0 -43
- dstack/_internal/core/models/backends/vultr.py +0 -40
- dstack/_internal/core/models/pools.py +0 -43
- dstack/_internal/server/routers/pools.py +0 -142
- dstack/_internal/server/schemas/pools.py +0 -38
- dstack/_internal/server/services/backends/configurators/base.py +0 -72
- dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
- dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
- dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
- dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
- dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
- dstack/_internal/server/services/backends/configurators/runpod.py +0 -97
- dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
- dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
- dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
- dstack/api/_public/pools.py +0 -41
- dstack/api/_public/resources.py +0 -105
- dstack/api/server/_pools.py +0 -63
- tests/_internal/server/routers/test_pools.py +0 -612
- /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/WHEEL +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -1,29 +1,22 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import Dict, List, Literal, Optional, Union
|
|
1
|
+
from typing import List, Optional
|
|
3
2
|
|
|
4
3
|
import yaml
|
|
5
|
-
from pydantic import
|
|
4
|
+
from pydantic import Field, ValidationError
|
|
6
5
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
7
6
|
from typing_extensions import Annotated
|
|
8
7
|
|
|
8
|
+
import dstack._internal.core.backends.configurators
|
|
9
|
+
from dstack._internal.core.backends.models import (
|
|
10
|
+
AnyBackendConfigWithCreds,
|
|
11
|
+
AnyBackendFileConfigWithCreds,
|
|
12
|
+
BackendInfoYAML,
|
|
13
|
+
)
|
|
9
14
|
from dstack._internal.core.errors import (
|
|
10
15
|
BackendNotAvailable,
|
|
11
16
|
ResourceNotExistsError,
|
|
12
17
|
ServerClientError,
|
|
13
18
|
)
|
|
14
|
-
from dstack._internal.core.models.backends import AnyConfigInfoWithCreds, BackendInfoYAML
|
|
15
|
-
from dstack._internal.core.models.backends.aws import AnyAWSCreds, AWSOSImageConfig
|
|
16
|
-
from dstack._internal.core.models.backends.azure import AnyAzureCreds
|
|
17
19
|
from dstack._internal.core.models.backends.base import BackendType
|
|
18
|
-
from dstack._internal.core.models.backends.cudo import AnyCudoCreds
|
|
19
|
-
from dstack._internal.core.models.backends.datacrunch import AnyDataCrunchCreds
|
|
20
|
-
from dstack._internal.core.models.backends.kubernetes import KubernetesNetworkingConfig
|
|
21
|
-
from dstack._internal.core.models.backends.lambdalabs import AnyLambdaCreds
|
|
22
|
-
from dstack._internal.core.models.backends.oci import AnyOCICreds
|
|
23
|
-
from dstack._internal.core.models.backends.runpod import AnyRunpodCreds
|
|
24
|
-
from dstack._internal.core.models.backends.tensordock import AnyTensorDockCreds
|
|
25
|
-
from dstack._internal.core.models.backends.vastai import AnyVastAICreds
|
|
26
|
-
from dstack._internal.core.models.backends.vultr import AnyVultrCreds
|
|
27
20
|
from dstack._internal.core.models.common import CoreModel
|
|
28
21
|
from dstack._internal.server import settings
|
|
29
22
|
from dstack._internal.server.models import ProjectModel, UserModel
|
|
@@ -36,7 +29,6 @@ from dstack._internal.server.services.permissions import (
|
|
|
36
29
|
DefaultPermissions,
|
|
37
30
|
set_default_permissions,
|
|
38
31
|
)
|
|
39
|
-
from dstack._internal.utils.common import run_async
|
|
40
32
|
from dstack._internal.utils.logging import get_logger
|
|
41
33
|
|
|
42
34
|
logger = get_logger(__name__)
|
|
@@ -45,7 +37,7 @@ logger = get_logger(__name__)
|
|
|
45
37
|
# By default, PyYAML chooses the style of a collection depending on whether it has nested collections.
|
|
46
38
|
# If a collection has nested collections, it will be assigned the block style. Otherwise it will have the flow style.
|
|
47
39
|
#
|
|
48
|
-
# We want mapping to always be
|
|
40
|
+
# We want mapping to always be displayed in block-style but lists without nested objects in flow-style.
|
|
49
41
|
# So we define a custom representeter
|
|
50
42
|
|
|
51
43
|
|
|
@@ -57,464 +49,15 @@ def seq_representer(dumper, sequence):
|
|
|
57
49
|
yaml.add_representer(list, seq_representer)
|
|
58
50
|
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# credentials by looking for a file, while YAML-based API doesn't do this.
|
|
63
|
-
# So for some backends there are two sets of config models.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class AWSConfig(CoreModel):
|
|
67
|
-
type: Annotated[Literal["aws"], Field(description="The type of the backend")] = "aws"
|
|
68
|
-
regions: Annotated[
|
|
69
|
-
Optional[List[str]], Field(description="The list of AWS regions. Omit to use all regions")
|
|
70
|
-
] = None
|
|
71
|
-
vpc_name: Annotated[
|
|
72
|
-
Optional[str],
|
|
73
|
-
Field(
|
|
74
|
-
description=(
|
|
75
|
-
"The name of custom VPCs. All configured regions must have a VPC with this name."
|
|
76
|
-
" If your custom VPCs don't have names or have different names in different regions, use `vpc_ids` instead."
|
|
77
|
-
)
|
|
78
|
-
),
|
|
79
|
-
] = None
|
|
80
|
-
vpc_ids: Annotated[
|
|
81
|
-
Optional[Dict[str, str]],
|
|
82
|
-
Field(
|
|
83
|
-
description=(
|
|
84
|
-
"The mapping from AWS regions to VPC IDs."
|
|
85
|
-
" If `default_vpcs: true`, omitted regions will use default VPCs"
|
|
86
|
-
)
|
|
87
|
-
),
|
|
88
|
-
] = None
|
|
89
|
-
default_vpcs: Annotated[
|
|
90
|
-
Optional[bool],
|
|
91
|
-
Field(
|
|
92
|
-
description=(
|
|
93
|
-
"A flag to enable/disable using default VPCs in regions not configured by `vpc_ids`."
|
|
94
|
-
" Set to `false` if default VPCs should never be used."
|
|
95
|
-
" Defaults to `true`"
|
|
96
|
-
)
|
|
97
|
-
),
|
|
98
|
-
] = None
|
|
99
|
-
public_ips: Annotated[
|
|
100
|
-
Optional[bool],
|
|
101
|
-
Field(
|
|
102
|
-
description=(
|
|
103
|
-
"A flag to enable/disable public IP assigning on instances."
|
|
104
|
-
" `public_ips: false` requires at least one private subnet with outbound internet connectivity"
|
|
105
|
-
" provided by a NAT Gateway or a Transit Gateway."
|
|
106
|
-
" Defaults to `true`"
|
|
107
|
-
)
|
|
108
|
-
),
|
|
109
|
-
] = None
|
|
110
|
-
iam_instance_profile: Annotated[
|
|
111
|
-
Optional[str],
|
|
112
|
-
Field(
|
|
113
|
-
description=(
|
|
114
|
-
"The name of the IAM instance profile to associate with EC2 instances."
|
|
115
|
-
" You can also specify the IAM role name for roles created via the AWS console."
|
|
116
|
-
" AWS automatically creates an instance profile and gives it the same name as the role"
|
|
117
|
-
)
|
|
118
|
-
),
|
|
119
|
-
] = None
|
|
120
|
-
tags: Annotated[
|
|
121
|
-
Optional[Dict[str, str]],
|
|
122
|
-
Field(description="The tags that will be assigned to resources created by `dstack`"),
|
|
123
|
-
] = None
|
|
124
|
-
os_images: Annotated[
|
|
125
|
-
Optional[AWSOSImageConfig],
|
|
126
|
-
Field(
|
|
127
|
-
description="The mapping of instance categories (CPU, NVIDIA GPU) to AMI configurations"
|
|
128
|
-
),
|
|
129
|
-
] = None
|
|
130
|
-
creds: AnyAWSCreds = Field(..., description="The credentials", discriminator="type")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
class AzureConfig(CoreModel):
|
|
134
|
-
type: Annotated[Literal["azure"], Field(description="The type of the backend")] = "azure"
|
|
135
|
-
tenant_id: Annotated[str, Field(description="The tenant ID")]
|
|
136
|
-
subscription_id: Annotated[str, Field(description="The subscription ID")]
|
|
137
|
-
resource_group: Annotated[
|
|
138
|
-
Optional[str],
|
|
139
|
-
Field(
|
|
140
|
-
description=(
|
|
141
|
-
"The resource group for resources created by `dstack`."
|
|
142
|
-
" If not specified, `dstack` will create a new resource group"
|
|
143
|
-
)
|
|
144
|
-
),
|
|
145
|
-
] = None
|
|
146
|
-
regions: Annotated[
|
|
147
|
-
Optional[List[str]],
|
|
148
|
-
Field(description="The list of Azure regions (locations). Omit to use all regions"),
|
|
149
|
-
] = None
|
|
150
|
-
vpc_ids: Annotated[
|
|
151
|
-
Optional[Dict[str, str]],
|
|
152
|
-
Field(
|
|
153
|
-
description=(
|
|
154
|
-
"The mapping from configured Azure locations to network IDs."
|
|
155
|
-
" A network ID must have a format `networkResourceGroup/networkName`"
|
|
156
|
-
" If not specified, `dstack` will create a new network for every configured region"
|
|
157
|
-
)
|
|
158
|
-
),
|
|
159
|
-
] = None
|
|
160
|
-
public_ips: Annotated[
|
|
161
|
-
Optional[bool],
|
|
162
|
-
Field(
|
|
163
|
-
description=(
|
|
164
|
-
"A flag to enable/disable public IP assigning on instances."
|
|
165
|
-
" `public_ips: false` requires `vpc_ids` that specifies custom networks with outbound internet connectivity"
|
|
166
|
-
" provided by NAT Gateway or other mechanism."
|
|
167
|
-
" Defaults to `true`"
|
|
168
|
-
)
|
|
169
|
-
),
|
|
170
|
-
] = None
|
|
171
|
-
tags: Annotated[
|
|
172
|
-
Optional[Dict[str, str]],
|
|
173
|
-
Field(description="The tags that will be assigned to resources created by `dstack`"),
|
|
174
|
-
] = None
|
|
175
|
-
creds: AnyAzureCreds = Field(..., description="The credentials", discriminator="type")
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
class CudoConfig(CoreModel):
|
|
179
|
-
type: Annotated[Literal["cudo"], Field(description="The type of backend")] = "cudo"
|
|
180
|
-
regions: Annotated[
|
|
181
|
-
Optional[List[str]], Field(description="The list of Cudo regions. Omit to use all regions")
|
|
182
|
-
] = None
|
|
183
|
-
project_id: Annotated[str, Field(description="The project ID")]
|
|
184
|
-
creds: Annotated[AnyCudoCreds, Field(description="The credentials")]
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class DataCrunchConfig(CoreModel):
|
|
188
|
-
type: Annotated[Literal["datacrunch"], Field(description="The type of backend")] = "datacrunch"
|
|
189
|
-
regions: Annotated[
|
|
190
|
-
Optional[List[str]],
|
|
191
|
-
Field(description="The list of DataCrunch regions. Omit to use all regions"),
|
|
192
|
-
] = None
|
|
193
|
-
creds: Annotated[AnyDataCrunchCreds, Field(description="The credentials")]
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
class GCPServiceAccountCreds(CoreModel):
|
|
197
|
-
type: Annotated[Literal["service_account"], Field(description="The type of credentials")] = (
|
|
198
|
-
"service_account"
|
|
199
|
-
)
|
|
200
|
-
filename: Annotated[str, Field(description="The path to the service account file")]
|
|
201
|
-
data: Annotated[
|
|
202
|
-
Optional[str],
|
|
203
|
-
Field(
|
|
204
|
-
description=(
|
|
205
|
-
"The contents of the service account file."
|
|
206
|
-
" When configuring via `server/config.yml`, it's automatically filled from `filename`."
|
|
207
|
-
" When configuring via UI, it has to be specified explicitly"
|
|
208
|
-
)
|
|
209
|
-
),
|
|
210
|
-
] = None
|
|
211
|
-
|
|
212
|
-
@root_validator
|
|
213
|
-
def fill_data(cls, values):
|
|
214
|
-
return _fill_data(values)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
class GCPServiceAccountAPICreds(CoreModel):
|
|
218
|
-
type: Annotated[Literal["service_account"], Field(description="The type of credentials")] = (
|
|
219
|
-
"service_account"
|
|
220
|
-
)
|
|
221
|
-
filename: Annotated[
|
|
222
|
-
Optional[str], Field(description="The path to the service account file")
|
|
223
|
-
] = ""
|
|
224
|
-
data: Annotated[str, Field(description="The contents of the service account file")]
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
class GCPDefaultCreds(CoreModel):
|
|
228
|
-
type: Annotated[Literal["default"], Field(description="The type of credentials")] = "default"
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
AnyGCPCreds = Union[GCPServiceAccountCreds, GCPDefaultCreds]
|
|
232
|
-
AnyGCPAPICreds = Union[GCPServiceAccountAPICreds, GCPDefaultCreds]
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
class GCPConfig(CoreModel):
|
|
236
|
-
type: Annotated[Literal["gcp"], Field(description="The type of backend")] = "gcp"
|
|
237
|
-
project_id: Annotated[str, Field(description="The project ID")]
|
|
238
|
-
regions: Annotated[
|
|
239
|
-
Optional[List[str]], Field(description="The list of GCP regions. Omit to use all regions")
|
|
240
|
-
] = None
|
|
241
|
-
vpc_name: Annotated[Optional[str], Field(description="The name of a custom VPC")] = None
|
|
242
|
-
vpc_project_id: Annotated[
|
|
243
|
-
Optional[str],
|
|
244
|
-
Field(description="The shared VPC hosted project ID. Required for shared VPC only"),
|
|
245
|
-
] = None
|
|
246
|
-
public_ips: Annotated[
|
|
247
|
-
Optional[bool],
|
|
248
|
-
Field(
|
|
249
|
-
description="A flag to enable/disable public IP assigning on instances. Defaults to `true`"
|
|
250
|
-
),
|
|
251
|
-
] = None
|
|
252
|
-
nat_check: Annotated[
|
|
253
|
-
Optional[bool],
|
|
254
|
-
Field(
|
|
255
|
-
description=(
|
|
256
|
-
"A flag to enable/disable a check that Cloud NAT is configured for the VPC."
|
|
257
|
-
" This should be set to `false` when `public_ips: false` and outbound internet connectivity"
|
|
258
|
-
" is provided by a mechanism other than Cloud NAT such as a third-party NAT appliance."
|
|
259
|
-
" Defaults to `true`"
|
|
260
|
-
)
|
|
261
|
-
),
|
|
262
|
-
] = None
|
|
263
|
-
vm_service_account: Annotated[
|
|
264
|
-
Optional[str], Field(description="The service account to associate with provisioned VMs")
|
|
265
|
-
] = None
|
|
266
|
-
tags: Annotated[
|
|
267
|
-
Optional[Dict[str, str]],
|
|
268
|
-
Field(
|
|
269
|
-
description="The tags (labels) that will be assigned to resources created by `dstack`"
|
|
270
|
-
),
|
|
271
|
-
] = None
|
|
272
|
-
creds: AnyGCPCreds = Field(..., description="The credentials", discriminator="type")
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
class GCPAPIConfig(CoreModel):
|
|
276
|
-
type: Annotated[Literal["gcp"], Field(description="The type of backend")] = "gcp"
|
|
277
|
-
project_id: Annotated[str, Field(description="The project ID")]
|
|
278
|
-
regions: Annotated[
|
|
279
|
-
Optional[List[str]], Field(description="The list of GCP regions. Omit to use all regions")
|
|
280
|
-
] = None
|
|
281
|
-
vpc_name: Annotated[Optional[str], Field(description="The name of a custom VPC")] = None
|
|
282
|
-
vpc_project_id: Annotated[
|
|
283
|
-
Optional[str],
|
|
284
|
-
Field(description="The shared VPC hosted project ID. Required for shared VPC only"),
|
|
285
|
-
] = None
|
|
286
|
-
public_ips: Annotated[
|
|
287
|
-
Optional[bool],
|
|
288
|
-
Field(
|
|
289
|
-
description="A flag to enable/disable public IP assigning on instances. Defaults to `true`"
|
|
290
|
-
),
|
|
291
|
-
] = None
|
|
292
|
-
nat_check: Annotated[
|
|
293
|
-
Optional[bool],
|
|
294
|
-
Field(
|
|
295
|
-
description=(
|
|
296
|
-
"A flag to enable/disable a check that Cloud NAT is configured for the VPC."
|
|
297
|
-
" This should be set to `false` when `public_ips: false` and outbound internet connectivity"
|
|
298
|
-
" is provided by a mechanism other than Cloud NAT such as a third-party NAT appliance."
|
|
299
|
-
" Defaults to `true`"
|
|
300
|
-
)
|
|
301
|
-
),
|
|
302
|
-
] = None
|
|
303
|
-
vm_service_account: Annotated[
|
|
304
|
-
Optional[str], Field(description="The service account associated with provisioned VMs")
|
|
305
|
-
] = None
|
|
306
|
-
tags: Annotated[
|
|
307
|
-
Optional[Dict[str, str]],
|
|
308
|
-
Field(
|
|
309
|
-
description="The tags (labels) that will be assigned to resources created by `dstack`"
|
|
310
|
-
),
|
|
311
|
-
] = None
|
|
312
|
-
creds: AnyGCPAPICreds = Field(..., description="The credentials", discriminator="type")
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
class KubeconfigConfig(CoreModel):
|
|
316
|
-
filename: Annotated[str, Field(description="The path to the kubeconfig file")]
|
|
317
|
-
data: Annotated[
|
|
318
|
-
Optional[str],
|
|
319
|
-
Field(
|
|
320
|
-
description=(
|
|
321
|
-
"The contents of the kubeconfig file."
|
|
322
|
-
" When configuring via `server/config.yml`, it's automatically filled from `filename`."
|
|
323
|
-
" When configuring via UI, it has to be specified explicitly"
|
|
324
|
-
)
|
|
325
|
-
),
|
|
326
|
-
] = None
|
|
327
|
-
|
|
328
|
-
@root_validator
|
|
329
|
-
def fill_data(cls, values):
|
|
330
|
-
return _fill_data(values)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
class KubeconfigAPIConfig(CoreModel):
|
|
334
|
-
filename: Annotated[str, Field(description="The path to the kubeconfig file")] = ""
|
|
335
|
-
data: Annotated[str, Field(description="The contents of the kubeconfig file")]
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
class KubernetesConfig(CoreModel):
|
|
339
|
-
type: Annotated[Literal["kubernetes"], Field(description="The type of backend")] = "kubernetes"
|
|
340
|
-
kubeconfig: Annotated[KubeconfigConfig, Field(description="The kubeconfig configuration")]
|
|
341
|
-
networking: Annotated[
|
|
342
|
-
Optional[KubernetesNetworkingConfig], Field(description="The networking configuration")
|
|
343
|
-
]
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
class KubernetesAPIConfig(CoreModel):
|
|
347
|
-
type: Annotated[Literal["kubernetes"], Field(description="The type of backend")] = "kubernetes"
|
|
348
|
-
kubeconfig: Annotated[KubeconfigAPIConfig, Field(description="The kubeconfig configuration")]
|
|
349
|
-
networking: Annotated[
|
|
350
|
-
Optional[KubernetesNetworkingConfig], Field(description="The networking configuration")
|
|
351
|
-
]
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
class LambdaConfig(CoreModel):
|
|
355
|
-
type: Annotated[Literal["lambda"], Field(description="The type of backend")] = "lambda"
|
|
356
|
-
regions: Annotated[
|
|
357
|
-
Optional[List[str]],
|
|
358
|
-
Field(description="The list of Lambda regions. Omit to use all regions"),
|
|
359
|
-
] = None
|
|
360
|
-
creds: Annotated[AnyLambdaCreds, Field(description="The credentials")]
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
class NebiusServiceAccountCreds(CoreModel):
|
|
364
|
-
type: Annotated[Literal["service_account"], Field(description="The type of credentials")] = (
|
|
365
|
-
"service_account"
|
|
366
|
-
)
|
|
367
|
-
filename: Annotated[str, Field(description="The path to the service account file")]
|
|
368
|
-
data: Annotated[
|
|
369
|
-
Optional[str], Field(description="The contents of the service account file")
|
|
370
|
-
] = None
|
|
371
|
-
|
|
372
|
-
@root_validator
|
|
373
|
-
def fill_data(cls, values):
|
|
374
|
-
return _fill_data(values)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
class NebiusServiceAccountAPICreds(CoreModel):
|
|
378
|
-
type: Annotated[Literal["service_account"], Field(description="The type of credentials")] = (
|
|
379
|
-
"service_account"
|
|
380
|
-
)
|
|
381
|
-
filename: Annotated[str, Field(description="The path to the service account file")]
|
|
382
|
-
data: Annotated[str, Field(description="The contents of the service account file")]
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
AnyNebiusCreds = NebiusServiceAccountCreds
|
|
386
|
-
AnyNebiusAPICreds = NebiusServiceAccountAPICreds
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
class NebiusConfig(CoreModel):
|
|
390
|
-
type: Literal["nebius"] = "nebius"
|
|
391
|
-
cloud_id: str
|
|
392
|
-
folder_id: str
|
|
393
|
-
network_id: str
|
|
394
|
-
regions: Optional[List[str]] = None
|
|
395
|
-
creds: AnyNebiusCreds
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
class NebiusAPIConfig(CoreModel):
|
|
399
|
-
type: Literal["nebius"] = "nebius"
|
|
400
|
-
cloud_id: str
|
|
401
|
-
folder_id: str
|
|
402
|
-
network_id: str
|
|
403
|
-
regions: Optional[List[str]] = None
|
|
404
|
-
creds: AnyNebiusAPICreds
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
class OCIConfig(CoreModel):
|
|
408
|
-
type: Annotated[Literal["oci"], Field(description="The type of backend")] = "oci"
|
|
409
|
-
creds: Annotated[AnyOCICreds, Field(description="The credentials", discriminator="type")]
|
|
410
|
-
regions: Annotated[
|
|
411
|
-
Optional[List[str]],
|
|
412
|
-
Field(description="The list of OCI regions. Omit to use all regions"),
|
|
413
|
-
] = None
|
|
414
|
-
compartment_id: Annotated[
|
|
415
|
-
Optional[str],
|
|
416
|
-
Field(
|
|
417
|
-
description=(
|
|
418
|
-
"Compartment where `dstack` will create all resources."
|
|
419
|
-
" Omit to instruct `dstack` to create a new compartment"
|
|
420
|
-
)
|
|
421
|
-
),
|
|
422
|
-
] = None
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
class RunpodConfig(CoreModel):
|
|
426
|
-
type: Literal["runpod"] = "runpod"
|
|
427
|
-
regions: Annotated[
|
|
428
|
-
Optional[List[str]],
|
|
429
|
-
Field(description="The list of RunPod regions. Omit to use all regions"),
|
|
430
|
-
] = None
|
|
431
|
-
creds: Annotated[AnyRunpodCreds, Field(description="The credentials")]
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
class TensorDockConfig(CoreModel):
|
|
435
|
-
type: Annotated[Literal["tensordock"], Field(description="The type of backend")] = "tensordock"
|
|
436
|
-
regions: Annotated[
|
|
437
|
-
Optional[List[str]],
|
|
438
|
-
Field(description="The list of TensorDock regions. Omit to use all regions"),
|
|
439
|
-
] = None
|
|
440
|
-
creds: Annotated[AnyTensorDockCreds, Field(description="The credentials")]
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
class VastAIConfig(CoreModel):
|
|
444
|
-
type: Annotated[Literal["vastai"], Field(description="The type of backend")] = "vastai"
|
|
445
|
-
regions: Annotated[
|
|
446
|
-
Optional[List[str]],
|
|
447
|
-
Field(description="The list of VastAI regions. Omit to use all regions"),
|
|
448
|
-
] = None
|
|
449
|
-
creds: Annotated[AnyVastAICreds, Field(description="The credentials")]
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
class VultrConfig(CoreModel):
|
|
453
|
-
type: Annotated[Literal["vultr"], Field(description="The type of backend")] = "vultr"
|
|
454
|
-
regions: Annotated[
|
|
455
|
-
Optional[List[str]],
|
|
456
|
-
Field(description="The list of Vultr regions. Omit to use all regions"),
|
|
457
|
-
] = None
|
|
458
|
-
creds: Annotated[AnyVultrCreds, Field(description="The credentials")]
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
class DstackConfig(CoreModel):
|
|
462
|
-
type: Annotated[Literal["dstack"], Field(description="The type of backend")] = "dstack"
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
AnyBackendConfig = Union[
|
|
466
|
-
AWSConfig,
|
|
467
|
-
AzureConfig,
|
|
468
|
-
CudoConfig,
|
|
469
|
-
DataCrunchConfig,
|
|
470
|
-
GCPConfig,
|
|
471
|
-
KubernetesConfig,
|
|
472
|
-
LambdaConfig,
|
|
473
|
-
NebiusConfig,
|
|
474
|
-
OCIConfig,
|
|
475
|
-
RunpodConfig,
|
|
476
|
-
TensorDockConfig,
|
|
477
|
-
VastAIConfig,
|
|
478
|
-
VultrConfig,
|
|
479
|
-
DstackConfig,
|
|
52
|
+
BackendFileConfigWithCreds = Annotated[
|
|
53
|
+
AnyBackendFileConfigWithCreds, Field(..., discriminator="type")
|
|
480
54
|
]
|
|
481
55
|
|
|
482
|
-
BackendConfig = Annotated[AnyBackendConfig, Field(..., discriminator="type")]
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
class _BackendConfig(BaseModel):
|
|
486
|
-
__root__: BackendConfig
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
AnyBackendAPIConfig = Union[
|
|
490
|
-
AWSConfig,
|
|
491
|
-
AzureConfig,
|
|
492
|
-
CudoConfig,
|
|
493
|
-
DataCrunchConfig,
|
|
494
|
-
GCPAPIConfig,
|
|
495
|
-
KubernetesAPIConfig,
|
|
496
|
-
LambdaConfig,
|
|
497
|
-
NebiusAPIConfig,
|
|
498
|
-
OCIConfig,
|
|
499
|
-
RunpodConfig,
|
|
500
|
-
TensorDockConfig,
|
|
501
|
-
VastAIConfig,
|
|
502
|
-
VultrConfig,
|
|
503
|
-
DstackConfig,
|
|
504
|
-
]
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
BackendAPIConfig = Annotated[AnyBackendAPIConfig, Field(..., discriminator="type")]
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
class _BackendAPIConfig(CoreModel):
|
|
511
|
-
__root__: BackendAPIConfig
|
|
512
|
-
|
|
513
56
|
|
|
514
57
|
class ProjectConfig(CoreModel):
|
|
515
58
|
name: Annotated[str, Field(description="The name of the project")]
|
|
516
59
|
backends: Annotated[
|
|
517
|
-
Optional[List[
|
|
60
|
+
Optional[List[BackendFileConfigWithCreds]], Field(description="The list of backends")
|
|
518
61
|
] = None
|
|
519
62
|
|
|
520
63
|
|
|
@@ -545,19 +88,13 @@ class ServerConfigManager:
|
|
|
545
88
|
Initializes the default server/config.yml.
|
|
546
89
|
The default config is empty or contains an existing `main` project config.
|
|
547
90
|
"""
|
|
548
|
-
|
|
549
|
-
# so that the backend configuration is always explicit.
|
|
550
|
-
# Details: https://github.com/dstackai/dstack/issues/1384
|
|
551
|
-
self.config = await self._init_config(session=session, init_backends=False)
|
|
91
|
+
self.config = await self._init_config(session)
|
|
552
92
|
if self.config is not None:
|
|
553
93
|
self._save_config(self.config)
|
|
554
94
|
|
|
555
95
|
async def sync_config(self, session: AsyncSession):
|
|
556
96
|
# Disable config.yml sync for https://github.com/dstackai/dstack/issues/815.
|
|
557
97
|
return
|
|
558
|
-
# self.config = await self._init_config(session=session, init_backends=False)
|
|
559
|
-
# if self.config is not None:
|
|
560
|
-
# self._save_config(self.config)
|
|
561
98
|
|
|
562
99
|
async def apply_encryption(self):
|
|
563
100
|
if self.config is None:
|
|
@@ -593,13 +130,15 @@ class ServerConfigManager:
|
|
|
593
130
|
project = await projects_services.get_project_model_by_name_or_error(
|
|
594
131
|
session=session, project_name=project_config.name
|
|
595
132
|
)
|
|
596
|
-
backends_to_delete = set(
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
133
|
+
backends_to_delete = set(
|
|
134
|
+
dstack._internal.core.backends.configurators.list_available_backend_types()
|
|
135
|
+
)
|
|
136
|
+
for backend_file_config in project_config.backends or []:
|
|
137
|
+
backend_config = file_config_to_config(backend_file_config)
|
|
138
|
+
backend_type = BackendType(backend_config.type)
|
|
600
139
|
backends_to_delete.difference_update([backend_type])
|
|
601
140
|
try:
|
|
602
|
-
|
|
141
|
+
current_backend_config = await backends_services.get_backend_config(
|
|
603
142
|
project=project,
|
|
604
143
|
backend_type=backend_type,
|
|
605
144
|
)
|
|
@@ -610,23 +149,23 @@ class ServerConfigManager:
|
|
|
610
149
|
backend_type.value,
|
|
611
150
|
)
|
|
612
151
|
continue
|
|
613
|
-
if
|
|
152
|
+
if backend_config == current_backend_config:
|
|
614
153
|
continue
|
|
615
154
|
backend_exists = any(backend_type == b.type for b in project.backends)
|
|
616
155
|
try:
|
|
617
|
-
#
|
|
156
|
+
# current_backend_config may be None if backend exists
|
|
618
157
|
# but it's config is invalid (e.g. cannot be decrypted).
|
|
619
158
|
# Update backend in this case.
|
|
620
|
-
if
|
|
159
|
+
if current_backend_config is None and not backend_exists:
|
|
621
160
|
await backends_services.create_backend(
|
|
622
|
-
session=session, project=project, config=
|
|
161
|
+
session=session, project=project, config=backend_config
|
|
623
162
|
)
|
|
624
163
|
else:
|
|
625
164
|
await backends_services.update_backend(
|
|
626
|
-
session=session, project=project, config=
|
|
165
|
+
session=session, project=project, config=backend_config
|
|
627
166
|
)
|
|
628
167
|
except Exception as e:
|
|
629
|
-
logger.warning("Failed to configure backend %s: %s",
|
|
168
|
+
logger.warning("Failed to configure backend %s: %s", backend_config.type, e)
|
|
630
169
|
await delete_backends_safe(
|
|
631
170
|
session=session,
|
|
632
171
|
project=project,
|
|
@@ -634,9 +173,7 @@ class ServerConfigManager:
|
|
|
634
173
|
error=False,
|
|
635
174
|
)
|
|
636
175
|
|
|
637
|
-
async def _init_config(
|
|
638
|
-
self, session: AsyncSession, init_backends: bool
|
|
639
|
-
) -> Optional[ServerConfig]:
|
|
176
|
+
async def _init_config(self, session: AsyncSession) -> Optional[ServerConfig]:
|
|
640
177
|
project = await projects_services.get_project_model_by_name(
|
|
641
178
|
session=session,
|
|
642
179
|
project_name=settings.DEFAULT_PROJECT_NAME,
|
|
@@ -646,40 +183,20 @@ class ServerConfigManager:
|
|
|
646
183
|
# Force project reload to reflect updates when syncing
|
|
647
184
|
await session.refresh(project)
|
|
648
185
|
backends = []
|
|
649
|
-
for
|
|
650
|
-
|
|
186
|
+
for (
|
|
187
|
+
backend_type
|
|
188
|
+
) in dstack._internal.core.backends.configurators.list_available_backend_types():
|
|
189
|
+
backend_config = await backends_services.get_backend_config(
|
|
651
190
|
project=project, backend_type=backend_type
|
|
652
191
|
)
|
|
653
|
-
if
|
|
654
|
-
backends.append(
|
|
655
|
-
if init_backends and len(backends) == 0:
|
|
656
|
-
backends = await self._init_backends(session=session, project=project)
|
|
192
|
+
if backend_config is not None:
|
|
193
|
+
backends.append(backend_config)
|
|
657
194
|
return ServerConfig(
|
|
658
195
|
projects=[ProjectConfig(name=settings.DEFAULT_PROJECT_NAME, backends=backends)],
|
|
659
196
|
encryption=EncryptionConfig(keys=[]),
|
|
660
197
|
default_permissions=None,
|
|
661
198
|
)
|
|
662
199
|
|
|
663
|
-
async def _init_backends(
|
|
664
|
-
self, session: AsyncSession, project: ProjectModel
|
|
665
|
-
) -> List[BackendConfig]:
|
|
666
|
-
backends = []
|
|
667
|
-
for backend_type in backends_services.list_available_backend_types():
|
|
668
|
-
configurator = backends_services.get_configurator(backend_type)
|
|
669
|
-
if configurator is None:
|
|
670
|
-
continue
|
|
671
|
-
config_infos = await run_async(configurator.get_default_configs)
|
|
672
|
-
for config_info in config_infos:
|
|
673
|
-
try:
|
|
674
|
-
await backends_services.create_backend(
|
|
675
|
-
session=session, project=project, config=config_info
|
|
676
|
-
)
|
|
677
|
-
backends.append(internal_config_to_config(config_info))
|
|
678
|
-
break
|
|
679
|
-
except Exception as e:
|
|
680
|
-
logger.debug("Failed to configure backend %s: %s", config_info.type, e)
|
|
681
|
-
return backends
|
|
682
|
-
|
|
683
200
|
def _load_config(self) -> Optional[ServerConfig]:
|
|
684
201
|
try:
|
|
685
202
|
with open(settings.SERVER_CONFIG_FILE_PATH) as f:
|
|
@@ -697,13 +214,12 @@ class ServerConfigManager:
|
|
|
697
214
|
async def get_backend_config_yaml(
|
|
698
215
|
project: ProjectModel, backend_type: BackendType
|
|
699
216
|
) -> BackendInfoYAML:
|
|
700
|
-
|
|
217
|
+
backend_config = await backends_services.get_backend_config(
|
|
701
218
|
project=project, backend_type=backend_type
|
|
702
219
|
)
|
|
703
|
-
if
|
|
220
|
+
if backend_config is None:
|
|
704
221
|
raise ResourceNotExistsError()
|
|
705
|
-
|
|
706
|
-
config_yaml = config_to_yaml(config)
|
|
222
|
+
config_yaml = config_to_yaml(backend_config)
|
|
707
223
|
return BackendInfoYAML(
|
|
708
224
|
name=backend_type,
|
|
709
225
|
config_yaml=config_yaml,
|
|
@@ -715,9 +231,8 @@ async def create_backend_config_yaml(
|
|
|
715
231
|
project: ProjectModel,
|
|
716
232
|
config_yaml: str,
|
|
717
233
|
):
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
await backends_services.create_backend(session=session, project=project, config=config_info)
|
|
234
|
+
config = config_yaml_to_backend_config(config_yaml)
|
|
235
|
+
await backends_services.create_backend(session=session, project=project, config=config)
|
|
721
236
|
|
|
722
237
|
|
|
723
238
|
async def update_backend_config_yaml(
|
|
@@ -725,64 +240,35 @@ async def update_backend_config_yaml(
|
|
|
725
240
|
project: ProjectModel,
|
|
726
241
|
config_yaml: str,
|
|
727
242
|
):
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
await backends_services.update_backend(session=session, project=project, config=config_info)
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
server_config_manager = ServerConfigManager()
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
def internal_config_to_config(config_info: AnyConfigInfoWithCreds) -> BackendConfig:
|
|
737
|
-
backend_config = _BackendConfig.parse_obj(config_info.dict(exclude={"locations"}))
|
|
738
|
-
if config_info.type == "azure":
|
|
739
|
-
backend_config.__root__.regions = config_info.locations
|
|
740
|
-
return backend_config.__root__
|
|
741
|
-
|
|
243
|
+
config = config_yaml_to_backend_config(config_yaml)
|
|
244
|
+
await backends_services.update_backend(session=session, project=project, config=config)
|
|
742
245
|
|
|
743
|
-
class _ConfigInfoWithCreds(CoreModel):
|
|
744
|
-
__root__: Annotated[AnyConfigInfoWithCreds, Field(..., discriminator="type")]
|
|
745
246
|
|
|
247
|
+
class _BackendConfigWithCreds(CoreModel):
|
|
248
|
+
"""
|
|
249
|
+
Model for parsing API and file YAML configs.
|
|
250
|
+
"""
|
|
746
251
|
|
|
747
|
-
|
|
748
|
-
backend_config: Union[BackendConfig, BackendAPIConfig],
|
|
749
|
-
) -> AnyConfigInfoWithCreds:
|
|
750
|
-
backend_config_dict = backend_config.dict()
|
|
751
|
-
# Allow to not specify networking
|
|
752
|
-
if backend_config.type == "kubernetes":
|
|
753
|
-
if backend_config.networking is None:
|
|
754
|
-
backend_config_dict["networking"] = {}
|
|
755
|
-
if backend_config.type == "azure":
|
|
756
|
-
backend_config_dict["locations"] = backend_config_dict["regions"]
|
|
757
|
-
del backend_config_dict["regions"]
|
|
758
|
-
config_info = _ConfigInfoWithCreds.parse_obj(backend_config_dict)
|
|
759
|
-
return config_info.__root__
|
|
252
|
+
__root__: Annotated[AnyBackendConfigWithCreds, Field(..., discriminator="type")]
|
|
760
253
|
|
|
761
254
|
|
|
762
|
-
def config_yaml_to_backend_config(config_yaml: str) ->
|
|
255
|
+
def config_yaml_to_backend_config(config_yaml: str) -> AnyBackendConfigWithCreds:
|
|
763
256
|
try:
|
|
764
257
|
config_dict = yaml.load(config_yaml, yaml.FullLoader)
|
|
765
258
|
except yaml.YAMLError:
|
|
766
259
|
raise ServerClientError("Error parsing YAML")
|
|
767
260
|
try:
|
|
768
|
-
backend_config =
|
|
261
|
+
backend_config = _BackendConfigWithCreds.parse_obj(config_dict).__root__
|
|
769
262
|
except ValidationError as e:
|
|
770
263
|
raise ServerClientError(str(e))
|
|
771
264
|
return backend_config
|
|
772
265
|
|
|
773
266
|
|
|
774
|
-
def
|
|
775
|
-
|
|
267
|
+
def file_config_to_config(file_config: AnyBackendFileConfigWithCreds) -> AnyBackendConfigWithCreds:
|
|
268
|
+
backend_config_dict = file_config.dict()
|
|
269
|
+
backend_config = _BackendConfigWithCreds.parse_obj(backend_config_dict)
|
|
270
|
+
return backend_config.__root__
|
|
776
271
|
|
|
777
272
|
|
|
778
|
-
def
|
|
779
|
-
|
|
780
|
-
return values
|
|
781
|
-
if "filename" not in values:
|
|
782
|
-
raise ValueError()
|
|
783
|
-
try:
|
|
784
|
-
with open(Path(values["filename"]).expanduser()) as f:
|
|
785
|
-
values["data"] = f.read()
|
|
786
|
-
except OSError:
|
|
787
|
-
raise ValueError(f"No such file {values['filename']}")
|
|
788
|
-
return values
|
|
273
|
+
def config_to_yaml(config: CoreModel) -> str:
|
|
274
|
+
return yaml.dump(config.dict(exclude_none=True), sort_keys=False)
|