diff --git a/vendor/easy-asterisk/docker/entrypoint.sh b/vendor/easy-asterisk/docker/entrypoint.sh index 943b3d6..7bdf744 100755 --- a/vendor/easy-asterisk/docker/entrypoint.sh +++ b/vendor/easy-asterisk/docker/entrypoint.sh @@ -464,7 +464,20 @@ fi # ── 11. Signal handling for clean shutdown ──────────────────── cleanup() { log_info "Shutting down..." - pkill -f "easy-asterisk-webadmin" 2>/dev/null || true + # pkill only sends the signal and returns immediately — it doesn't wait + # for the process to actually exit and release its listening socket. + # Under network_mode: host there's no Docker-managed port mapping to + # tear down, so the next container's bind attempt races however long + # this process actually takes to die. Wait for it (briefly) instead of + # racing the next container's startup — it retries too now, but a + # clean handoff here means it usually shouldn't need to. + if pkill -f "easy-asterisk-webadmin" 2>/dev/null; then + for i in $(seq 1 20); do + pgrep -f "easy-asterisk-webadmin" >/dev/null 2>&1 || break + sleep 0.1 + done + pkill -9 -f "easy-asterisk-webadmin" 2>/dev/null || true + fi asterisk -rx "core stop now" 2>/dev/null || true exit 0 } diff --git a/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh b/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh index a3b1c55..d16fa12 100755 --- a/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh +++ b/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh @@ -4116,6 +4116,7 @@ import json import subprocess import os import re +import time import base64 import hashlib import html @@ -6134,7 +6135,25 @@ class WebAdminHandler(http.server.BaseHTTPRequestHandler): self.wfile.write(json.dumps(data).encode()) def main(): - with socketserver.TCPServer(("", PORT), WebAdminHandler) as httpd: + # A container recreate under network_mode: host depends on the *previous* + # container's web admin process actually dying before the port is free — + # there's no Docker-managed port mapping to instantly release it like + # there would be in bridge mode. A fast recreate-right-after-recreate can + # briefly race that teardown. Retry instead of crashing on the first + # failure, which otherwise means the web admin silently never comes up. + httpd = None + last_err = None + for attempt in range(10): + try: + httpd = socketserver.TCPServer(("", PORT), WebAdminHandler) + break + except OSError as e: + last_err = e + print(f"Port {PORT} not free yet ({e}), retrying...") + time.sleep(2) + if httpd is None: + raise last_err + with httpd: print(f"Easy Asterisk Web Admin running on port {PORT}") httpd.serve_forever()