Skip to content

Commit 1763a9a

Browse files
committed
builder: Use "failed" on failure to ensure front-end indicates failure
Converts `ProgressEvent.BuildStatus` into a string Enum, and uses Enum values to ensure consistent use of phases. Note repo2docker uses `"failure"` instead of `"failed"` so the event source still has a mix of failed/failure
1 parent 631d47e commit 1763a9a

File tree

3 files changed

+40
-8
lines changed

3 files changed

+40
-8
lines changed

binderhub/build.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@ class BuildStatus(Enum):
4040
The state the build is now in
4141
4242
Used when `kind` is `Kind.BUILD_STATUS_CHANGE`
43+
44+
These enum values are referenced in the front-end to display the build status,
45+
hence the mismatch between the Enum name (backend) and value (frontend).
4346
"""
4447

45-
PENDING = 1
46-
RUNNING = 2
47-
COMPLETED = 3
48-
FAILED = 4
49-
UNKNOWN = 5
48+
PENDING = "pending"
49+
RUNNING = "running"
50+
COMPLETED = "built"
51+
FAILED = "failed"
52+
UNKNOWN = "unknown"
5053

5154
def __init__(self, kind: Kind, payload: Union[str, BuildStatus]):
5255
self.kind = kind

binderhub/builder.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -509,12 +509,13 @@ def _check_result(future):
509509
# FIXME: If pod goes into an unrecoverable stage, such as ImagePullBackoff or
510510
# whatever, we should fail properly.
511511
if progress.kind == ProgressEvent.Kind.BUILD_STATUS_CHANGE:
512+
phase = progress.payload.value
512513
if progress.payload == ProgressEvent.BuildStatus.PENDING:
513514
# nothing to do, just waiting
514515
continue
515516
elif progress.payload == ProgressEvent.BuildStatus.COMPLETED:
516517
event = {
517-
"phase": "built",
518+
"phase": phase,
518519
"message": "Built image, launching...\n",
519520
"imageName": image_name,
520521
}
@@ -529,9 +530,13 @@ def _check_result(future):
529530
# Do nothing, is ok!
530531
continue
531532
elif progress.payload == ProgressEvent.BuildStatus.FAILED:
532-
event = {"phase": "failure"}
533+
event = {"phase": phase}
533534
elif progress.payload == ProgressEvent.BuildStatus.UNKNOWN:
534-
event = {"phase": "unknown"}
535+
event = {"phase": phase}
536+
else:
537+
raise ValueError(
538+
f"Found unknown phase {phase} in ProgressEvent"
539+
)
535540
elif progress.kind == ProgressEvent.Kind.LOG_MESSAGE:
536541
# The logs are coming out of repo2docker, so we expect
537542
# them to be JSON structured anyway

binderhub/tests/test_build.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,30 @@ async def test_build(app, needs_build, needs_launch, always_build, slug, pytestc
9494
assert r.url.startswith(final["url"])
9595

9696

97+
@pytest.mark.asyncio(timeout=120)
98+
@pytest.mark.remote
99+
async def test_build_fail(app, needs_build, needs_launch, always_build, pytestconfig):
100+
"""
101+
Test build a repo that should fail immediately.
102+
"""
103+
slug = "gist/manics/cc003c8264db20d22b5fa437770f655e"
104+
build_url = f"{app.url}/build/{slug}"
105+
r = await async_requests.get(build_url, stream=True)
106+
r.raise_for_status()
107+
failed_events = 0
108+
async for line in async_requests.iter_lines(r):
109+
line = line.decode("utf8", "replace")
110+
if line.startswith("data:"):
111+
event = json.loads(line.split(":", 1)[1])
112+
assert event.get("phase") not in ("launching", "ready")
113+
if event.get("phase") == "failed":
114+
failed_events += 1
115+
break
116+
r.close()
117+
118+
assert failed_events > 0, "Should have seen phase 'failed'"
119+
120+
97121
def _list_dind_pods_mock():
98122
"""Mock list of DIND pods"""
99123
mock_response = mock.MagicMock()

0 commit comments

Comments
 (0)