Skip to content

Commit ba37fb0

Browse files
Bihan  RanaBihan  Rana
authored andcommitted
Resolve Review Comments and Update docs
1 parent 1393761 commit ba37fb0

9 files changed

Lines changed: 180 additions & 57 deletions

File tree

docs/docs/concepts/backends.md

Lines changed: 98 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -579,34 +579,6 @@ gcloud projects list --format="json(projectId)"
579579
Using private subnets assumes that both the `dstack` server and users can access the configured VPC's private subnets.
580580
Additionally, [Cloud NAT](https://cloud.google.com/nat/docs/overview) must be configured to provide access to external resources for provisioned instances.
581581

582-
## Hot Aisle
583-
584-
Log in to the SSH TUI as described in the [Hot Aisle Quick Start :material-arrow-top-right-thin:{ .external }](https://hotaisle.xyz/quick-start/).
585-
Create a new team and generate an API key for the member in the team.
586-
587-
Then, go ahead and configure the backend:
588-
589-
<div editor-title="~/.dstack/server/config.yml">
590-
591-
```yaml
592-
projects:
593-
- name: main
594-
backends:
595-
- type: hotaisle
596-
team_handle: hotaisle-team-handle
597-
creds:
598-
type: api_key
599-
api_key: 9c27a4bb7a8e472fae12ab34.3f2e3c1db75b9a0187fd2196c6b3e56d2b912e1c439ba08d89e7b6fcd4ef1d3f
600-
```
601-
602-
</div>
603-
604-
??? info "Required permissions"
605-
The API key must have the following roles assigned:
606-
607-
* **Owner role for the user** - Required for creating and managing SSH keys
608-
* **Operator role for the team** - Required for managing virtual machines within the team
609-
610582
## Lambda
611583

612584
Log into your [Lambda Cloud :material-arrow-top-right-thin:{ .external }](https://lambdalabs.com/service/gpu-cloud) account, click API keys in the sidebar, and then click the `Generate API key`
@@ -937,6 +909,104 @@ projects:
937909

938910
</div>
939911

912+
## AMD Developer Cloud
913+
Log into your [AMD Developer Cloud :material-arrow-top-right-thin:{ .external }](https://amd.digitalocean.com/login) account. Click `API` in the sidebar and click the button `Generate New Token`.
914+
915+
Then, go ahead and configure the backend:
916+
917+
<div editor-title="~/.dstack/server/config.yml">
918+
919+
```yaml
920+
projects:
921+
- name: main
922+
backends:
923+
- type: amddevcloud
924+
project_name: my-amd-project
925+
creds:
926+
type: api_key
927+
api_key: dop_v1_71ea79a0c4bf2ffa70ac9d2a7b2689d2b41768567b22ebabe58a80066dcc5e92
928+
```
929+
930+
</div>
931+
932+
??? info "Project Name"
933+
**project_name** configuration is optional. If it is not provided, the default project is used.
934+
935+
??? info "Required permissions"
936+
The API key must have the following scopes assigned:
937+
938+
* **account** - read
939+
* **droplet** - create,read,update,delete,admin
940+
* **project** - create,read,update,delete
941+
* **regions** - read
942+
* **sizes** - read
943+
* **ssh_key** - create,read,update,delete
944+
945+
946+
947+
## Digital Ocean
948+
Log into your [Digital Ocean :material-arrow-top-right-thin:{ .external }](https://cloud.digitalocean.com/login) account. Click `API` in the sidebar and click the button `Generate New Token`.
949+
950+
Then, go ahead and configure the backend:
951+
952+
<div editor-title="~/.dstack/server/config.yml">
953+
954+
```yaml
955+
projects:
956+
- name: main
957+
backends:
958+
- type: digitalocean
959+
project_name: my-digital-ocean-project
960+
creds:
961+
type: api_key
962+
api_key: dop_v1_71ea79a0c4bf2ffa70ac9d2a7b2689d2b41768567b22ebabe58a80066dcc5e92
963+
```
964+
965+
</div>
966+
967+
??? info "Project Name"
968+
**project_name** configuration is optional. If it is not provided, the default project is used.
969+
970+
??? info "Required permissions"
971+
The API key must have the following scopes assigned:
972+
973+
* **account** - read
974+
* **droplet** - create,read,update,delete,admin
975+
* **project** - create,read,update,delete
976+
* **regions** - read
977+
* **sizes** - read
978+
* **ssh_key** - create,read,update,delete
979+
980+
981+
## Hot Aisle
982+
983+
Log in to the SSH TUI as described in the [Hot Aisle Quick Start :material-arrow-top-right-thin:{ .external }](https://hotaisle.xyz/quick-start/).
984+
Create a new team and generate an API key for the member in the team.
985+
986+
Then, go ahead and configure the backend:
987+
988+
<div editor-title="~/.dstack/server/config.yml">
989+
990+
```yaml
991+
projects:
992+
- name: main
993+
backends:
994+
- type: hotaisle
995+
team_handle: hotaisle-team-handle
996+
creds:
997+
type: api_key
998+
api_key: 9c27a4bb7a8e472fae12ab34.3f2e3c1db75b9a0187fd2196c6b3e56d2b912e1c439ba08d89e7b6fcd4ef1d3f
999+
```
1000+
1001+
</div>
1002+
1003+
??? info "Required permissions"
1004+
The API key must have the following roles assigned:
1005+
1006+
* **Owner role for the user** - Required for creating and managing SSH keys
1007+
* **Operator role for the team** - Required for managing virtual machines within the team
1008+
1009+
9401010
## CloudRift
9411011

9421012
Log into your [CloudRift :material-arrow-top-right-thin:{ .external }](https://console.cloudrift.ai/) console, click `API Keys` in the sidebar and click the button to create a new API key.

docs/docs/reference/server/config.yml.md

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -126,22 +126,6 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
126126
type:
127127
required: true
128128

129-
##### `projects[n].backends[type=hotaisle]` { #hotaisle data-toc-label="hotaisle" }
130-
131-
#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleBackendConfigWithCreds
132-
overrides:
133-
show_root_heading: false
134-
type:
135-
required: true
136-
item_id_prefix: hotaisle-
137-
138-
###### `projects[n].backends[type=hotaisle].creds` { #hotaisle-creds data-toc-label="creds" }
139-
140-
#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleAPIKeyCreds
141-
overrides:
142-
show_root_heading: false
143-
type:
144-
required: true
145129

146130
##### `projects[n].backends[type=lambda]` { #lambda data-toc-label="lambda" }
147131

@@ -332,6 +316,57 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
332316
type:
333317
required: true
334318

319+
##### `projects[n].backends[type=amddevcloud]` { #amddevcloud data-toc-label="amddevcloud" }
320+
321+
#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanBackendConfigWithCreds
322+
overrides:
323+
show_root_heading: false
324+
type:
325+
required: true
326+
item_id_prefix: amddevcloud-
327+
328+
###### `projects[n].backends[type=amddevcloud].creds` { #amddevcloud-creds data-toc-label="creds" }
329+
330+
#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanAPIKeyCreds
331+
overrides:
332+
show_root_heading: false
333+
type:
334+
required: true
335+
336+
##### `projects[n].backends[type=digitalocean]` { #digitalocean data-toc-label="digitalocean" }
337+
338+
#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanBackendConfigWithCreds
339+
overrides:
340+
show_root_heading: false
341+
type:
342+
required: true
343+
item_id_prefix: digitalocean-
344+
345+
###### `projects[n].backends[type=digitalocean].creds` { #digitalocean-creds data-toc-label="creds" }
346+
347+
#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanAPIKeyCreds
348+
overrides:
349+
show_root_heading: false
350+
type:
351+
required: true
352+
353+
##### `projects[n].backends[type=hotaisle]` { #hotaisle data-toc-label="hotaisle" }
354+
355+
#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleBackendConfigWithCreds
356+
overrides:
357+
show_root_heading: false
358+
type:
359+
required: true
360+
item_id_prefix: hotaisle-
361+
362+
###### `projects[n].backends[type=hotaisle].creds` { #hotaisle-creds data-toc-label="creds" }
363+
364+
#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleAPIKeyCreds
365+
overrides:
366+
show_root_heading: false
367+
type:
368+
required: true
369+
335370
##### `projects[n].backends[type=cloudrift]` { #cloudrift data-toc-label="cloudrift" }
336371

337372
#SCHEMA# dstack._internal.core.backends.cloudrift.models.CloudRiftBackendConfigWithCreds

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Optional
2+
13
from dstack._internal.core.backends.amddevcloud.backend import AMDDevCloudBackend
24
from dstack._internal.core.backends.digitalocean_base.api_client import DigitalOceanAPIClient
35
from dstack._internal.core.backends.digitalocean_base.backend import BaseDigitalOceanBackend
@@ -19,6 +21,8 @@ def get_backend(self, record) -> BaseDigitalOceanBackend:
1921
config = self._get_config(record)
2022
return AMDDevCloudBackend(config=config, api_url=self.API_URL)
2123

22-
def _validate_creds(self, creds: AnyBaseDigitalOceanCreds):
24+
def _validate_creds(self, creds: AnyBaseDigitalOceanCreds, project_name: Optional[str] = None):
2325
api_client = DigitalOceanAPIClient(creds.api_key, self.API_URL)
2426
api_client.validate_api_key()
27+
if project_name:
28+
api_client.validate_project_name(project_name)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Optional
2+
13
from dstack._internal.core.backends.base.configurator import BackendRecord
24
from dstack._internal.core.backends.digitalocean.backend import DigitalOceanBackend
35
from dstack._internal.core.backends.digitalocean_base.api_client import DigitalOceanAPIClient
@@ -22,6 +24,8 @@ def get_backend(self, record: BackendRecord) -> BaseDigitalOceanBackend:
2224
config = self._get_config(record)
2325
return DigitalOceanBackend(config=config, api_url=self.API_URL)
2426

25-
def _validate_creds(self, creds: AnyBaseDigitalOceanCreds):
27+
def _validate_creds(self, creds: AnyBaseDigitalOceanCreds, project_name: Optional[str] = None):
2628
api_client = DigitalOceanAPIClient(creds.api_key, self.API_URL)
2729
api_client.validate_api_key()
30+
if project_name:
31+
api_client.validate_project_name(project_name)

src/dstack/_internal/core/backends/digitalocean_base/api_client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ def validate_api_key(self) -> bool:
2727
)
2828
raise e
2929

30+
def validate_project_name(self, project_name: str) -> bool:
31+
if self.get_project_id(project_name) is None:
32+
raise_invalid_credentials_error(
33+
fields=[["config", "project_name"]],
34+
details=f"Project with name '{project_name}' does not exist",
35+
)
36+
return True
37+
3038
def list_ssh_keys(self) -> List[Dict[str, Any]]:
3139
response = self._make_request("GET", "/v2/account/keys")
3240
response.raise_for_status()
@@ -83,7 +91,6 @@ def _make_request(
8391
) -> requests.Response:
8492
url = f"{self.base_url}{endpoint}"
8593
headers = {
86-
"Content-Type": "application/json",
8794
"Authorization": f"Bearer {self.api_key}",
8895
}
8996

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,18 @@ def create_instance(
8888
name=f"dstack-{instance_config.project_name}",
8989
public_key=project_ssh_key.public,
9090
)
91-
9291
size_slug = instance_offer.instance.name
9392

9493
if not instance_offer.instance.resources.gpus:
9594
backend_specific_commands = SETUP_COMMANDS + DOCKER_INSTALL_COMMANDS
9695
else:
9796
backend_specific_commands = SETUP_COMMANDS
9897

99-
project_id = self.api_client.get_project_id(self.config.project_name)
98+
project_id = (
99+
self.api_client.get_project_id(self.config.project_name)
100+
if self.config.project_name
101+
else None
102+
)
100103
droplet_config = {
101104
"name": instance_name,
102105
"region": instance_offer.region,
@@ -155,9 +158,9 @@ def _get_image_for_instance(self, instance_offer: InstanceOfferWithAvailability)
155158
return "ubuntu-24-04-x64"
156159

157160
gpu_count = len(instance_offer.instance.resources.gpus)
158-
gpu_name = instance_offer.instance.resources.gpus[0].name
161+
gpu_vendor = instance_offer.instance.resources.gpus[0].vendor
159162

160-
if gpu_name == "MI300X":
163+
if gpu_vendor == "amd":
161164
# AMD GPU
162165
return "digitaloceanai-rocmjupyter"
163166
else:

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from typing import Optional
23

34
from dstack._internal.core.backends.base.configurator import (
45
BackendRecord,
@@ -20,7 +21,7 @@ class BaseDigitalOceanConfigurator(Configurator):
2021
def validate_config(
2122
self, config: BaseDigitalOceanBackendConfigWithCreds, default_creds_enabled: bool
2223
):
23-
self._validate_creds(config.creds)
24+
self._validate_creds(config.creds, config.project_name)
2425

2526
def create_backend(
2627
self, project_name: str, config: BaseDigitalOceanBackendConfigWithCreds
@@ -49,5 +50,5 @@ def _get_config(self, record: BackendRecord) -> BaseDigitalOceanConfig:
4950
creds=BaseDigitalOceanCreds.parse_raw(record.auth),
5051
)
5152

52-
def _validate_creds(self, creds: AnyBaseDigitalOceanCreds):
53+
def _validate_creds(self, creds: AnyBaseDigitalOceanCreds, project_name: Optional[str] = None):
5354
pass

src/dstack/_internal/core/backends/digitalocean_base/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ class BaseDigitalOceanBackendConfig(CoreModel):
1919
Literal["amddevcloud", "digitalocean"],
2020
Field(description="The type of backend"),
2121
] = "digitalocean"
22-
project_name: Annotated[str, Field(description="The name of the DigitalOcean project")] = None
22+
project_name: Annotated[Optional[str], Field(description="The name of the project")] = None
2323
regions: Annotated[
2424
Optional[List[str]],
25-
Field(description="The list of DigitalOcean regions. Omit to use all regions"),
25+
Field(description="The list of regions. Omit to use all regions"),
2626
] = None
2727

2828

src/dstack/_internal/core/backends/models.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from dstack._internal.core.backends.digitalocean_base.models import (
2424
BaseDigitalOceanBackendConfig,
2525
BaseDigitalOceanBackendConfigWithCreds,
26-
BaseDigitalOceanBackendFileConfigWithCreds,
2726
)
2827
from dstack._internal.core.backends.dstack.models import (
2928
DstackBackendConfig,
@@ -129,7 +128,7 @@
129128
CloudRiftBackendConfigWithCreds,
130129
CudoBackendConfigWithCreds,
131130
DataCrunchBackendConfigWithCreds,
132-
BaseDigitalOceanBackendFileConfigWithCreds,
131+
BaseDigitalOceanBackendConfigWithCreds,
133132
GCPBackendFileConfigWithCreds,
134133
HotAisleBackendFileConfigWithCreds,
135134
KubernetesBackendFileConfigWithCreds,

0 commit comments

Comments
 (0)