Skip to content

Commit 00bb4b3

Browse files
[UX]: Make run status in UI and CLI easier to understand #2716
1 parent 9ce73d1 commit 00bb4b3

2 files changed

Lines changed: 50 additions & 24 deletions

File tree

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

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -351,30 +351,20 @@ def _error(cls, values) -> Dict:
351351

352352
@staticmethod
353353
def _get_error(termination_reason: Optional[JobTerminationReason]) -> Optional[str]:
354-
if termination_reason == JobTerminationReason.INSTANCE_UNREACHABLE:
355-
return "instance unreachable"
356-
elif termination_reason == JobTerminationReason.WAITING_INSTANCE_LIMIT_EXCEEDED:
357-
return "waiting instance limit exceeded"
358-
elif termination_reason == JobTerminationReason.VOLUME_ERROR:
359-
return "waiting runner limit exceeded"
360-
elif termination_reason == JobTerminationReason.GATEWAY_ERROR:
361-
return "gateway error"
362-
elif termination_reason == JobTerminationReason.SCALED_DOWN:
363-
return "scaled down"
364-
elif termination_reason == JobTerminationReason.INACTIVITY_DURATION_EXCEEDED:
365-
return "inactivity duration exceeded"
366-
elif termination_reason == JobTerminationReason.TERMINATED_DUE_TO_UTILIZATION_POLICY:
367-
return "utilization policy"
368-
elif termination_reason == JobTerminationReason.PORTS_BINDING_FAILED:
369-
return "ports binding failed"
370-
elif termination_reason == JobTerminationReason.CREATING_CONTAINER_ERROR:
371-
return "runner error"
372-
elif termination_reason == JobTerminationReason.EXECUTOR_ERROR:
373-
return "executor error"
374-
elif termination_reason == JobTerminationReason.MAX_DURATION_EXCEEDED:
375-
return "max duration exceeded"
376-
else:
377-
return None
354+
error_mapping = {
355+
JobTerminationReason.INSTANCE_UNREACHABLE: "instance unreachable",
356+
JobTerminationReason.WAITING_INSTANCE_LIMIT_EXCEEDED: "waiting instance limit exceeded",
357+
JobTerminationReason.VOLUME_ERROR: "waiting runner limit exceeded",
358+
JobTerminationReason.GATEWAY_ERROR: "gateway error",
359+
JobTerminationReason.SCALED_DOWN: "scaled down",
360+
JobTerminationReason.INACTIVITY_DURATION_EXCEEDED: "inactivity duration exceeded",
361+
JobTerminationReason.TERMINATED_DUE_TO_UTILIZATION_POLICY: "utilization policy",
362+
JobTerminationReason.PORTS_BINDING_FAILED: "ports binding failed",
363+
JobTerminationReason.CREATING_CONTAINER_ERROR: "runner error",
364+
JobTerminationReason.EXECUTOR_ERROR: "executor error",
365+
JobTerminationReason.MAX_DURATION_EXCEEDED: "max duration exceeded",
366+
}
367+
return error_mapping.get(termination_reason)
378368

379369

380370
class Job(CoreModel):

src/tests/_internal/core/models/test_runs.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from dstack._internal.core.models.runs import (
22
JobStatus,
3+
JobSubmission,
34
JobTerminationReason,
5+
Run,
46
RunStatus,
57
RunTerminationReason,
68
)
@@ -22,3 +24,37 @@ def test_job_termination_reason_to_status_works_with_all_enum_varians():
2224
for job_termination_reason in JobTerminationReason:
2325
job_status = job_termination_reason.to_status()
2426
assert isinstance(job_status, JobStatus)
27+
28+
29+
# Will fail if JobTerminationReason value is added without updaing JobSubmission._get_error
30+
def test_get_error_returns_expected_messages():
31+
no_error_reasons = [
32+
JobTerminationReason.FAILED_TO_START_DUE_TO_NO_CAPACITY,
33+
JobTerminationReason.INTERRUPTED_BY_NO_CAPACITY,
34+
JobTerminationReason.WAITING_RUNNER_LIMIT_EXCEEDED,
35+
JobTerminationReason.TERMINATED_BY_USER,
36+
JobTerminationReason.DONE_BY_RUNNER,
37+
JobTerminationReason.ABORTED_BY_USER,
38+
JobTerminationReason.TERMINATED_BY_SERVER,
39+
JobTerminationReason.CONTAINER_EXITED_WITH_ERROR,
40+
]
41+
42+
for reason in JobTerminationReason:
43+
if JobSubmission._get_error(reason) is None:
44+
# Fail no-error reason is not in the list
45+
assert reason in no_error_reasons
46+
47+
48+
# Will fail if RunTerminationReason value is added without updating Run._get_error
49+
def test_run_get_error_returns_none_for_specific_reasons():
50+
no_error_reasons = [
51+
RunTerminationReason.ALL_JOBS_DONE,
52+
RunTerminationReason.JOB_FAILED,
53+
RunTerminationReason.STOPPED_BY_USER,
54+
RunTerminationReason.ABORTED_BY_USER,
55+
]
56+
57+
for reason in RunTerminationReason:
58+
if Run._get_error(reason) is None:
59+
# Fail no-error reason is not in the list
60+
assert reason in no_error_reasons

0 commit comments

Comments
 (0)