diff --git a/frontend/src/pages/Events/List/hooks/useColumnDefinitions.tsx b/frontend/src/pages/Events/List/hooks/useColumnDefinitions.tsx
index d6e5b846e..bc9a07aa0 100644
--- a/frontend/src/pages/Events/List/hooks/useColumnDefinitions.tsx
+++ b/frontend/src/pages/Events/List/hooks/useColumnDefinitions.tsx
@@ -138,6 +138,19 @@ export const useColumnsDefinitions = () => {
);
+ case 'secret':
+ return (
+
+ Secret{' '}
+ {target.project_name && (
+
+ {target.project_name}
+
+ )}
+ /{target.name}
+
+ );
+
default:
return '---';
}
diff --git a/frontend/src/pages/Events/List/hooks/useFilters.ts b/frontend/src/pages/Events/List/hooks/useFilters.ts
index d463770b3..e6a97c71e 100644
--- a/frontend/src/pages/Events/List/hooks/useFilters.ts
+++ b/frontend/src/pages/Events/List/hooks/useFilters.ts
@@ -19,6 +19,7 @@ type RequestParamsKeys = keyof Pick<
| 'target_jobs'
| 'target_volumes'
| 'target_gateways'
+ | 'target_secrets'
| 'within_projects'
| 'within_fleets'
| 'within_runs'
@@ -35,6 +36,7 @@ const filterKeys: Record = {
TARGET_JOBS: 'target_jobs',
TARGET_VOLUMES: 'target_volumes',
TARGET_GATEWAYS: 'target_gateways',
+ TARGET_SECRETS: 'target_secrets',
WITHIN_PROJECTS: 'within_projects',
WITHIN_FLEETS: 'within_fleets',
WITHIN_RUNS: 'within_runs',
@@ -53,6 +55,7 @@ const multipleChoiseKeys: RequestParamsKeys[] = [
'target_jobs',
'target_volumes',
'target_gateways',
+ 'target_secrets',
'within_projects',
'within_fleets',
'within_runs',
@@ -69,6 +72,7 @@ const targetTypes = [
{ label: 'Job', value: 'job' },
{ label: 'Volume', value: 'volume' },
{ label: 'Gateway', value: 'gateway' },
+ { label: 'Secret', value: 'secret' },
];
export const useFilters = () => {
@@ -171,6 +175,11 @@ export const useFilters = () => {
operators: ['='],
propertyLabel: 'Target gateways',
},
+ {
+ key: filterKeys.TARGET_SECRETS,
+ operators: ['='],
+ propertyLabel: 'Target secrets',
+ },
{
key: filterKeys.WITHIN_PROJECTS,
diff --git a/frontend/src/types/event.d.ts b/frontend/src/types/event.d.ts
index 618ea6673..dd0147fe1 100644
--- a/frontend/src/types/event.d.ts
+++ b/frontend/src/types/event.d.ts
@@ -1,4 +1,4 @@
-declare type TEventTargetType = 'project' | 'user' | 'fleet' | 'instance' | 'run' | 'job' | 'volume' | 'gateway';
+declare type TEventTargetType = 'project' | 'user' | 'fleet' | 'instance' | 'run' | 'job' | 'volume' | 'gateway' | 'secret';
declare type TEventListRequestParams = Omit & {
prev_recorded_at?: string;
@@ -10,6 +10,7 @@ declare type TEventListRequestParams = Omit EventListFilters:
" Update the server to 0.20.7 or higher or remove --target-gateway."
)
filters.target_gateways.append(id)
+ elif args.target_secrets:
+ filters.target_secrets = [
+ api.client.secrets.get(api.project, name=name).id for name in args.target_secrets
+ ]
if args.within_fleets:
filters.within_fleets = [
diff --git a/src/dstack/_internal/cli/services/events.py b/src/dstack/_internal/cli/services/events.py
index 11f764bd1..0f0eb0f4b 100644
--- a/src/dstack/_internal/cli/services/events.py
+++ b/src/dstack/_internal/cli/services/events.py
@@ -18,6 +18,7 @@ class EventListFilters:
target_runs: Optional[list[uuid.UUID]] = None
target_volumes: Optional[list[uuid.UUID]] = None
target_gateways: Optional[list[uuid.UUID]] = None
+ target_secrets: Optional[list[uuid.UUID]] = None
within_projects: Optional[list[uuid.UUID]] = None
within_fleets: Optional[list[uuid.UUID]] = None
within_runs: Optional[list[uuid.UUID]] = None
diff --git a/src/dstack/_internal/core/models/events.py b/src/dstack/_internal/core/models/events.py
index 289c4fc67..f2efb80d0 100644
--- a/src/dstack/_internal/core/models/events.py
+++ b/src/dstack/_internal/core/models/events.py
@@ -18,6 +18,7 @@ class EventTargetType(str, Enum):
JOB = "job"
VOLUME = "volume"
GATEWAY = "gateway"
+ SECRET = "secret"
class EventTarget(CoreModel):
diff --git a/src/dstack/_internal/server/routers/events.py b/src/dstack/_internal/server/routers/events.py
index 4250eb4d7..036a8b2be 100644
--- a/src/dstack/_internal/server/routers/events.py
+++ b/src/dstack/_internal/server/routers/events.py
@@ -46,6 +46,7 @@ async def list_events(
target_jobs=body.target_jobs,
target_volumes=body.target_volumes,
target_gateways=body.target_gateways,
+ target_secrets=body.target_secrets,
within_projects=body.within_projects,
within_fleets=body.within_fleets,
within_runs=body.within_runs,
diff --git a/src/dstack/_internal/server/schemas/events.py b/src/dstack/_internal/server/schemas/events.py
index 30f7fe324..3899b1f39 100644
--- a/src/dstack/_internal/server/schemas/events.py
+++ b/src/dstack/_internal/server/schemas/events.py
@@ -102,6 +102,17 @@ class ListEventsRequest(CoreModel):
max_items=MAX_FILTER_ITEMS,
),
] = None
+ target_secrets: Annotated[
+ Optional[list[uuid.UUID]],
+ Field(
+ description=(
+ "List of secret IDs."
+ " The response will only include events that target the specified secrets"
+ ),
+ min_items=MIN_FILTER_ITEMS,
+ max_items=MAX_FILTER_ITEMS,
+ ),
+ ] = None
within_projects: Annotated[
Optional[list[uuid.UUID]],
Field(
diff --git a/src/dstack/_internal/server/services/events.py b/src/dstack/_internal/server/services/events.py
index c6d35a457..d46b43e20 100644
--- a/src/dstack/_internal/server/services/events.py
+++ b/src/dstack/_internal/server/services/events.py
@@ -20,6 +20,7 @@
MemberModel,
ProjectModel,
RunModel,
+ SecretModel,
UserModel,
VolumeModel,
)
@@ -93,6 +94,7 @@ def from_model(
JobModel,
ProjectModel,
RunModel,
+ SecretModel,
UserModel,
VolumeModel,
],
@@ -139,6 +141,13 @@ def from_model(
id=model.id,
name=model.run_name,
)
+ if isinstance(model, SecretModel):
+ return Target(
+ type=EventTargetType.SECRET,
+ project_id=model.project_id or model.project.id,
+ id=model.id,
+ name=model.name,
+ )
if isinstance(model, UserModel):
return Target(
type=EventTargetType.USER,
@@ -232,6 +241,7 @@ async def list_events(
target_jobs: Optional[list[uuid.UUID]],
target_volumes: Optional[list[uuid.UUID]],
target_gateways: Optional[list[uuid.UUID]],
+ target_secrets: Optional[list[uuid.UUID]],
within_projects: Optional[list[uuid.UUID]],
within_fleets: Optional[list[uuid.UUID]],
within_runs: Optional[list[uuid.UUID]],
@@ -315,6 +325,13 @@ async def list_events(
EventTargetModel.entity_id.in_(target_gateways),
)
)
+ if target_secrets is not None:
+ target_filters.append(
+ and_(
+ EventTargetModel.entity_type == EventTargetType.SECRET,
+ EventTargetModel.entity_id.in_(target_secrets),
+ )
+ )
if within_projects is not None:
target_filters.append(EventTargetModel.entity_project_id.in_(within_projects))
if within_fleets is not None:
diff --git a/src/dstack/api/server/_events.py b/src/dstack/api/server/_events.py
index d403fb242..5aff9c9d2 100644
--- a/src/dstack/api/server/_events.py
+++ b/src/dstack/api/server/_events.py
@@ -31,6 +31,7 @@ def list(
# NOTE: New parameters go here. Avoid positional parameters, they can break compatibility.
target_volumes: Optional[list[UUID]] = None,
target_gateways: Optional[list[UUID]] = None,
+ target_secrets: Optional[list[UUID]] = None,
) -> list[Event]:
if prev_recorded_at is not None:
# Time zones other than UTC are misinterpreted by the server:
@@ -45,6 +46,7 @@ def list(
target_jobs=target_jobs,
target_volumes=target_volumes,
target_gateways=target_gateways,
+ target_secrets=target_secrets,
within_projects=within_projects,
within_fleets=within_fleets,
within_runs=within_runs,