Skip to content

Commit 418076d

Browse files
fix: retry azure log fetch in e2e
1 parent 0bd9ada commit 418076d

2 files changed

Lines changed: 100 additions & 19 deletions

File tree

scripts/e2e_litellm_entry.py

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -401,26 +401,81 @@ def docker_logs(container: str, tail: int) -> str:
401401
return completed.stdout
402402

403403

404+
RETRYABLE_AZURE_LOG_PATTERNS = (
405+
"connecttimeouterror",
406+
"connection to management.azure.com timed out",
407+
"max retries exceeded",
408+
"read timed out",
409+
"serviceunavailable",
410+
"gateway timeout",
411+
"temporarily unavailable",
412+
"too many requests",
413+
"http 429",
414+
"http 5",
415+
)
416+
NON_RETRYABLE_AZURE_LOG_PATTERNS = (
417+
"authorizationfailed",
418+
"forbidden",
419+
"invalidauthenticationtoken",
420+
"please run 'az login'",
421+
"resource not found",
422+
"resourcenotfound",
423+
)
424+
425+
426+
def azure_log_error_output(exc: BaseException) -> str:
427+
if isinstance(exc, subprocess.CalledProcessError):
428+
return str(exc.output or "")
429+
if isinstance(exc, subprocess.TimeoutExpired):
430+
output = exc.output or exc.stderr or ""
431+
if isinstance(output, bytes):
432+
return output.decode("utf-8", errors="replace")
433+
return str(output)
434+
return str(exc)
435+
436+
437+
def retryable_azure_log_error(exc: BaseException) -> bool:
438+
if isinstance(exc, subprocess.TimeoutExpired):
439+
return True
440+
text = azure_log_error_output(exc).lower()
441+
if any(pattern in text for pattern in NON_RETRYABLE_AZURE_LOG_PATTERNS):
442+
return False
443+
return any(pattern in text for pattern in RETRYABLE_AZURE_LOG_PATTERNS)
444+
445+
404446
def azure_containerapp_logs(name: str, resource_group: str, tail: int) -> str:
405-
completed = subprocess.run(
406-
[
407-
"az",
408-
"containerapp",
409-
"logs",
410-
"show",
411-
"--name",
412-
name,
413-
"--resource-group",
414-
resource_group,
415-
"--tail",
416-
str(tail),
417-
],
418-
check=True,
419-
stdout=subprocess.PIPE,
420-
stderr=subprocess.STDOUT,
421-
text=True,
422-
)
423-
return completed.stdout
447+
command = [
448+
"az",
449+
"containerapp",
450+
"logs",
451+
"show",
452+
"--name",
453+
name,
454+
"--resource-group",
455+
resource_group,
456+
"--tail",
457+
str(tail),
458+
]
459+
attempts = 3
460+
last_exc: BaseException | None = None
461+
for attempt in range(1, attempts + 1):
462+
try:
463+
completed = subprocess.run(
464+
command,
465+
check=True,
466+
stdout=subprocess.PIPE,
467+
stderr=subprocess.STDOUT,
468+
text=True,
469+
errors="replace",
470+
)
471+
return completed.stdout
472+
except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as exc:
473+
last_exc = exc
474+
if attempt >= attempts or not retryable_azure_log_error(exc):
475+
break
476+
time.sleep(2.0 * (2 ** (attempt - 1)))
477+
assert last_exc is not None
478+
raise last_exc
424479

425480

426481
def collect_logs(

tests/test_e2e_litellm_entry.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import subprocess
4+
35
from scripts.e2e_litellm_entry import (
46
CheckResult,
57
Probe,
@@ -481,6 +483,30 @@ def test_collect_logs_requires_azure_details():
481483
)
482484

483485

486+
def test_azure_containerapp_logs_retries_transient_failures(monkeypatch):
487+
from scripts import e2e_litellm_entry as script
488+
489+
calls: list[int] = []
490+
491+
def fake_run(*args, **kwargs):
492+
calls.append(1)
493+
if len(calls) == 1:
494+
raise subprocess.CalledProcessError(
495+
1,
496+
args[0],
497+
output="ConnectTimeoutError: Connection to management.azure.com timed out",
498+
)
499+
return subprocess.CompletedProcess(args[0], 0, stdout='{"event":"route_complete"}\n')
500+
501+
monkeypatch.setattr(script.subprocess, "run", fake_run)
502+
monkeypatch.setattr(script.time, "sleep", lambda seconds: None)
503+
504+
output = script.azure_containerapp_logs("imux", "rg", 10)
505+
506+
assert output.startswith('{"event"')
507+
assert len(calls) == 2
508+
509+
484510
def test_run_e2e_uses_provider_router_request_id_for_strict_log_match(monkeypatch):
485511
monkeypatch.setattr(
486512
"scripts.e2e_litellm_entry.httpx.Client",

0 commit comments

Comments
 (0)