Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@
"backend": "Backend",
"region": "Region",
"instance_id": "Instance ID",
"schedule": "Schedule",
"next_run": "Next run",
"resources": "Resources",
"spot": "Spot",
"termination_reason": "Termination reason",
Expand Down
28 changes: 22 additions & 6 deletions frontend/src/pages/Runs/Details/RunDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getRunListItemPrice,
getRunListItemRegion,
getRunListItemResources,
getRunListItemSchedule,
getRunListItemServiceUrl,
getRunListItemSpot,
} from '../../List/helpers';
Expand All @@ -38,6 +39,8 @@ export const RunDetails = () => {
});

const serviceUrl = runData ? getRunListItemServiceUrl(runData) : null;
const schedule = runData ? getRunListItemSchedule(runData) : null;
const nextTriggeredAt = runData ? runData.next_triggered_at : null;

if (isLoadingRun)
return (
Expand Down Expand Up @@ -115,7 +118,7 @@ export const RunDetails = () => {

<div>
<Box variant="awsui-key-label">{t('projects.run.error')}</Box>
<div>{getRunError(runData)}</div>
<div>{getRunError(runData) ?? '-'}</div>
</div>

<div>
Expand All @@ -138,6 +141,11 @@ export const RunDetails = () => {
<div>{getRunListItemResources(runData)}</div>
</div>

<div>
<Box variant="awsui-key-label">{t('projects.run.backend')}</Box>
<div>{getRunListItemBackend(runData)}</div>
</div>

<div>
<Box variant="awsui-key-label">{t('projects.run.region')}</Box>
<div>{getRunListItemRegion(runData)}</div>
Expand All @@ -152,11 +160,6 @@ export const RunDetails = () => {
<Box variant="awsui-key-label">{t('projects.run.spot')}</Box>
<div>{getRunListItemSpot(runData)}</div>
</div>

<div>
<Box variant="awsui-key-label">{t('projects.run.backend')}</Box>
<div>{getRunListItemBackend(runData)}</div>
</div>
</ColumnLayout>

{serviceUrl && (
Expand All @@ -169,6 +172,19 @@ export const RunDetails = () => {
</div>
</ColumnLayout>
)}

{schedule && (
<ColumnLayout columns={4} variant="text-grid">
<div>
<Box variant="awsui-key-label">{t('projects.run.schedule')}</Box>
<div>{schedule}</div>
</div>
<div>
<Box variant="awsui-key-label">{t('projects.run.next_run')}</Box>
<div>{nextTriggeredAt ? format(new Date(nextTriggeredAt), DATE_TIME_FORMAT) : '-'}</div>
</div>
</ColumnLayout>
)}
</Container>

{runData.run_spec.configuration.type === 'dev-environment' && !runIsStopped(runData.status) && (
Expand Down
18 changes: 12 additions & 6 deletions frontend/src/pages/Runs/List/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const getRunListItemResources = (run: IRun) => {
return '-';
}

return run.latest_job_submission?.job_provisioning_data?.instance_type?.resources?.description;
return run.latest_job_submission?.job_provisioning_data?.instance_type?.resources?.description ?? '-';
};

export const getRunListItemSpotLabelKey = (run: IRun) => {
Expand All @@ -31,7 +31,7 @@ export const getRunListItemSpotLabelKey = (run: IRun) => {

export const getRunListItemSpot = (run: IRun) => {
if (run.jobs.length > 1) {
return '';
return '-';
}

return run.latest_job_submission?.job_provisioning_data?.instance_type?.resources?.spot?.toString() ?? '-';
Expand All @@ -57,31 +57,31 @@ export const getRunListItemPrice = (run: IRun) => {

export const getRunListItemInstance = (run: IRun) => {
if (run.jobs.length > 1) {
return '';
return '-';
}

return run.latest_job_submission?.job_provisioning_data?.instance_type?.name;
};

export const getRunListItemInstanceId = (run: IRun) => {
if (run.jobs.length > 1) {
return '';
return '-';
}

return run.latest_job_submission?.job_provisioning_data?.instance_id ?? '-';
};

export const getRunListItemRegion = (run: IRun) => {
if (run.jobs.length > 1) {
return '';
return '-';
}

return run.latest_job_submission?.job_provisioning_data?.region ?? '-';
};

export const getRunListItemBackend = (run: IRun) => {
if (run.jobs.length > 1) {
return '';
return '-';
}

return run.latest_job_submission?.job_provisioning_data?.backend ?? '-';
Expand All @@ -92,3 +92,9 @@ export const getRunListItemServiceUrl = (run: IRun) => {
if (!url) return null;
return url.startsWith('/') ? `${getBaseUrl()}${url}` : url;
};

export const getRunListItemSchedule = (run: IRun) => {
if (run.run_spec.configuration.type != 'task' || !run.run_spec.configuration.schedule) return null;

return run.run_spec.configuration.schedule.cron.join(', ');
};
7 changes: 7 additions & 0 deletions frontend/src/types/run.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,16 +240,22 @@ declare interface IJob {
job_submissions: IJobSubmission[];
}

declare interface ISchedule {
cron: string[];
}

declare interface ITaskConfiguration {
type: 'task';
priority?: number | null;
schedule?: ISchedule | null;
}

declare interface IServiceConfiguration {
type: 'service';
gateway: string | null;
priority?: number | null;
}

declare interface IRunSpec {
configuration: TDevEnvironmentConfiguration | ITaskConfiguration | IServiceConfiguration;
configuration_path: string;
Expand Down Expand Up @@ -286,6 +292,7 @@ declare interface IRun {
cost: number;
service: IRunService | null;
status_message?: string | null;
next_triggered_at?: string | null;
}

declare interface IMetricsItem {
Expand Down
1 change: 1 addition & 0 deletions src/dstack/_internal/core/models/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ class Run(CoreModel):
deployment_num: int = 0 # default for compatibility with pre-0.19.14 servers
error: Optional[str] = None
deleted: Optional[bool] = None
next_triggered_at: Optional[datetime] = None

def is_deployment_in_progress(self) -> bool:
return any(
Expand Down
4 changes: 4 additions & 0 deletions src/dstack/_internal/server/services/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,9 @@ def run_model_to_run(
status_message = _get_run_status_message(run_model)
error = _get_run_error(run_model)
fleet = _get_run_fleet(run_model)
next_triggered_at = None
if not run_model.status.is_finished():
next_triggered_at = _get_next_triggered_at(run_spec)
run = Run(
id=run_model.id,
project_name=run_model.project.name,
Expand All @@ -734,6 +737,7 @@ def run_model_to_run(
deployment_num=run_model.deployment_num,
error=error,
deleted=run_model.deleted,
next_triggered_at=next_triggered_at,
)
run.cost = _get_run_cost(run)
return run
Expand Down
4 changes: 4 additions & 0 deletions src/tests/_internal/server/routers/test_runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ def get_dev_env_run_dict(
"termination_reason": None,
"error": None,
"deleted": deleted,
"next_triggered_at": None,
}


Expand Down Expand Up @@ -665,6 +666,7 @@ async def test_lists_runs(self, test_db, session: AsyncSession, client: AsyncCli
"termination_reason": None,
"error": None,
"deleted": False,
"next_triggered_at": None,
},
{
"id": str(run2.id),
Expand All @@ -687,6 +689,7 @@ async def test_lists_runs(self, test_db, session: AsyncSession, client: AsyncCli
"termination_reason": None,
"error": None,
"deleted": False,
"next_triggered_at": None,
},
]

Expand Down Expand Up @@ -853,6 +856,7 @@ async def test_limits_job_submissions(
"termination_reason": None,
"error": None,
"deleted": False,
"next_triggered_at": None,
},
]

Expand Down