Skip to content

Commit 03e0e55

Browse files
erlenlhCopilot
andcommitted
Record when a workflow job was cancelled, not just failed
A cooperatively-cancelled internal job returns normally, so hasFailed() stays False and the job was indistinguishable from one that ran to completion. WorkflowJobResult and RunModelWorkflowLogEvent now carry a 'cancelled' flag alongside 'failed', and the persisted log entry status reflects it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 74d8bfe commit 03e0e55

5 files changed

Lines changed: 28 additions & 1 deletion

File tree

src/ert/run_models/event.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,17 @@ class RunModelWorkflowLogEvent(BaseModel, extra="forbid"):
106106
stdout: str
107107
stderr: str
108108
failed: bool
109+
cancelled: bool = False
109110
timestamp: datetime
110111
iteration: int | None = None
111112

112113
def as_log_entry(self) -> str:
113-
status = "failed" if self.failed else "success"
114+
if self.cancelled:
115+
status = "cancelled"
116+
elif self.failed:
117+
status = "failed"
118+
else:
119+
status = "success"
114120
header = (
115121
f"=== {self.timestamp.isoformat(timespec='seconds')} {self.hook} "
116122
f"workflow={self.workflow_name} job={self.job_name}#{self.job_index} "

src/ert/run_models/run_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,7 @@ def _send_workflow_log_events(
887887
stdout=result.stdout,
888888
stderr=result.stderr,
889889
failed=result.failed,
890+
cancelled=result.cancelled,
890891
timestamp=result.timestamp,
891892
iteration=iteration,
892893
)

src/ert/workflow_runner.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class WorkflowJobResult:
2929
stdout: str
3030
stderr: str
3131
failed: bool
32+
cancelled: bool = False
3233
timestamp: datetime.datetime = field(
3334
default_factory=lambda: datetime.datetime.now(tz=datetime.UTC)
3435
)
@@ -171,6 +172,7 @@ def run_blocking(self) -> None:
171172
if not self.__cancelled:
172173
logger.info(f"Workflow job {jobrunner.name} starting")
173174
jobrunner.run(args, fixtures=self.fixtures)
175+
job_was_cancelled = self.__cancelled
174176
self.__status[jobrunner.name] = {
175177
"stdout": jobrunner.stdoutdata(),
176178
"stderr": jobrunner.stderrdata(),
@@ -184,6 +186,7 @@ def run_blocking(self) -> None:
184186
stdout=jobrunner.stdoutdata(),
185187
stderr=jobrunner.stderrdata(),
186188
failed=jobrunner.hasFailed(),
189+
cancelled=job_was_cancelled,
187190
)
188191
)
189192

@@ -205,6 +208,10 @@ def run_blocking(self) -> None:
205208
)
206209

207210
logger.error(f"Workflow job {jobrunner.name} failed", extra=info)
211+
elif job_was_cancelled:
212+
logger.info(
213+
f"Workflow job {jobrunner.name} was cancelled", extra=info
214+
)
208215
else:
209216
logger.info(
210217
f"Workflow job {jobrunner.name} completed successfully",

tests/ert/unit_tests/run_models/test_workflow_log_event.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ def test_that_workflow_log_entry_header_states_hook_workflow_job_and_status():
5353
)
5454

5555

56+
def test_that_a_cancelled_job_is_reported_as_cancelled_even_if_not_failed():
57+
entry = _event(cancelled=True, failed=False).as_log_entry()
58+
59+
assert "status=cancelled" in entry.splitlines()[0]
60+
61+
5662
def test_that_workflow_log_entries_are_separated_by_a_blank_line():
5763
first = _event(job_name="FIRST", stdout="first\n").as_log_entry()
5864
second = _event(job_name="SECOND", stdout="second\n").as_log_entry()

tests/ert/unit_tests/workflow_runner/test_workflow_runner.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ def test_that_job_results_contain_one_entry_per_job_invocation():
228228
]
229229
assert [result.stdout for result in results] == ["Hello World\n", "Hello World\n"]
230230
assert not any(result.failed for result in results)
231+
assert not any(result.cancelled for result in results)
231232

232233

233234
@pytest.mark.slow
@@ -267,6 +268,12 @@ def test_workflow_thread_cancel_ert_script():
267268
assert not Path("wait_cancelled_2").exists()
268269
assert not Path("wait_finished_2").exists()
269270

271+
results = {result.index: result for result in workflow_runner.workflowJobResults()}
272+
assert results[0].cancelled is False
273+
assert results[0].failed is False
274+
assert results[1].cancelled is True
275+
assert results[1].failed is False
276+
270277

271278
@pytest.mark.slow
272279
@pytest.mark.usefixtures("use_tmpdir")

0 commit comments

Comments
 (0)