dstack 0.19.12rc1__py3-none-any.whl → 0.19.13__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.
Potentially problematic release.
This version of dstack might be problematic. Click here for more details.
- dstack/_internal/cli/services/configurators/run.py +43 -47
- dstack/_internal/cli/utils/run.py +15 -27
- dstack/_internal/core/backends/aws/compute.py +22 -9
- dstack/_internal/core/backends/aws/resources.py +26 -0
- dstack/_internal/core/backends/base/offers.py +0 -1
- dstack/_internal/core/backends/template/configurator.py.jinja +1 -6
- dstack/_internal/core/backends/template/models.py.jinja +4 -0
- dstack/_internal/core/compatibility/__init__.py +0 -0
- dstack/_internal/core/compatibility/fleets.py +72 -0
- dstack/_internal/core/compatibility/gateways.py +34 -0
- dstack/_internal/core/compatibility/runs.py +125 -0
- dstack/_internal/core/compatibility/volumes.py +32 -0
- dstack/_internal/core/models/configurations.py +1 -1
- dstack/_internal/core/models/fleets.py +6 -1
- dstack/_internal/core/models/instances.py +51 -12
- dstack/_internal/core/models/profiles.py +43 -3
- dstack/_internal/core/models/repos/local.py +3 -3
- dstack/_internal/core/models/runs.py +118 -44
- dstack/_internal/server/app.py +1 -1
- dstack/_internal/server/background/tasks/process_running_jobs.py +47 -12
- dstack/_internal/server/background/tasks/process_runs.py +14 -1
- dstack/_internal/server/services/runner/client.py +4 -1
- dstack/_internal/server/services/storage/__init__.py +38 -0
- dstack/_internal/server/services/storage/base.py +27 -0
- dstack/_internal/server/services/storage/gcs.py +44 -0
- dstack/_internal/server/services/{storage.py → storage/s3.py} +4 -27
- dstack/_internal/server/settings.py +7 -3
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-5b9786c955b42bf93581.js → main-2066f1f22ddb4557bcde.js} +1677 -46
- dstack/_internal/server/statics/{main-5b9786c955b42bf93581.js.map → main-2066f1f22ddb4557bcde.js.map} +1 -1
- dstack/_internal/server/statics/{main-8f9c66f404e9c7e7e020.css → main-f39c418b05fe14772dd8.css} +1 -1
- dstack/_internal/server/testing/common.py +2 -1
- dstack/_internal/utils/common.py +4 -0
- dstack/api/server/_fleets.py +9 -69
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_runs.py +4 -116
- dstack/api/server/_volumes.py +3 -14
- dstack/plugins/builtin/rest_plugin/_plugin.py +24 -5
- dstack/version.py +2 -2
- {dstack-0.19.12rc1.dist-info → dstack-0.19.13.dist-info}/METADATA +1 -1
- {dstack-0.19.12rc1.dist-info → dstack-0.19.13.dist-info}/RECORD +44 -36
- {dstack-0.19.12rc1.dist-info → dstack-0.19.13.dist-info}/WHEEL +0 -0
- {dstack-0.19.12rc1.dist-info → dstack-0.19.13.dist-info}/entry_points.txt +0 -0
- {dstack-0.19.12rc1.dist-info → dstack-0.19.13.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -308,12 +308,13 @@ async def create_job(
|
|
|
308
308
|
) -> JobModel:
|
|
309
309
|
run_spec = RunSpec.parse_raw(run.run_spec)
|
|
310
310
|
job_spec = (await get_job_specs_from_run_spec(run_spec, replica_num=replica_num))[0]
|
|
311
|
+
job_spec.job_num = job_num
|
|
311
312
|
job = JobModel(
|
|
312
313
|
project_id=run.project_id,
|
|
313
314
|
run_id=run.id,
|
|
314
315
|
run_name=run.run_name,
|
|
315
316
|
job_num=job_num,
|
|
316
|
-
job_name=run.run_name + f"-
|
|
317
|
+
job_name=run.run_name + f"-{job_num}-{replica_num}",
|
|
317
318
|
replica_num=replica_num,
|
|
318
319
|
submission_num=submission_num,
|
|
319
320
|
submitted_at=submitted_at,
|
dstack/_internal/utils/common.py
CHANGED
|
@@ -314,3 +314,7 @@ def make_proxy_url(server_url: str, proxy_url: str) -> str:
|
|
|
314
314
|
path=concat_url_path(server.path, proxy.path),
|
|
315
315
|
)
|
|
316
316
|
return proxy.geturl()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def list_enum_values_for_annotation(enum_class: type[enum.Enum]) -> str:
|
|
320
|
+
return ", ".join(f"`{e.value}`" for e in enum_class)
|
dstack/api/server/_fleets.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import List, Union
|
|
2
2
|
|
|
3
3
|
from pydantic import parse_obj_as
|
|
4
4
|
|
|
5
|
+
from dstack._internal.core.compatibility.fleets import (
|
|
6
|
+
get_apply_plan_excludes,
|
|
7
|
+
get_create_fleet_excludes,
|
|
8
|
+
get_get_plan_excludes,
|
|
9
|
+
)
|
|
5
10
|
from dstack._internal.core.models.fleets import ApplyFleetPlanInput, Fleet, FleetPlan, FleetSpec
|
|
6
|
-
from dstack._internal.core.models.instances import Instance
|
|
7
11
|
from dstack._internal.server.schemas.fleets import (
|
|
8
12
|
ApplyFleetPlanRequest,
|
|
9
13
|
CreateFleetRequest,
|
|
@@ -34,7 +38,7 @@ class FleetsAPIClient(APIClientGroup):
|
|
|
34
38
|
spec: FleetSpec,
|
|
35
39
|
) -> FleetPlan:
|
|
36
40
|
body = GetFleetPlanRequest(spec=spec)
|
|
37
|
-
body_json = body.json(exclude=
|
|
41
|
+
body_json = body.json(exclude=get_get_plan_excludes(spec))
|
|
38
42
|
resp = self._request(f"/api/project/{project_name}/fleets/get_plan", body=body_json)
|
|
39
43
|
return parse_obj_as(FleetPlan.__response__, resp.json())
|
|
40
44
|
|
|
@@ -46,7 +50,7 @@ class FleetsAPIClient(APIClientGroup):
|
|
|
46
50
|
) -> Fleet:
|
|
47
51
|
plan_input = ApplyFleetPlanInput.__response__.parse_obj(plan)
|
|
48
52
|
body = ApplyFleetPlanRequest(plan=plan_input, force=force)
|
|
49
|
-
body_json = body.json(exclude=
|
|
53
|
+
body_json = body.json(exclude=get_apply_plan_excludes(plan_input))
|
|
50
54
|
resp = self._request(f"/api/project/{project_name}/fleets/apply", body=body_json)
|
|
51
55
|
return parse_obj_as(Fleet.__response__, resp.json())
|
|
52
56
|
|
|
@@ -66,70 +70,6 @@ class FleetsAPIClient(APIClientGroup):
|
|
|
66
70
|
spec: FleetSpec,
|
|
67
71
|
) -> Fleet:
|
|
68
72
|
body = CreateFleetRequest(spec=spec)
|
|
69
|
-
body_json = body.json(exclude=
|
|
73
|
+
body_json = body.json(exclude=get_create_fleet_excludes(spec))
|
|
70
74
|
resp = self._request(f"/api/project/{project_name}/fleets/create", body=body_json)
|
|
71
75
|
return parse_obj_as(Fleet.__response__, resp.json())
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _get_get_plan_excludes(fleet_spec: FleetSpec) -> Dict:
|
|
75
|
-
get_plan_excludes = {}
|
|
76
|
-
spec_excludes = _get_fleet_spec_excludes(fleet_spec)
|
|
77
|
-
if spec_excludes:
|
|
78
|
-
get_plan_excludes["spec"] = spec_excludes
|
|
79
|
-
return get_plan_excludes
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _get_apply_plan_excludes(plan_input: ApplyFleetPlanInput) -> Dict:
|
|
83
|
-
apply_plan_excludes = {}
|
|
84
|
-
spec_excludes = _get_fleet_spec_excludes(plan_input.spec)
|
|
85
|
-
if spec_excludes:
|
|
86
|
-
apply_plan_excludes["spec"] = apply_plan_excludes
|
|
87
|
-
current_resource = plan_input.current_resource
|
|
88
|
-
if current_resource is not None:
|
|
89
|
-
current_resource_excludes = {}
|
|
90
|
-
apply_plan_excludes["current_resource"] = current_resource_excludes
|
|
91
|
-
if all(map(_should_exclude_instance_cpu_arch, current_resource.instances)):
|
|
92
|
-
current_resource_excludes["instances"] = {
|
|
93
|
-
"__all__": {"instance_type": {"resources": {"cpu_arch"}}}
|
|
94
|
-
}
|
|
95
|
-
return {"plan": apply_plan_excludes}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def _should_exclude_instance_cpu_arch(instance: Instance) -> bool:
|
|
99
|
-
try:
|
|
100
|
-
return instance.instance_type.resources.cpu_arch is None
|
|
101
|
-
except AttributeError:
|
|
102
|
-
return True
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def _get_create_fleet_excludes(fleet_spec: FleetSpec) -> Dict:
|
|
106
|
-
create_fleet_excludes = {}
|
|
107
|
-
spec_excludes = _get_fleet_spec_excludes(fleet_spec)
|
|
108
|
-
if spec_excludes:
|
|
109
|
-
create_fleet_excludes["spec"] = spec_excludes
|
|
110
|
-
return create_fleet_excludes
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def _get_fleet_spec_excludes(fleet_spec: FleetSpec) -> Optional[Dict]:
|
|
114
|
-
"""
|
|
115
|
-
Returns `fleet_spec` exclude mapping to exclude certain fields from the request.
|
|
116
|
-
Use this method to exclude new fields when they are not set to keep
|
|
117
|
-
clients backward-compatibility with older servers.
|
|
118
|
-
"""
|
|
119
|
-
spec_excludes: Dict[str, Any] = {}
|
|
120
|
-
configuration_excludes: Dict[str, Any] = {}
|
|
121
|
-
profile_excludes: set[str] = set()
|
|
122
|
-
profile = fleet_spec.profile
|
|
123
|
-
if profile.fleets is None:
|
|
124
|
-
profile_excludes.add("fleets")
|
|
125
|
-
if fleet_spec.configuration.tags is None:
|
|
126
|
-
configuration_excludes["tags"] = True
|
|
127
|
-
if profile.tags is None:
|
|
128
|
-
profile_excludes.add("tags")
|
|
129
|
-
if configuration_excludes:
|
|
130
|
-
spec_excludes["configuration"] = configuration_excludes
|
|
131
|
-
if profile_excludes:
|
|
132
|
-
spec_excludes["profile"] = profile_excludes
|
|
133
|
-
if spec_excludes:
|
|
134
|
-
return spec_excludes
|
|
135
|
-
return None
|
dstack/api/server/_gateways.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import List
|
|
2
2
|
|
|
3
3
|
from pydantic import parse_obj_as
|
|
4
4
|
|
|
5
|
+
from dstack._internal.core.compatibility.gateways import get_create_gateway_excludes
|
|
5
6
|
from dstack._internal.core.models.gateways import Gateway, GatewayConfiguration
|
|
6
7
|
from dstack._internal.server.schemas.gateways import (
|
|
7
8
|
CreateGatewayRequest,
|
|
@@ -31,7 +32,7 @@ class GatewaysAPIClient(APIClientGroup):
|
|
|
31
32
|
body = CreateGatewayRequest(configuration=configuration)
|
|
32
33
|
resp = self._request(
|
|
33
34
|
f"/api/project/{project_name}/gateways/create",
|
|
34
|
-
body=body.json(exclude=
|
|
35
|
+
body=body.json(exclude=get_create_gateway_excludes(configuration)),
|
|
35
36
|
)
|
|
36
37
|
return parse_obj_as(Gateway.__response__, resp.json())
|
|
37
38
|
|
|
@@ -51,15 +52,3 @@ class GatewaysAPIClient(APIClientGroup):
|
|
|
51
52
|
f"/api/project/{project_name}/gateways/set_wildcard_domain", body=body.json()
|
|
52
53
|
)
|
|
53
54
|
return parse_obj_as(Gateway.__response__, resp.json())
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _get_gateway_configuration_excludes(configuration: GatewayConfiguration) -> Dict:
|
|
57
|
-
"""
|
|
58
|
-
Returns `configuration` exclude mapping to exclude certain fields from the request.
|
|
59
|
-
Use this method to exclude new fields when they are not set to keep
|
|
60
|
-
clients backward-compatibility with older servers.
|
|
61
|
-
"""
|
|
62
|
-
configuration_excludes = {}
|
|
63
|
-
if configuration.tags is None:
|
|
64
|
-
configuration_excludes["tags"] = True
|
|
65
|
-
return {"configuration": configuration_excludes}
|
dstack/api/server/_runs.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
3
|
from uuid import UUID
|
|
4
4
|
|
|
5
5
|
from pydantic import parse_obj_as
|
|
6
6
|
|
|
7
|
-
from dstack._internal.core.
|
|
7
|
+
from dstack._internal.core.compatibility.runs import get_apply_plan_excludes, get_get_plan_excludes
|
|
8
8
|
from dstack._internal.core.models.runs import (
|
|
9
9
|
ApplyRunPlanInput,
|
|
10
|
-
JobSubmission,
|
|
11
10
|
Run,
|
|
12
11
|
RunPlan,
|
|
13
12
|
RunSpec,
|
|
@@ -60,7 +59,7 @@ class RunsAPIClient(APIClientGroup):
|
|
|
60
59
|
body = GetRunPlanRequest(run_spec=run_spec, max_offers=max_offers)
|
|
61
60
|
resp = self._request(
|
|
62
61
|
f"/api/project/{project_name}/runs/get_plan",
|
|
63
|
-
body=body.json(exclude=
|
|
62
|
+
body=body.json(exclude=get_get_plan_excludes(body)),
|
|
64
63
|
)
|
|
65
64
|
return parse_obj_as(RunPlan.__response__, resp.json())
|
|
66
65
|
|
|
@@ -74,7 +73,7 @@ class RunsAPIClient(APIClientGroup):
|
|
|
74
73
|
body = ApplyRunPlanRequest(plan=plan_input, force=force)
|
|
75
74
|
resp = self._request(
|
|
76
75
|
f"/api/project/{project_name}/runs/apply",
|
|
77
|
-
body=body.json(exclude=
|
|
76
|
+
body=body.json(exclude=get_apply_plan_excludes(plan_input)),
|
|
78
77
|
)
|
|
79
78
|
return parse_obj_as(Run.__response__, resp.json())
|
|
80
79
|
|
|
@@ -85,114 +84,3 @@ class RunsAPIClient(APIClientGroup):
|
|
|
85
84
|
def delete(self, project_name: str, runs_names: List[str]):
|
|
86
85
|
body = DeleteRunsRequest(runs_names=runs_names)
|
|
87
86
|
self._request(f"/api/project/{project_name}/runs/delete", body=body.json())
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _get_apply_plan_excludes(plan: ApplyRunPlanInput) -> Optional[Dict]:
|
|
91
|
-
"""
|
|
92
|
-
Returns `plan` exclude mapping to exclude certain fields from the request.
|
|
93
|
-
Use this method to exclude new fields when they are not set to keep
|
|
94
|
-
clients backward-compatibility with older servers.
|
|
95
|
-
"""
|
|
96
|
-
apply_plan_excludes = {}
|
|
97
|
-
run_spec_excludes = _get_run_spec_excludes(plan.run_spec)
|
|
98
|
-
if run_spec_excludes is not None:
|
|
99
|
-
apply_plan_excludes["run_spec"] = run_spec_excludes
|
|
100
|
-
current_resource = plan.current_resource
|
|
101
|
-
if current_resource is not None:
|
|
102
|
-
current_resource_excludes = {}
|
|
103
|
-
apply_plan_excludes["current_resource"] = current_resource_excludes
|
|
104
|
-
current_resource_excludes["run_spec"] = _get_run_spec_excludes(current_resource.run_spec)
|
|
105
|
-
job_submissions_excludes = {}
|
|
106
|
-
current_resource_excludes["jobs"] = {
|
|
107
|
-
"__all__": {"job_submissions": {"__all__": job_submissions_excludes}}
|
|
108
|
-
}
|
|
109
|
-
job_submissions = [js for j in current_resource.jobs for js in j.job_submissions]
|
|
110
|
-
if all(map(_should_exclude_job_submission_jpd_cpu_arch, job_submissions)):
|
|
111
|
-
job_submissions_excludes["job_provisioning_data"] = {
|
|
112
|
-
"instance_type": {"resources": {"cpu_arch"}}
|
|
113
|
-
}
|
|
114
|
-
if all(map(_should_exclude_job_submission_jrd_cpu_arch, job_submissions)):
|
|
115
|
-
job_submissions_excludes["job_runtime_data"] = {
|
|
116
|
-
"offer": {"instance": {"resources": {"cpu_arch"}}}
|
|
117
|
-
}
|
|
118
|
-
if all(js.exit_status is None for js in job_submissions):
|
|
119
|
-
job_submissions_excludes["exit_status"] = True
|
|
120
|
-
latest_job_submission = current_resource.latest_job_submission
|
|
121
|
-
if latest_job_submission is not None:
|
|
122
|
-
latest_job_submission_excludes = {}
|
|
123
|
-
current_resource_excludes["latest_job_submission"] = latest_job_submission_excludes
|
|
124
|
-
if _should_exclude_job_submission_jpd_cpu_arch(latest_job_submission):
|
|
125
|
-
latest_job_submission_excludes["job_provisioning_data"] = {
|
|
126
|
-
"instance_type": {"resources": {"cpu_arch"}}
|
|
127
|
-
}
|
|
128
|
-
if _should_exclude_job_submission_jrd_cpu_arch(latest_job_submission):
|
|
129
|
-
latest_job_submission_excludes["job_runtime_data"] = {
|
|
130
|
-
"offer": {"instance": {"resources": {"cpu_arch"}}}
|
|
131
|
-
}
|
|
132
|
-
if latest_job_submission.exit_status is None:
|
|
133
|
-
latest_job_submission_excludes["exit_status"] = True
|
|
134
|
-
return {"plan": apply_plan_excludes}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def _should_exclude_job_submission_jpd_cpu_arch(job_submission: JobSubmission) -> bool:
|
|
138
|
-
try:
|
|
139
|
-
return job_submission.job_provisioning_data.instance_type.resources.cpu_arch is None
|
|
140
|
-
except AttributeError:
|
|
141
|
-
return True
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def _should_exclude_job_submission_jrd_cpu_arch(job_submission: JobSubmission) -> bool:
|
|
145
|
-
try:
|
|
146
|
-
return job_submission.job_runtime_data.offer.instance.resources.cpu_arch is None
|
|
147
|
-
except AttributeError:
|
|
148
|
-
return True
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def _get_get_plan_excludes(request: GetRunPlanRequest) -> Optional[Dict]:
|
|
152
|
-
"""
|
|
153
|
-
Excludes new fields when they are not set to keep
|
|
154
|
-
clients backward-compatibility with older servers.
|
|
155
|
-
"""
|
|
156
|
-
get_plan_excludes = {}
|
|
157
|
-
run_spec_excludes = _get_run_spec_excludes(request.run_spec)
|
|
158
|
-
if run_spec_excludes is not None:
|
|
159
|
-
get_plan_excludes["run_spec"] = run_spec_excludes
|
|
160
|
-
if request.max_offers is None:
|
|
161
|
-
get_plan_excludes["max_offers"] = True
|
|
162
|
-
return get_plan_excludes
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def _get_run_spec_excludes(run_spec: RunSpec) -> Optional[Dict]:
|
|
166
|
-
"""
|
|
167
|
-
Returns `run_spec` exclude mapping to exclude certain fields from the request.
|
|
168
|
-
Use this method to exclude new fields when they are not set to keep
|
|
169
|
-
clients backward-compatibility with older servers.
|
|
170
|
-
"""
|
|
171
|
-
spec_excludes: dict[str, Any] = {}
|
|
172
|
-
configuration_excludes: dict[str, Any] = {}
|
|
173
|
-
profile_excludes: set[str] = set()
|
|
174
|
-
configuration = run_spec.configuration
|
|
175
|
-
profile = run_spec.profile
|
|
176
|
-
|
|
177
|
-
if configuration.fleets is None:
|
|
178
|
-
configuration_excludes["fleets"] = True
|
|
179
|
-
if profile is not None and profile.fleets is None:
|
|
180
|
-
profile_excludes.add("fleets")
|
|
181
|
-
if configuration.tags is None:
|
|
182
|
-
configuration_excludes["tags"] = True
|
|
183
|
-
if profile is not None and profile.tags is None:
|
|
184
|
-
profile_excludes.add("tags")
|
|
185
|
-
if isinstance(configuration, ServiceConfiguration) and not configuration.rate_limits:
|
|
186
|
-
configuration_excludes["rate_limits"] = True
|
|
187
|
-
if configuration.shell is None:
|
|
188
|
-
configuration_excludes["shell"] = True
|
|
189
|
-
if configuration.priority is None:
|
|
190
|
-
configuration_excludes["priority"] = True
|
|
191
|
-
|
|
192
|
-
if configuration_excludes:
|
|
193
|
-
spec_excludes["configuration"] = configuration_excludes
|
|
194
|
-
if profile_excludes:
|
|
195
|
-
spec_excludes["profile"] = profile_excludes
|
|
196
|
-
if spec_excludes:
|
|
197
|
-
return spec_excludes
|
|
198
|
-
return None
|
dstack/api/server/_volumes.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import List
|
|
2
2
|
|
|
3
3
|
from pydantic import parse_obj_as
|
|
4
4
|
|
|
5
|
+
from dstack._internal.core.compatibility.volumes import get_create_volume_excludes
|
|
5
6
|
from dstack._internal.core.models.volumes import Volume, VolumeConfiguration
|
|
6
7
|
from dstack._internal.server.schemas.volumes import (
|
|
7
8
|
CreateVolumeRequest,
|
|
@@ -29,22 +30,10 @@ class VolumesAPIClient(APIClientGroup):
|
|
|
29
30
|
body = CreateVolumeRequest(configuration=configuration)
|
|
30
31
|
resp = self._request(
|
|
31
32
|
f"/api/project/{project_name}/volumes/create",
|
|
32
|
-
body=body.json(exclude=
|
|
33
|
+
body=body.json(exclude=get_create_volume_excludes(configuration)),
|
|
33
34
|
)
|
|
34
35
|
return parse_obj_as(Volume.__response__, resp.json())
|
|
35
36
|
|
|
36
37
|
def delete(self, project_name: str, names: List[str]) -> None:
|
|
37
38
|
body = DeleteVolumesRequest(names=names)
|
|
38
39
|
self._request(f"/api/project/{project_name}/volumes/delete", body=body.json())
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _get_volume_configuration_excludes(configuration: VolumeConfiguration) -> Dict:
|
|
42
|
-
"""
|
|
43
|
-
Returns `configuration` exclude mapping to exclude certain fields from the request.
|
|
44
|
-
Use this method to exclude new fields when they are not set to keep
|
|
45
|
-
clients backward-compatibility with older servers.
|
|
46
|
-
"""
|
|
47
|
-
configuration_excludes = {}
|
|
48
|
-
if configuration.tags is None:
|
|
49
|
-
configuration_excludes["tags"] = True
|
|
50
|
-
return {"configuration": configuration_excludes}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
-
from typing import Type
|
|
3
|
+
from typing import Dict, Optional, Type
|
|
4
4
|
|
|
5
5
|
import requests
|
|
6
6
|
from pydantic import ValidationError
|
|
7
7
|
|
|
8
|
+
from dstack._internal.core.compatibility.fleets import get_fleet_spec_excludes
|
|
9
|
+
from dstack._internal.core.compatibility.gateways import get_gateway_spec_excludes
|
|
10
|
+
from dstack._internal.core.compatibility.runs import get_run_spec_excludes
|
|
11
|
+
from dstack._internal.core.compatibility.volumes import get_volume_spec_excludes
|
|
8
12
|
from dstack._internal.core.errors import ServerClientError
|
|
9
13
|
from dstack._internal.core.models.fleets import FleetSpec
|
|
10
14
|
from dstack._internal.core.models.gateways import GatewaySpec
|
|
@@ -44,12 +48,17 @@ class CustomApplyPolicy(ApplyPolicy):
|
|
|
44
48
|
logger.error(f"Plugin service rejected apply request: {response.error}")
|
|
45
49
|
raise ServerClientError(f"Apply request rejected: {response.error}")
|
|
46
50
|
|
|
47
|
-
def _call_plugin_service(
|
|
51
|
+
def _call_plugin_service(
|
|
52
|
+
self,
|
|
53
|
+
spec_request: SpecApplyRequest,
|
|
54
|
+
endpoint: str,
|
|
55
|
+
excludes: Optional[Dict],
|
|
56
|
+
) -> ApplySpec:
|
|
48
57
|
response = None
|
|
49
58
|
try:
|
|
50
59
|
response = requests.post(
|
|
51
60
|
f"{self._plugin_service_uri}{endpoint}",
|
|
52
|
-
json=spec_request.dict(),
|
|
61
|
+
json=spec_request.dict(exclude={"spec": excludes}),
|
|
53
62
|
headers={"accept": "application/json", "Content-Type": "application/json"},
|
|
54
63
|
timeout=PLUGIN_REQUEST_TIMEOUT_SEC,
|
|
55
64
|
)
|
|
@@ -75,10 +84,11 @@ class CustomApplyPolicy(ApplyPolicy):
|
|
|
75
84
|
user: str,
|
|
76
85
|
project: str,
|
|
77
86
|
spec: ApplySpec,
|
|
87
|
+
excludes: Optional[Dict] = None,
|
|
78
88
|
) -> ApplySpec:
|
|
79
89
|
try:
|
|
80
90
|
spec_request = request_cls(user=user, project=project, spec=spec)
|
|
81
|
-
spec_json = self._call_plugin_service(spec_request, endpoint)
|
|
91
|
+
spec_json = self._call_plugin_service(spec_request, endpoint, excludes)
|
|
82
92
|
response = response_cls(**spec_json)
|
|
83
93
|
self._check_request_rejected(response)
|
|
84
94
|
return response.spec
|
|
@@ -88,7 +98,13 @@ class CustomApplyPolicy(ApplyPolicy):
|
|
|
88
98
|
|
|
89
99
|
def on_run_apply(self, user: str, project: str, spec: RunSpec) -> RunSpec:
|
|
90
100
|
return self._on_apply(
|
|
91
|
-
RunSpecRequest,
|
|
101
|
+
RunSpecRequest,
|
|
102
|
+
RunSpecResponse,
|
|
103
|
+
"/apply_policies/on_run_apply",
|
|
104
|
+
user,
|
|
105
|
+
project,
|
|
106
|
+
spec,
|
|
107
|
+
excludes=get_run_spec_excludes(spec),
|
|
92
108
|
)
|
|
93
109
|
|
|
94
110
|
def on_fleet_apply(self, user: str, project: str, spec: FleetSpec) -> FleetSpec:
|
|
@@ -99,6 +115,7 @@ class CustomApplyPolicy(ApplyPolicy):
|
|
|
99
115
|
user,
|
|
100
116
|
project,
|
|
101
117
|
spec,
|
|
118
|
+
excludes=get_fleet_spec_excludes(spec),
|
|
102
119
|
)
|
|
103
120
|
|
|
104
121
|
def on_volume_apply(self, user: str, project: str, spec: VolumeSpec) -> VolumeSpec:
|
|
@@ -109,6 +126,7 @@ class CustomApplyPolicy(ApplyPolicy):
|
|
|
109
126
|
user,
|
|
110
127
|
project,
|
|
111
128
|
spec,
|
|
129
|
+
excludes=get_volume_spec_excludes(spec),
|
|
112
130
|
)
|
|
113
131
|
|
|
114
132
|
def on_gateway_apply(self, user: str, project: str, spec: GatewaySpec) -> GatewaySpec:
|
|
@@ -119,6 +137,7 @@ class CustomApplyPolicy(ApplyPolicy):
|
|
|
119
137
|
user,
|
|
120
138
|
project,
|
|
121
139
|
spec,
|
|
140
|
+
excludes=get_gateway_spec_excludes(spec),
|
|
122
141
|
)
|
|
123
142
|
|
|
124
143
|
|
dstack/version.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
__version__ = "0.19.
|
|
1
|
+
__version__ = "0.19.13"
|
|
2
2
|
__is_release__ = True
|
|
3
|
-
base_image = "0.
|
|
3
|
+
base_image = "0.9"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dstack
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.13
|
|
4
4
|
Summary: dstack is an open-source orchestration engine for running AI workloads on any cloud or on-premises.
|
|
5
5
|
Project-URL: Homepage, https://dstack.ai
|
|
6
6
|
Project-URL: Source, https://github.com/dstackai/dstack
|