Skip to content

Commit 3f54a50

Browse files
authored
Merge pull request #2392 from dstackai/issue_2372_backend_features
Introduce ComputeWith classes to detect compute features
2 parents b2a31ca + 0f9417b commit 3f54a50

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+450
-150
lines changed

contributing/BACKENDS.md

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,8 @@ Refer to examples:
115115
##### 2.4.4. Create the backend compute class
116116

117117
Under the backend directory you've created, create the `compute.py` file and define the
118-
backend compute class there (should extend `dstack._internal.core.backends.base.compute.Compute`).
119-
120-
You'll have to implement `get_offers`, `run_job` and `terminate_instance`.
121-
You may need to implement `update_provisioning_data`, see its docstring for details.
122-
123-
For VM-based backends, also implement the `create_instance` method and add the backend name to
124-
[`BACKENDS_WITH_CREATE_INSTANCE_SUPPORT`](`https://github.com/dstackai/dstack/blob/master/src/dstack/_internal/core/backends/__init__.py`).
118+
backend compute class that extends the `dstack._internal.core.backends.base.compute.Compute` class.
119+
It can also extend and implement `ComputeWith*` classes to support additional features such as fleets, volumes, gateways, placement groups, etc. For example, it should extend `ComputeWithCreateInstanceSupport` to support fleets.
125120

126121
Refer to examples:
127122
[datacrunch](https://github.com/dstackai/dstack/blob/master/src/dstack/_internal/core/backends/datacrunch/compute.py),
Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,59 @@
1+
from dstack._internal.core.backends.base.compute import (
2+
ComputeWithCreateInstanceSupport,
3+
ComputeWithGatewaySupport,
4+
ComputeWithMultinodeSupport,
5+
ComputeWithPlacementGroupSupport,
6+
ComputeWithPrivateGatewaySupport,
7+
ComputeWithReservationSupport,
8+
ComputeWithVolumeSupport,
9+
)
10+
from dstack._internal.core.backends.base.configurator import Configurator
11+
from dstack._internal.core.backends.configurators import list_available_configurator_classes
112
from dstack._internal.core.models.backends.base import BackendType
213

3-
BACKENDS_WITH_MULTINODE_SUPPORT = [
4-
BackendType.AWS,
5-
BackendType.AZURE,
6-
BackendType.GCP,
7-
BackendType.REMOTE,
8-
BackendType.OCI,
9-
BackendType.VULTR,
10-
]
11-
BACKENDS_WITH_CREATE_INSTANCE_SUPPORT = [
12-
BackendType.AWS,
13-
BackendType.DSTACK,
14-
BackendType.AZURE,
15-
BackendType.CUDO,
16-
BackendType.DATACRUNCH,
17-
BackendType.GCP,
18-
BackendType.LAMBDA,
19-
BackendType.OCI,
20-
BackendType.TENSORDOCK,
21-
BackendType.VULTR,
22-
]
23-
BACKENDS_WITH_PLACEMENT_GROUPS_SUPPORT = [
24-
BackendType.AWS,
25-
]
26-
BACKENDS_WITH_RESERVATION_SUPPORT = [
27-
BackendType.AWS,
28-
]
2914

30-
BACKENDS_WITH_GATEWAY_SUPPORT = [
31-
BackendType.AWS,
32-
BackendType.AZURE,
33-
BackendType.GCP,
34-
BackendType.KUBERNETES,
35-
]
36-
BACKENDS_WITH_PRIVATE_GATEWAY_SUPPORT = [BackendType.AWS]
37-
BACKENDS_WITH_VOLUMES_SUPPORT = [
38-
BackendType.AWS,
39-
BackendType.GCP,
40-
BackendType.LOCAL,
41-
BackendType.RUNPOD,
42-
]
15+
def _get_backends_with_compute_feature(
16+
configurator_classes: list[type[Configurator]],
17+
compute_feature_class: type,
18+
) -> list[BackendType]:
19+
backend_types = []
20+
for configurator_class in configurator_classes:
21+
compute_class = configurator_class.BACKEND_CLASS.COMPUTE_CLASS
22+
if issubclass(compute_class, compute_feature_class):
23+
backend_types.append(configurator_class.TYPE)
24+
return backend_types
25+
26+
27+
_configurator_classes = list_available_configurator_classes()
28+
29+
30+
# The following backend lists do not include unavailable backends (i.e. backends missing deps).
31+
# TODO: Add LocalBackend to lists if it's enabled
32+
BACKENDS_WITH_CREATE_INSTANCE_SUPPORT = _get_backends_with_compute_feature(
33+
configurator_classes=_configurator_classes,
34+
compute_feature_class=ComputeWithCreateInstanceSupport,
35+
)
36+
BACKENDS_WITH_MULTINODE_SUPPORT = [BackendType.REMOTE] + _get_backends_with_compute_feature(
37+
configurator_classes=_configurator_classes,
38+
compute_feature_class=ComputeWithMultinodeSupport,
39+
)
40+
BACKENDS_WITH_PLACEMENT_GROUPS_SUPPORT = _get_backends_with_compute_feature(
41+
configurator_classes=_configurator_classes,
42+
compute_feature_class=ComputeWithPlacementGroupSupport,
43+
)
44+
BACKENDS_WITH_RESERVATION_SUPPORT = _get_backends_with_compute_feature(
45+
configurator_classes=_configurator_classes,
46+
compute_feature_class=ComputeWithReservationSupport,
47+
)
48+
BACKENDS_WITH_GATEWAY_SUPPORT = _get_backends_with_compute_feature(
49+
configurator_classes=_configurator_classes,
50+
compute_feature_class=ComputeWithGatewaySupport,
51+
)
52+
BACKENDS_WITH_PRIVATE_GATEWAY_SUPPORT = _get_backends_with_compute_feature(
53+
configurator_classes=_configurator_classes,
54+
compute_feature_class=ComputeWithPrivateGatewaySupport,
55+
)
56+
BACKENDS_WITH_VOLUMES_SUPPORT = _get_backends_with_compute_feature(
57+
configurator_classes=_configurator_classes,
58+
compute_feature_class=ComputeWithVolumeSupport,
59+
)

src/dstack/_internal/core/backends/aws/backend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99

1010
class AWSBackend(Backend):
11-
TYPE: BackendType = BackendType.AWS
11+
TYPE = BackendType.AWS
12+
COMPUTE_CLASS = AWSCompute
1213

1314
def __init__(self, config: AWSConfig):
1415
self.config = config

src/dstack/_internal/core/backends/aws/compute.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
from dstack._internal.core.backends.aws.models import AWSAccessKeyCreds
1313
from dstack._internal.core.backends.base.compute import (
1414
Compute,
15+
ComputeWithCreateInstanceSupport,
16+
ComputeWithGatewaySupport,
17+
ComputeWithMultinodeSupport,
18+
ComputeWithPlacementGroupSupport,
19+
ComputeWithPrivateGatewaySupport,
20+
ComputeWithReservationSupport,
21+
ComputeWithVolumeSupport,
1522
generate_unique_gateway_instance_name,
1623
generate_unique_instance_name,
1724
generate_unique_volume_name,
@@ -62,7 +69,16 @@ class AWSVolumeBackendData(CoreModel):
6269
iops: int
6370

6471

65-
class AWSCompute(Compute):
72+
class AWSCompute(
73+
Compute,
74+
ComputeWithCreateInstanceSupport,
75+
ComputeWithMultinodeSupport,
76+
ComputeWithReservationSupport,
77+
ComputeWithPlacementGroupSupport,
78+
ComputeWithGatewaySupport,
79+
ComputeWithPrivateGatewaySupport,
80+
ComputeWithVolumeSupport,
81+
):
6682
def __init__(self, config: AWSConfig):
6783
super().__init__()
6884
self.config = config

src/dstack/_internal/core/backends/aws/configurator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353

5454

5555
class AWSConfigurator(Configurator):
56-
TYPE: BackendType = BackendType.AWS
56+
TYPE = BackendType.AWS
57+
BACKEND_CLASS = AWSBackend
5758

5859
def validate_config(self, config: AWSBackendConfigWithCreds, default_creds_enabled: bool):
5960
if is_core_model_instance(config.creds, AWSDefaultCreds) and not default_creds_enabled:

src/dstack/_internal/core/backends/azure/backend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77

88
class AzureBackend(Backend):
9-
TYPE: BackendType = BackendType.AZURE
9+
TYPE = BackendType.AZURE
10+
COMPUTE_CLASS = AzureCompute
1011

1112
def __init__(self, config: AzureConfig):
1213
self.config = config

src/dstack/_internal/core/backends/azure/compute.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
from dstack._internal.core.backends.azure.config import AzureConfig
4040
from dstack._internal.core.backends.base.compute import (
4141
Compute,
42+
ComputeWithCreateInstanceSupport,
43+
ComputeWithGatewaySupport,
44+
ComputeWithMultinodeSupport,
4245
generate_unique_gateway_instance_name,
4346
generate_unique_instance_name,
4447
get_gateway_user_data,
@@ -71,7 +74,12 @@
7174
CONFIGURABLE_DISK_SIZE = Range[Memory](min=Memory.parse("30GB"), max=Memory.parse("4095GB"))
7275

7376

74-
class AzureCompute(Compute):
77+
class AzureCompute(
78+
Compute,
79+
ComputeWithCreateInstanceSupport,
80+
ComputeWithMultinodeSupport,
81+
ComputeWithGatewaySupport,
82+
):
7583
def __init__(self, config: AzureConfig, credential: TokenCredential):
7684
super().__init__()
7785
self.config = config

src/dstack/_internal/core/backends/azure/configurator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272

7373

7474
class AzureConfigurator(Configurator):
75-
TYPE: BackendType = BackendType.AZURE
75+
TYPE = BackendType.AZURE
76+
BACKEND_CLASS = AzureBackend
7677

7778
def validate_config(self, config: AzureBackendConfigWithCreds, default_creds_enabled: bool):
7879
if is_core_model_instance(config.creds, AzureDefaultCreds) and not default_creds_enabled:
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
from abc import ABC, abstractmethod
2+
from typing import ClassVar
23

34
from dstack._internal.core.backends.base.compute import Compute
45
from dstack._internal.core.models.backends.base import BackendType
56

67

78
class Backend(ABC):
8-
TYPE: BackendType
9+
TYPE: ClassVar[BackendType]
10+
# `COMPUTE_CLASS` is used to introspect compute features without initializing it.
11+
COMPUTE_CLASS: ClassVar[type[Compute]]
912

1013
@abstractmethod
1114
def compute(self) -> Compute:
15+
"""
16+
Returns Compute instance.
17+
"""
1218
pass

0 commit comments

Comments
 (0)