dstack 0.19.18__py3-none-any.whl → 0.19.19__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/fleet.py +99 -1
- dstack/_internal/cli/services/profile.py +1 -1
- dstack/_internal/core/compatibility/runs.py +12 -1
- dstack/_internal/core/compatibility/volumes.py +2 -0
- dstack/_internal/core/models/common.py +38 -2
- dstack/_internal/core/models/configurations.py +9 -1
- dstack/_internal/core/models/fleets.py +2 -1
- dstack/_internal/core/models/profiles.py +8 -5
- dstack/_internal/core/models/resources.py +15 -8
- dstack/_internal/core/models/runs.py +41 -138
- dstack/_internal/core/models/volumes.py +14 -0
- dstack/_internal/core/services/diff.py +30 -10
- dstack/_internal/core/services/ssh/attach.py +2 -0
- dstack/_internal/server/app.py +17 -9
- dstack/_internal/server/background/__init__.py +5 -3
- dstack/_internal/server/background/tasks/process_gateways.py +46 -28
- dstack/_internal/server/background/tasks/process_idle_volumes.py +139 -0
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +2 -0
- dstack/_internal/server/migrations/versions/35e90e1b0d3e_add_rolling_deployment_fields.py +6 -6
- dstack/_internal/server/migrations/versions/d5863798bf41_add_volumemodel_last_job_processed_at.py +40 -0
- dstack/_internal/server/models.py +1 -0
- dstack/_internal/server/routers/backends.py +23 -16
- dstack/_internal/server/routers/files.py +7 -6
- dstack/_internal/server/routers/fleets.py +47 -36
- dstack/_internal/server/routers/gateways.py +27 -18
- dstack/_internal/server/routers/instances.py +18 -13
- dstack/_internal/server/routers/logs.py +7 -3
- dstack/_internal/server/routers/metrics.py +14 -8
- dstack/_internal/server/routers/projects.py +33 -22
- dstack/_internal/server/routers/repos.py +7 -6
- dstack/_internal/server/routers/runs.py +49 -28
- dstack/_internal/server/routers/secrets.py +20 -15
- dstack/_internal/server/routers/server.py +7 -4
- dstack/_internal/server/routers/users.py +22 -19
- dstack/_internal/server/routers/volumes.py +34 -25
- dstack/_internal/server/schemas/logs.py +2 -2
- dstack/_internal/server/schemas/runs.py +17 -5
- dstack/_internal/server/services/fleets.py +354 -72
- dstack/_internal/server/services/gateways/__init__.py +13 -4
- dstack/_internal/server/services/gateways/client.py +5 -3
- dstack/_internal/server/services/instances.py +8 -0
- dstack/_internal/server/services/jobs/__init__.py +45 -0
- dstack/_internal/server/services/jobs/configurators/base.py +7 -0
- dstack/_internal/server/services/locking.py +3 -1
- dstack/_internal/server/services/logging.py +4 -2
- dstack/_internal/server/services/logs/__init__.py +15 -2
- dstack/_internal/server/services/logs/aws.py +2 -4
- dstack/_internal/server/services/logs/filelog.py +33 -27
- dstack/_internal/server/services/logs/gcp.py +3 -5
- dstack/_internal/server/services/proxy/repo.py +4 -1
- dstack/_internal/server/services/runs.py +115 -32
- dstack/_internal/server/services/services/__init__.py +2 -1
- dstack/_internal/server/services/users.py +3 -1
- dstack/_internal/server/services/volumes.py +13 -0
- dstack/_internal/server/settings.py +7 -2
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-d1ac2e8c38ed5f08a114.js → main-64f8273740c4b52c18f5.js} +6 -6
- dstack/_internal/server/statics/{main-d1ac2e8c38ed5f08a114.js.map → main-64f8273740c4b52c18f5.js.map} +1 -1
- dstack/_internal/server/testing/common.py +41 -5
- dstack/_internal/server/utils/routers.py +31 -8
- dstack/_internal/utils/json_utils.py +54 -0
- dstack/api/_public/runs.py +13 -2
- dstack/api/server/_runs.py +12 -2
- dstack/version.py +1 -1
- {dstack-0.19.18.dist-info → dstack-0.19.19.dist-info}/METADATA +7 -5
- {dstack-0.19.18.dist-info → dstack-0.19.19.dist-info}/RECORD +69 -66
- {dstack-0.19.18.dist-info → dstack-0.19.19.dist-info}/WHEEL +0 -0
- {dstack-0.19.18.dist-info → dstack-0.19.19.dist-info}/entry_points.txt +0 -0
- {dstack-0.19.18.dist-info → dstack-0.19.19.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -18,7 +18,10 @@ from dstack._internal.server.schemas.runs import (
|
|
|
18
18
|
)
|
|
19
19
|
from dstack._internal.server.security.permissions import Authenticated, ProjectMember
|
|
20
20
|
from dstack._internal.server.services import runs
|
|
21
|
-
from dstack._internal.server.utils.routers import
|
|
21
|
+
from dstack._internal.server.utils.routers import (
|
|
22
|
+
CustomORJSONResponse,
|
|
23
|
+
get_base_api_additional_responses,
|
|
24
|
+
)
|
|
22
25
|
|
|
23
26
|
root_router = APIRouter(
|
|
24
27
|
prefix="/api/runs",
|
|
@@ -32,12 +35,15 @@ project_router = APIRouter(
|
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
|
|
35
|
-
@root_router.post(
|
|
38
|
+
@root_router.post(
|
|
39
|
+
"/list",
|
|
40
|
+
response_model=List[Run],
|
|
41
|
+
)
|
|
36
42
|
async def list_runs(
|
|
37
43
|
body: ListRunsRequest,
|
|
38
44
|
session: AsyncSession = Depends(get_session),
|
|
39
45
|
user: UserModel = Depends(Authenticated()),
|
|
40
|
-
)
|
|
46
|
+
):
|
|
41
47
|
"""
|
|
42
48
|
Returns all runs visible to user sorted by descending `submitted_at`.
|
|
43
49
|
`project_name`, `repo_id`, `username`, and `only_active` can be specified as filters.
|
|
@@ -47,26 +53,33 @@ async def list_runs(
|
|
|
47
53
|
The results are paginated. To get the next page, pass `submitted_at` and `id` of
|
|
48
54
|
the last run from the previous page as `prev_submitted_at` and `prev_run_id`.
|
|
49
55
|
"""
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
return CustomORJSONResponse(
|
|
57
|
+
await runs.list_user_runs(
|
|
58
|
+
session=session,
|
|
59
|
+
user=user,
|
|
60
|
+
project_name=body.project_name,
|
|
61
|
+
repo_id=body.repo_id,
|
|
62
|
+
username=body.username,
|
|
63
|
+
only_active=body.only_active,
|
|
64
|
+
include_jobs=body.include_jobs,
|
|
65
|
+
job_submissions_limit=body.job_submissions_limit,
|
|
66
|
+
prev_submitted_at=body.prev_submitted_at,
|
|
67
|
+
prev_run_id=body.prev_run_id,
|
|
68
|
+
limit=body.limit,
|
|
69
|
+
ascending=body.ascending,
|
|
70
|
+
)
|
|
61
71
|
)
|
|
62
72
|
|
|
63
73
|
|
|
64
|
-
@project_router.post(
|
|
74
|
+
@project_router.post(
|
|
75
|
+
"/get",
|
|
76
|
+
response_model=Run,
|
|
77
|
+
)
|
|
65
78
|
async def get_run(
|
|
66
79
|
body: GetRunRequest,
|
|
67
80
|
session: AsyncSession = Depends(get_session),
|
|
68
81
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
69
|
-
)
|
|
82
|
+
):
|
|
70
83
|
"""
|
|
71
84
|
Returns a run given `run_name` or `id`.
|
|
72
85
|
If given `run_name`, does not return deleted runs.
|
|
@@ -81,15 +94,18 @@ async def get_run(
|
|
|
81
94
|
)
|
|
82
95
|
if run is None:
|
|
83
96
|
raise ResourceNotExistsError("Run not found")
|
|
84
|
-
return run
|
|
97
|
+
return CustomORJSONResponse(run)
|
|
85
98
|
|
|
86
99
|
|
|
87
|
-
@project_router.post(
|
|
100
|
+
@project_router.post(
|
|
101
|
+
"/get_plan",
|
|
102
|
+
response_model=RunPlan,
|
|
103
|
+
)
|
|
88
104
|
async def get_plan(
|
|
89
105
|
body: GetRunPlanRequest,
|
|
90
106
|
session: AsyncSession = Depends(get_session),
|
|
91
107
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
92
|
-
)
|
|
108
|
+
):
|
|
93
109
|
"""
|
|
94
110
|
Returns a run plan for the given run spec.
|
|
95
111
|
This is an optional step before calling `/apply`.
|
|
@@ -102,15 +118,18 @@ async def get_plan(
|
|
|
102
118
|
run_spec=body.run_spec,
|
|
103
119
|
max_offers=body.max_offers,
|
|
104
120
|
)
|
|
105
|
-
return run_plan
|
|
121
|
+
return CustomORJSONResponse(run_plan)
|
|
106
122
|
|
|
107
123
|
|
|
108
|
-
@project_router.post(
|
|
124
|
+
@project_router.post(
|
|
125
|
+
"/apply",
|
|
126
|
+
response_model=Run,
|
|
127
|
+
)
|
|
109
128
|
async def apply_plan(
|
|
110
129
|
body: ApplyRunPlanRequest,
|
|
111
130
|
session: AsyncSession = Depends(get_session),
|
|
112
131
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
113
|
-
)
|
|
132
|
+
):
|
|
114
133
|
"""
|
|
115
134
|
Creates a new run or updates an existing run.
|
|
116
135
|
Errors if the expected current resource from the plan does not match the current resource.
|
|
@@ -118,12 +137,14 @@ async def apply_plan(
|
|
|
118
137
|
If the existing run is active and cannot be updated, it must be stopped first.
|
|
119
138
|
"""
|
|
120
139
|
user, project = user_project
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
140
|
+
return CustomORJSONResponse(
|
|
141
|
+
await runs.apply_plan(
|
|
142
|
+
session=session,
|
|
143
|
+
user=user,
|
|
144
|
+
project=project,
|
|
145
|
+
plan=body.plan,
|
|
146
|
+
force=body.force,
|
|
147
|
+
)
|
|
127
148
|
)
|
|
128
149
|
|
|
129
150
|
|
|
@@ -14,6 +14,7 @@ from dstack._internal.server.schemas.secrets import (
|
|
|
14
14
|
)
|
|
15
15
|
from dstack._internal.server.security.permissions import ProjectAdmin
|
|
16
16
|
from dstack._internal.server.services import secrets as secrets_services
|
|
17
|
+
from dstack._internal.server.utils.routers import CustomORJSONResponse
|
|
17
18
|
|
|
18
19
|
router = APIRouter(
|
|
19
20
|
prefix="/api/project/{project_name}/secrets",
|
|
@@ -21,24 +22,26 @@ router = APIRouter(
|
|
|
21
22
|
)
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
@router.post("/list")
|
|
25
|
+
@router.post("/list", response_model=List[Secret])
|
|
25
26
|
async def list_secrets(
|
|
26
27
|
session: AsyncSession = Depends(get_session),
|
|
27
28
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
|
|
28
|
-
)
|
|
29
|
+
):
|
|
29
30
|
_, project = user_project
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
return CustomORJSONResponse(
|
|
32
|
+
await secrets_services.list_secrets(
|
|
33
|
+
session=session,
|
|
34
|
+
project=project,
|
|
35
|
+
)
|
|
33
36
|
)
|
|
34
37
|
|
|
35
38
|
|
|
36
|
-
@router.post("/get")
|
|
39
|
+
@router.post("/get", response_model=Secret)
|
|
37
40
|
async def get_secret(
|
|
38
41
|
body: GetSecretRequest,
|
|
39
42
|
session: AsyncSession = Depends(get_session),
|
|
40
43
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
|
|
41
|
-
)
|
|
44
|
+
):
|
|
42
45
|
_, project = user_project
|
|
43
46
|
secret = await secrets_services.get_secret(
|
|
44
47
|
session=session,
|
|
@@ -47,21 +50,23 @@ async def get_secret(
|
|
|
47
50
|
)
|
|
48
51
|
if secret is None:
|
|
49
52
|
raise ResourceNotExistsError()
|
|
50
|
-
return secret
|
|
53
|
+
return CustomORJSONResponse(secret)
|
|
51
54
|
|
|
52
55
|
|
|
53
|
-
@router.post("/create_or_update")
|
|
56
|
+
@router.post("/create_or_update", response_model=Secret)
|
|
54
57
|
async def create_or_update_secret(
|
|
55
58
|
body: CreateOrUpdateSecretRequest,
|
|
56
59
|
session: AsyncSession = Depends(get_session),
|
|
57
60
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectAdmin()),
|
|
58
|
-
)
|
|
61
|
+
):
|
|
59
62
|
_, project = user_project
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
return CustomORJSONResponse(
|
|
64
|
+
await secrets_services.create_or_update_secret(
|
|
65
|
+
session=session,
|
|
66
|
+
project=project,
|
|
67
|
+
name=body.name,
|
|
68
|
+
value=body.value,
|
|
69
|
+
)
|
|
65
70
|
)
|
|
66
71
|
|
|
67
72
|
|
|
@@ -2,6 +2,7 @@ from fastapi import APIRouter
|
|
|
2
2
|
|
|
3
3
|
from dstack._internal import settings
|
|
4
4
|
from dstack._internal.core.models.server import ServerInfo
|
|
5
|
+
from dstack._internal.server.utils.routers import CustomORJSONResponse
|
|
5
6
|
|
|
6
7
|
router = APIRouter(
|
|
7
8
|
prefix="/api/server",
|
|
@@ -9,8 +10,10 @@ router = APIRouter(
|
|
|
9
10
|
)
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
@router.post("/get_info")
|
|
13
|
-
async def get_server_info()
|
|
14
|
-
return
|
|
15
|
-
|
|
13
|
+
@router.post("/get_info", response_model=ServerInfo)
|
|
14
|
+
async def get_server_info():
|
|
15
|
+
return CustomORJSONResponse(
|
|
16
|
+
ServerInfo(
|
|
17
|
+
server_version=settings.DSTACK_VERSION,
|
|
18
|
+
)
|
|
16
19
|
)
|
|
@@ -16,7 +16,10 @@ from dstack._internal.server.schemas.users import (
|
|
|
16
16
|
)
|
|
17
17
|
from dstack._internal.server.security.permissions import Authenticated, GlobalAdmin
|
|
18
18
|
from dstack._internal.server.services import users
|
|
19
|
-
from dstack._internal.server.utils.routers import
|
|
19
|
+
from dstack._internal.server.utils.routers import (
|
|
20
|
+
CustomORJSONResponse,
|
|
21
|
+
get_base_api_additional_responses,
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
router = APIRouter(
|
|
22
25
|
prefix="/api/users",
|
|
@@ -25,41 +28,41 @@ router = APIRouter(
|
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
|
|
28
|
-
@router.post("/list")
|
|
31
|
+
@router.post("/list", response_model=List[User])
|
|
29
32
|
async def list_users(
|
|
30
33
|
session: AsyncSession = Depends(get_session),
|
|
31
34
|
user: UserModel = Depends(Authenticated()),
|
|
32
|
-
)
|
|
33
|
-
return await users.list_users_for_user(session=session, user=user)
|
|
35
|
+
):
|
|
36
|
+
return CustomORJSONResponse(await users.list_users_for_user(session=session, user=user))
|
|
34
37
|
|
|
35
38
|
|
|
36
|
-
@router.post("/get_my_user")
|
|
39
|
+
@router.post("/get_my_user", response_model=User)
|
|
37
40
|
async def get_my_user(
|
|
38
41
|
user: UserModel = Depends(Authenticated()),
|
|
39
|
-
)
|
|
40
|
-
return users.user_model_to_user(user)
|
|
42
|
+
):
|
|
43
|
+
return CustomORJSONResponse(users.user_model_to_user(user))
|
|
41
44
|
|
|
42
45
|
|
|
43
|
-
@router.post("/get_user")
|
|
46
|
+
@router.post("/get_user", response_model=UserWithCreds)
|
|
44
47
|
async def get_user(
|
|
45
48
|
body: GetUserRequest,
|
|
46
49
|
session: AsyncSession = Depends(get_session),
|
|
47
50
|
user: UserModel = Depends(Authenticated()),
|
|
48
|
-
)
|
|
51
|
+
):
|
|
49
52
|
res = await users.get_user_with_creds_by_name(
|
|
50
53
|
session=session, current_user=user, username=body.username
|
|
51
54
|
)
|
|
52
55
|
if res is None:
|
|
53
56
|
raise ResourceNotExistsError()
|
|
54
|
-
return res
|
|
57
|
+
return CustomORJSONResponse(res)
|
|
55
58
|
|
|
56
59
|
|
|
57
|
-
@router.post("/create")
|
|
60
|
+
@router.post("/create", response_model=User)
|
|
58
61
|
async def create_user(
|
|
59
62
|
body: CreateUserRequest,
|
|
60
63
|
session: AsyncSession = Depends(get_session),
|
|
61
64
|
user: UserModel = Depends(GlobalAdmin()),
|
|
62
|
-
)
|
|
65
|
+
):
|
|
63
66
|
res = await users.create_user(
|
|
64
67
|
session=session,
|
|
65
68
|
username=body.username,
|
|
@@ -67,15 +70,15 @@ async def create_user(
|
|
|
67
70
|
email=body.email,
|
|
68
71
|
active=body.active,
|
|
69
72
|
)
|
|
70
|
-
return users.user_model_to_user(res)
|
|
73
|
+
return CustomORJSONResponse(users.user_model_to_user(res))
|
|
71
74
|
|
|
72
75
|
|
|
73
|
-
@router.post("/update")
|
|
76
|
+
@router.post("/update", response_model=User)
|
|
74
77
|
async def update_user(
|
|
75
78
|
body: UpdateUserRequest,
|
|
76
79
|
session: AsyncSession = Depends(get_session),
|
|
77
80
|
user: UserModel = Depends(GlobalAdmin()),
|
|
78
|
-
)
|
|
81
|
+
):
|
|
79
82
|
res = await users.update_user(
|
|
80
83
|
session=session,
|
|
81
84
|
username=body.username,
|
|
@@ -85,19 +88,19 @@ async def update_user(
|
|
|
85
88
|
)
|
|
86
89
|
if res is None:
|
|
87
90
|
raise ResourceNotExistsError()
|
|
88
|
-
return users.user_model_to_user(res)
|
|
91
|
+
return CustomORJSONResponse(users.user_model_to_user(res))
|
|
89
92
|
|
|
90
93
|
|
|
91
|
-
@router.post("/refresh_token")
|
|
94
|
+
@router.post("/refresh_token", response_model=UserWithCreds)
|
|
92
95
|
async def refresh_token(
|
|
93
96
|
body: RefreshTokenRequest,
|
|
94
97
|
session: AsyncSession = Depends(get_session),
|
|
95
98
|
user: UserModel = Depends(Authenticated()),
|
|
96
|
-
)
|
|
99
|
+
):
|
|
97
100
|
res = await users.refresh_user_token(session=session, user=user, username=body.username)
|
|
98
101
|
if res is None:
|
|
99
102
|
raise ResourceNotExistsError()
|
|
100
|
-
return users.user_model_to_user_with_creds(res)
|
|
103
|
+
return CustomORJSONResponse(users.user_model_to_user_with_creds(res))
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
@router.post("/delete")
|
|
@@ -15,7 +15,10 @@ from dstack._internal.server.schemas.volumes import (
|
|
|
15
15
|
ListVolumesRequest,
|
|
16
16
|
)
|
|
17
17
|
from dstack._internal.server.security.permissions import Authenticated, ProjectMember
|
|
18
|
-
from dstack._internal.server.utils.routers import
|
|
18
|
+
from dstack._internal.server.utils.routers import (
|
|
19
|
+
CustomORJSONResponse,
|
|
20
|
+
get_base_api_additional_responses,
|
|
21
|
+
)
|
|
19
22
|
|
|
20
23
|
root_router = APIRouter(
|
|
21
24
|
prefix="/api/volumes",
|
|
@@ -25,12 +28,12 @@ root_router = APIRouter(
|
|
|
25
28
|
project_router = APIRouter(prefix="/api/project/{project_name}/volumes", tags=["volumes"])
|
|
26
29
|
|
|
27
30
|
|
|
28
|
-
@root_router.post("/list")
|
|
31
|
+
@root_router.post("/list", response_model=List[Volume])
|
|
29
32
|
async def list_volumes(
|
|
30
33
|
body: ListVolumesRequest,
|
|
31
34
|
session: AsyncSession = Depends(get_session),
|
|
32
35
|
user: UserModel = Depends(Authenticated()),
|
|
33
|
-
)
|
|
36
|
+
):
|
|
34
37
|
"""
|
|
35
38
|
Returns all volumes visible to user sorted by descending `created_at`.
|
|
36
39
|
`project_name` and `only_active` can be specified as filters.
|
|
@@ -38,36 +41,40 @@ async def list_volumes(
|
|
|
38
41
|
The results are paginated. To get the next page, pass `created_at` and `id` of
|
|
39
42
|
the last fleet from the previous page as `prev_created_at` and `prev_id`.
|
|
40
43
|
"""
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
return CustomORJSONResponse(
|
|
45
|
+
await volumes_services.list_volumes(
|
|
46
|
+
session=session,
|
|
47
|
+
user=user,
|
|
48
|
+
project_name=body.project_name,
|
|
49
|
+
only_active=body.only_active,
|
|
50
|
+
prev_created_at=body.prev_created_at,
|
|
51
|
+
prev_id=body.prev_id,
|
|
52
|
+
limit=body.limit,
|
|
53
|
+
ascending=body.ascending,
|
|
54
|
+
)
|
|
50
55
|
)
|
|
51
56
|
|
|
52
57
|
|
|
53
|
-
@project_router.post("/list")
|
|
58
|
+
@project_router.post("/list", response_model=List[Volume])
|
|
54
59
|
async def list_project_volumes(
|
|
55
60
|
session: AsyncSession = Depends(get_session),
|
|
56
61
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
57
|
-
)
|
|
62
|
+
):
|
|
58
63
|
"""
|
|
59
64
|
Returns all volumes in the project.
|
|
60
65
|
"""
|
|
61
66
|
_, project = user_project
|
|
62
|
-
return
|
|
67
|
+
return CustomORJSONResponse(
|
|
68
|
+
await volumes_services.list_project_volumes(session=session, project=project)
|
|
69
|
+
)
|
|
63
70
|
|
|
64
71
|
|
|
65
|
-
@project_router.post("/get")
|
|
72
|
+
@project_router.post("/get", response_model=Volume)
|
|
66
73
|
async def get_volume(
|
|
67
74
|
body: GetVolumeRequest,
|
|
68
75
|
session: AsyncSession = Depends(get_session),
|
|
69
76
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
70
|
-
)
|
|
77
|
+
):
|
|
71
78
|
"""
|
|
72
79
|
Returns a volume given a volume name.
|
|
73
80
|
"""
|
|
@@ -77,24 +84,26 @@ async def get_volume(
|
|
|
77
84
|
)
|
|
78
85
|
if volume is None:
|
|
79
86
|
raise ResourceNotExistsError()
|
|
80
|
-
return volume
|
|
87
|
+
return CustomORJSONResponse(volume)
|
|
81
88
|
|
|
82
89
|
|
|
83
|
-
@project_router.post("/create")
|
|
90
|
+
@project_router.post("/create", response_model=Volume)
|
|
84
91
|
async def create_volume(
|
|
85
92
|
body: CreateVolumeRequest,
|
|
86
93
|
session: AsyncSession = Depends(get_session),
|
|
87
94
|
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
|
|
88
|
-
)
|
|
95
|
+
):
|
|
89
96
|
"""
|
|
90
97
|
Creates a volume given a volume configuration.
|
|
91
98
|
"""
|
|
92
99
|
user, project = user_project
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
return CustomORJSONResponse(
|
|
101
|
+
await volumes_services.create_volume(
|
|
102
|
+
session=session,
|
|
103
|
+
project=project,
|
|
104
|
+
user=user,
|
|
105
|
+
configuration=body.configuration,
|
|
106
|
+
)
|
|
98
107
|
)
|
|
99
108
|
|
|
100
109
|
|
|
@@ -9,8 +9,8 @@ from dstack._internal.core.models.common import CoreModel
|
|
|
9
9
|
class PollLogsRequest(CoreModel):
|
|
10
10
|
run_name: str
|
|
11
11
|
job_submission_id: UUID4
|
|
12
|
-
start_time: Optional[datetime]
|
|
13
|
-
end_time: Optional[datetime]
|
|
12
|
+
start_time: Optional[datetime] = None
|
|
13
|
+
end_time: Optional[datetime] = None
|
|
14
14
|
descending: bool = False
|
|
15
15
|
next_token: Optional[str] = None
|
|
16
16
|
limit: int = Field(100, ge=0, le=1000)
|
|
@@ -9,12 +9,24 @@ from dstack._internal.core.models.runs import ApplyRunPlanInput, RunSpec
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class ListRunsRequest(CoreModel):
|
|
12
|
-
project_name: Optional[str]
|
|
13
|
-
repo_id: Optional[str]
|
|
14
|
-
username: Optional[str]
|
|
12
|
+
project_name: Optional[str] = None
|
|
13
|
+
repo_id: Optional[str] = None
|
|
14
|
+
username: Optional[str] = None
|
|
15
15
|
only_active: bool = False
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
include_jobs: bool = Field(
|
|
17
|
+
True,
|
|
18
|
+
description=("Whether to include `jobs` in the response"),
|
|
19
|
+
)
|
|
20
|
+
job_submissions_limit: Optional[int] = Field(
|
|
21
|
+
None,
|
|
22
|
+
ge=0,
|
|
23
|
+
description=(
|
|
24
|
+
"Limit number of job submissions returned per job to avoid large responses."
|
|
25
|
+
"Drops older job submissions. No effect with `include_jobs: false`"
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
prev_submitted_at: Optional[datetime] = None
|
|
29
|
+
prev_run_id: Optional[UUID] = None
|
|
18
30
|
limit: int = Field(100, ge=0, le=100)
|
|
19
31
|
ascending: bool = False
|
|
20
32
|
|