Skip to content

Commit b4b69fb

Browse files
authored
Support gateway events in API, CLI, and UI (#3499)
1 parent 1985341 commit b4b69fb

13 files changed

Lines changed: 90 additions & 2 deletions

File tree

frontend/src/pages/Events/List/hooks/useColumnDefinitions.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,19 @@ export const useColumnsDefinitions = () => {
125125
</div>
126126
);
127127

128+
case 'gateway':
129+
return (
130+
<div>
131+
Gateway{' '}
132+
{target.project_name && (
133+
<NavigateLink href={ROUTES.PROJECT.DETAILS.FORMAT(target.project_name)}>
134+
{target.project_name}
135+
</NavigateLink>
136+
)}
137+
/{target.name}
138+
</div>
139+
);
140+
128141
default:
129142
return '---';
130143
}

frontend/src/pages/Events/List/hooks/useFilters.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type RequestParamsKeys = keyof Pick<
1818
| 'target_runs'
1919
| 'target_jobs'
2020
| 'target_volumes'
21+
| 'target_gateways'
2122
| 'within_projects'
2223
| 'within_fleets'
2324
| 'within_runs'
@@ -33,6 +34,7 @@ const filterKeys: Record<string, RequestParamsKeys> = {
3334
TARGET_RUNS: 'target_runs',
3435
TARGET_JOBS: 'target_jobs',
3536
TARGET_VOLUMES: 'target_volumes',
37+
TARGET_GATEWAYS: 'target_gateways',
3638
WITHIN_PROJECTS: 'within_projects',
3739
WITHIN_FLEETS: 'within_fleets',
3840
WITHIN_RUNS: 'within_runs',
@@ -50,6 +52,7 @@ const multipleChoiseKeys: RequestParamsKeys[] = [
5052
'target_runs',
5153
'target_jobs',
5254
'target_volumes',
55+
'target_gateways',
5356
'within_projects',
5457
'within_fleets',
5558
'within_runs',
@@ -65,6 +68,7 @@ const targetTypes = [
6568
{ label: 'Run', value: 'run' },
6669
{ label: 'Job', value: 'job' },
6770
{ label: 'Volume', value: 'volume' },
71+
{ label: 'Gateway', value: 'gateway' },
6872
];
6973

7074
export const useFilters = () => {
@@ -162,6 +166,11 @@ export const useFilters = () => {
162166
operators: ['='],
163167
propertyLabel: 'Target volumes',
164168
},
169+
{
170+
key: filterKeys.TARGET_GATEWAYS,
171+
operators: ['='],
172+
propertyLabel: 'Target gateways',
173+
},
165174

166175
{
167176
key: filterKeys.WITHIN_PROJECTS,

frontend/src/types/event.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
declare type TEventTargetType = 'project' | 'user' | 'fleet' | 'instance' | 'run' | 'job' | 'volume';
1+
declare type TEventTargetType = 'project' | 'user' | 'fleet' | 'instance' | 'run' | 'job' | 'volume' | 'gateway';
22

33
declare type TEventListRequestParams = Omit<TBaseRequestListParams, 'prev_created_at'> & {
44
prev_recorded_at?: string;
@@ -9,6 +9,7 @@ declare type TEventListRequestParams = Omit<TBaseRequestListParams, 'prev_create
99
target_runs?: string[];
1010
target_jobs?: string[];
1111
target_volumes?: string[];
12+
target_gateways?: string[];
1213
within_projects?: string[];
1314
within_fleets?: string[];
1415
within_runs?: string[];
@@ -17,7 +18,7 @@ declare type TEventListRequestParams = Omit<TBaseRequestListParams, 'prev_create
1718
};
1819

1920
declare interface IEventTarget {
20-
type: 'project' | 'user' | 'fleet' | 'instance' | 'run' | 'job' | 'volume';
21+
type: 'project' | 'user' | 'fleet' | 'instance' | 'run' | 'job' | 'volume' | 'gateway';
2122
project_id?: string;
2223
project_name?: string;
2324
id: string;

src/dstack/_internal/cli/commands/event.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dstack._internal.cli.utils.common import (
77
get_start_time,
88
)
9+
from dstack._internal.core.errors import CLIError
910
from dstack._internal.core.models.events import EventTargetType
1011
from dstack._internal.server.schemas.events import LIST_EVENTS_DEFAULT_LIMIT
1112
from dstack.api import Client
@@ -59,6 +60,13 @@ def _register(self):
5960
dest="target_volumes",
6061
help="Only show events that target the specified volumes",
6162
)
63+
target_filters_group.add_argument(
64+
"--target-gateway",
65+
action="append",
66+
metavar="NAME",
67+
dest="target_gateways",
68+
help="Only show events that target the specified gateways",
69+
)
6270
within_filters_group = parser.add_mutually_exclusive_group()
6371
within_filters_group.add_argument(
6472
"--within-fleet",
@@ -120,6 +128,17 @@ def _build_filters(args: argparse.Namespace, api: Client) -> EventListFilters:
120128
api.client.volumes.get(project_name=api.project, name=name).id
121129
for name in args.target_volumes
122130
]
131+
elif args.target_gateways:
132+
filters.target_gateways = []
133+
for name in args.target_gateways:
134+
id = api.client.gateways.get(api.project, name).id
135+
if id is None:
136+
# TODO(0.21): Remove this check once `Gateway.id` is required.
137+
raise CLIError(
138+
"Cannot determine gateway ID, most likely due to an outdated dstack server."
139+
" Update the server to 0.20.7 or higher or remove --target-gateway."
140+
)
141+
filters.target_gateways.append(id)
123142

124143
if args.within_fleets:
125144
filters.within_fleets = [

src/dstack/_internal/cli/services/events.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class EventListFilters:
1717
target_fleets: Optional[list[uuid.UUID]] = None
1818
target_runs: Optional[list[uuid.UUID]] = None
1919
target_volumes: Optional[list[uuid.UUID]] = None
20+
target_gateways: Optional[list[uuid.UUID]] = None
2021
within_projects: Optional[list[uuid.UUID]] = None
2122
within_fleets: Optional[list[uuid.UUID]] = None
2223
within_runs: Optional[list[uuid.UUID]] = None

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class EventTargetType(str, Enum):
1717
RUN = "run"
1818
JOB = "job"
1919
VOLUME = "volume"
20+
GATEWAY = "gateway"
2021

2122

2223
class EventTarget(CoreModel):

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import datetime
2+
import uuid
23
from enum import Enum
34
from typing import Dict, Optional, Union
45

@@ -93,6 +94,9 @@ class GatewaySpec(CoreModel):
9394

9495

9596
class Gateway(CoreModel):
97+
# ID is only optional on the client side for compatibility with pre-0.20.7 servers.
98+
# TODO(0.21): Make required.
99+
id: Optional[uuid.UUID] = None
96100
name: str
97101
configuration: GatewayConfiguration
98102
created_at: datetime.datetime

src/dstack/_internal/server/routers/events.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ async def list_events(
4545
target_runs=body.target_runs,
4646
target_jobs=body.target_jobs,
4747
target_volumes=body.target_volumes,
48+
target_gateways=body.target_gateways,
4849
within_projects=body.within_projects,
4950
within_fleets=body.within_fleets,
5051
within_runs=body.within_runs,

src/dstack/_internal/server/schemas/events.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ class ListEventsRequest(CoreModel):
9191
max_items=MAX_FILTER_ITEMS,
9292
),
9393
] = None
94+
target_gateways: Annotated[
95+
Optional[list[uuid.UUID]],
96+
Field(
97+
description=(
98+
"List of gateway IDs."
99+
" The response will only include events that target the specified gateways"
100+
),
101+
min_items=MIN_FILTER_ITEMS,
102+
max_items=MAX_FILTER_ITEMS,
103+
),
104+
] = None
94105
within_projects: Annotated[
95106
Optional[list[uuid.UUID]],
96107
Field(

src/dstack/_internal/server/services/events.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
EventModel,
1515
EventTargetModel,
1616
FleetModel,
17+
GatewayModel,
1718
InstanceModel,
1819
JobModel,
1920
MemberModel,
@@ -87,6 +88,7 @@ def __post_init__(self):
8788
def from_model(
8889
model: Union[
8990
FleetModel,
91+
GatewayModel,
9092
InstanceModel,
9193
JobModel,
9294
ProjectModel,
@@ -102,6 +104,13 @@ def from_model(
102104
id=model.id,
103105
name=model.name,
104106
)
107+
if isinstance(model, GatewayModel):
108+
return Target(
109+
type=EventTargetType.GATEWAY,
110+
project_id=model.project_id or model.project.id,
111+
id=model.id,
112+
name=model.name,
113+
)
105114
if isinstance(model, InstanceModel):
106115
return Target(
107116
type=EventTargetType.INSTANCE,
@@ -222,6 +231,7 @@ async def list_events(
222231
target_runs: Optional[list[uuid.UUID]],
223232
target_jobs: Optional[list[uuid.UUID]],
224233
target_volumes: Optional[list[uuid.UUID]],
234+
target_gateways: Optional[list[uuid.UUID]],
225235
within_projects: Optional[list[uuid.UUID]],
226236
within_fleets: Optional[list[uuid.UUID]],
227237
within_runs: Optional[list[uuid.UUID]],
@@ -298,6 +308,13 @@ async def list_events(
298308
EventTargetModel.entity_id.in_(target_volumes),
299309
)
300310
)
311+
if target_gateways is not None:
312+
target_filters.append(
313+
and_(
314+
EventTargetModel.entity_type == EventTargetType.GATEWAY,
315+
EventTargetModel.entity_id.in_(target_gateways),
316+
)
317+
)
301318
if within_projects is not None:
302319
target_filters.append(EventTargetModel.entity_project_id.in_(within_projects))
303320
if within_fleets is not None:

0 commit comments

Comments
 (0)