gitea: fix LFS round-trip post-restart wait (stale on 28.0.0)
continuous-integration/drone/push Build is failing
continuous-integration/drone/push Build is failing
test_lfs_roundtrip's post-restart poll timed out on gitea 28.0.0 (PR #10 run #31): _api() caught only HTTPError, so a single urlopen(timeout=20) socket timeout inside the poll raised and aborted the poll itself instead of being retried; the poll also hard-coded a 120s deadline + a 20s request timeout, contradicting the recipe's declared HTTP_TIMEOUT=600 (tests/STYLE.md §5-sized for the warm custom tier; the 28.0.0 boot runs a longer migration window than 1.27.3). Fix, assertion untouched (§3): - _api() is never-raising like lifecycle.http_fetch: transient URLError/socket timeout/OSError -> (0, {}) so the bounded poll simply retries (each request still bounded at 20s; the poll's deadline is what bounds the wait). - poll deadline derives from recipe_meta.HTTP_TIMEOUT (600) instead of a hard-coded 120, probing the same /api/v1/version path recipe_meta.READY_PROBE declares. Evidence: gitea 28.0.0 dev deploy converged + served /api/healthz and /version (gitea-upgrade-2026-10-02.md 2b); the RED run #31 failed only in this poll while install/upgrade/backup/restore and every other custom test passed.
This commit is contained in:
@@ -23,6 +23,7 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
@@ -45,7 +46,15 @@ def _lfs_available() -> bool:
|
||||
return os.path.exists(os.path.join(abra_dir, "recipes", "gitea", _LFS_OVERLAY))
|
||||
|
||||
|
||||
def _api(domain, path, method="GET", body=None, user="", password=""):
|
||||
API_TIMEOUT = 20
|
||||
|
||||
|
||||
def _api(domain, path, method="GET", body=None, user="", password="", timeout=API_TIMEOUT):
|
||||
"""One API call → (status, JSON). Never raises: transient errors (connection refused /
|
||||
dropped mid-restart, DNS, socket timeout) return (0, {}) so a caller's bounded poll can
|
||||
simply retry, mirroring lifecycle.http_fetch. Only HTTPError was caught before; a socket
|
||||
timeout on a slow post-restart boot (gitea 28.0.0 migration window, PR #10 run #31) escaped
|
||||
as TimeoutError and aborted the restart poll itself."""
|
||||
data = json.dumps(body).encode() if body is not None else None
|
||||
auth = base64.b64encode(f"{user}:{password}".encode()).decode()
|
||||
headers = {"Authorization": f"Basic {auth}"}
|
||||
@@ -55,7 +64,7 @@ def _api(domain, path, method="GET", body=None, user="", password=""):
|
||||
f"https://{domain}/api/v1{path}", data=data, headers=headers, method=method
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=20, context=_CTX) as r:
|
||||
with urllib.request.urlopen(req, timeout=timeout, context=_CTX) as r:
|
||||
raw = r.read()
|
||||
return r.status, (json.loads(raw) if raw else {})
|
||||
except urllib.error.HTTPError as e:
|
||||
@@ -64,6 +73,8 @@ def _api(domain, path, method="GET", body=None, user="", password=""):
|
||||
return e.code, json.loads(raw)
|
||||
except (ValueError, json.JSONDecodeError):
|
||||
return e.code, {}
|
||||
except (urllib.error.URLError, TimeoutError, OSError): # TimeoutError ⊃ socket.timeout (UP041)
|
||||
return 0, {}
|
||||
|
||||
|
||||
def _run_git(args, cwd, env=None):
|
||||
@@ -164,6 +175,8 @@ def test_lfs_roundtrip(live_app):
|
||||
finally:
|
||||
shutil.rmtree(fresh_dir, ignore_errors=True)
|
||||
|
||||
import recipe_meta # noqa: E402 — per-recipe declared timeouts (tests/STYLE.md §5)
|
||||
|
||||
# 7. JWT-secret stability: restart gitea + assert LFS_JWT_SECRET unchanged.
|
||||
# Read the current secret from inside the container's rendered app.ini.
|
||||
current_jwt = lifecycle.exec_in_app(
|
||||
@@ -182,14 +195,15 @@ def test_lfs_roundtrip(live_app):
|
||||
capture_output=True,
|
||||
timeout=120,
|
||||
)
|
||||
# Wait for gitea to come back up
|
||||
|
||||
# Re-read meta from the live_app fixture (meta is not in scope here — use the stored meta)
|
||||
import time
|
||||
|
||||
deadline = time.time() + 120
|
||||
# Wait for gitea to come back up. Window + probe are DERIVED from the recipe's declared
|
||||
# readiness (recipe_meta.HTTP_TIMEOUT / READY_PROBE — the /api/v1/version readiness that
|
||||
# recipe_meta already declares for the harness, not a guess) — tests/STYLE.md §5.
|
||||
wait_timeout = int(getattr(recipe_meta, "HTTP_TIMEOUT", 300))
|
||||
probe_path = "/version" # READY_PROBE's readiness path (rules carry only ok + domain)
|
||||
deadline = time.time() + wait_timeout
|
||||
status2 = 0
|
||||
while time.time() < deadline:
|
||||
status2, _ = _api(live_app, "/version", user=user, password=password)
|
||||
status2, _ = _api(live_app, probe_path, user=user, password=password)
|
||||
if status2 == 200:
|
||||
break
|
||||
time.sleep(5)
|
||||
|
||||
Reference in New Issue
Block a user