Skip to content

Commit c965216

Browse files
committed
test(haos): retry initial Supervisor readiness
1 parent 4c86717 commit c965216

2 files changed

Lines changed: 52 additions & 30 deletions

File tree

tests/haos_image_build/build_image.py

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,30 +1800,16 @@ def _wait_supervisor_ready(
18001800
operation. Beta image configuration uses the same predicate after channel
18011801
selection and update.
18021802
"""
1803-
info = ws.supervisor_api("/supervisor/info", method="get", timeout=30.0)
1804-
LOG.info(
1805-
"Supervisor ready: version=%s version_latest=%s arch=%s",
1806-
info.get("version"),
1807-
info.get("version_latest"),
1808-
info.get("arch"),
1809-
)
1810-
if _supervisor_info_ready(
1811-
info,
1812-
expected_channel=expected_channel,
1813-
minimum_version=minimum_version,
1814-
):
1815-
return info
1816-
1817-
LOG.info(
1818-
"Supervisor self-update pending (%s -> %s); waiting before store ops...",
1819-
info.get("version"),
1820-
info.get("version_latest"),
1821-
)
18221803
deadline = time.monotonic() + update_timeout
1823-
last_version = info.get("version")
1804+
info: dict[str, Any] = {}
1805+
last_version: object = None
18241806
last_error: BaseException | None = None
1825-
while time.monotonic() < deadline:
1826-
time.sleep(10.0)
1807+
first_probe = True
1808+
first_success = True
1809+
while first_probe or time.monotonic() < deadline:
1810+
if not first_probe:
1811+
time.sleep(10.0)
1812+
first_probe = False
18271813
try:
18281814
info = ws.supervisor_api("/supervisor/info", method="get", timeout=30.0)
18291815
except _SUPERVISOR_WAIT_TRANSIENT_ERRORS as e:
@@ -1845,14 +1831,30 @@ def _wait_supervisor_ready(
18451831
last_error = reconnect_err
18461832
continue
18471833
version = info.get("version")
1848-
if version != last_version:
1849-
LOG.info("Supervisor version changed: %s -> %s", last_version, version)
1850-
last_version = version
1851-
if _supervisor_info_ready(
1834+
ready = _supervisor_info_ready(
18521835
info,
18531836
expected_channel=expected_channel,
18541837
minimum_version=minimum_version,
1855-
):
1838+
)
1839+
if first_success:
1840+
LOG.info(
1841+
"Supervisor ready: version=%s version_latest=%s arch=%s",
1842+
info.get("version"),
1843+
info.get("version_latest"),
1844+
info.get("arch"),
1845+
)
1846+
first_success = False
1847+
if ready:
1848+
return info
1849+
LOG.info(
1850+
"Supervisor self-update pending (%s -> %s); waiting before store ops...",
1851+
info.get("version"),
1852+
info.get("version_latest"),
1853+
)
1854+
elif version != last_version:
1855+
LOG.info("Supervisor version changed: %s -> %s", last_version, version)
1856+
last_version = version
1857+
if ready:
18561858
LOG.info("Supervisor self-update complete: version=%s", version)
18571859
return info
18581860
last_err_suffix = f"; last error: {last_error!r}" if last_error else ""

tests/src/unit/test_haos_supervisor_wait.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,27 @@ def test_returns_immediately_when_up_to_date() -> None:
263263
sleep.assert_not_called()
264264

265265

266+
def test_tolerates_setup_state_from_initial_probe() -> None:
267+
"""A setup-state error on the first readiness probe is retried."""
268+
ws = Mock()
269+
ws.supervisor_api.side_effect = [
270+
WSCommandError(
271+
"not ready",
272+
code="unknown_error",
273+
supervisor_message="System is not ready with state: setup",
274+
),
275+
_info(update_available=False),
276+
]
277+
278+
with patch("tests.haos_image_build.build_image.time.sleep") as sleep:
279+
result = _wait_supervisor_ready(ws)
280+
281+
assert result["version"] == "2026.06.1"
282+
assert ws.supervisor_api.call_count == 2
283+
sleep.assert_called_once_with(10.0)
284+
ws.reconnect.assert_called_once_with()
285+
286+
266287
def test_waits_until_update_clears() -> None:
267288
"""Polls /supervisor/info until update_available flips False."""
268289
ws = Mock()
@@ -318,9 +339,8 @@ def test_raises_on_update_timeout() -> None:
318339
def test_persistent_error_surfaced_in_timeout() -> None:
319340
"""Persistent WSCommandError -> timeout message includes last error."""
320341
ws = Mock()
321-
# Initial read sees a pending update; the poll then hits a persistent
322-
# WSCommandError until the deadline (the initial read is outside the
323-
# tolerant loop, so it must succeed for the loop to be exercised).
342+
# Initial read sees a pending update; later probes hit a transient
343+
# WSCommandError until the deadline.
324344
ws.supervisor_api.side_effect = [
325345
_info(update_available=True, version="2026.05.1"),
326346
WSCommandError(

0 commit comments

Comments
 (0)