wip(admin-config-tool): task 6.3 HA E2E test (blocked, flaky primary-push)

Standby-redirect half is done and correct: OP_upgradeConfig has no
server-side redirect wiring today, matches design.md's own documented
"Out of Boundary" gap, and this test locks in the real current
rejection behavior.

Primary-push success-path half is unreliable (~24/25 live fails in one
investigation round, see bug-1485) -- the elected primary was observed
as a <defunct> zombie shortly after promotion, but a dedicated debug
pass could not reproduce it in 26+ clean runs on an idle host and
captured no crash evidence either way. tasks.md marks 6.3 _Blocked:_
pending re-verification on an idle host with real stderr capture.
This commit is contained in:
Rongsong Shen 2026-07-31 08:09:36 +00:00
parent 0206b9e166
commit 34bd934bc7
3 changed files with 603 additions and 53 deletions

View file

@ -49,65 +49,20 @@ fix).
from __future__ import annotations
import re
import shutil
import socket
import subprocess
import time
from pathlib import Path
import pytest
SRC_DIR = Path(__file__).resolve().parents[2]
REPO_CONFIGS_DIR = SRC_DIR / "configs"
DEPLOY_HA_SH = SRC_DIR / "tools" / "deploy-ha.sh"
# Mirrors deploy-ha.sh's own xceadmin discovery order (src/tools/deploy-ha.sh)
# -- xce.build (asan, the default profile) first, then release, then debug.
_CONTROLLER_CANDIDATES = [
SRC_DIR / "xce.build" / "controller" / "xce_controller",
SRC_DIR / "build.release" / "controller" / "xce_controller",
SRC_DIR / "build.dbg" / "controller" / "xce_controller",
]
_XCEADMIN_CANDIDATES = [
SRC_DIR / "xce.build" / "xceadmin" / ".venv" / "bin" / "xceadmin",
SRC_DIR / "build.release" / "xceadmin" / ".venv" / "bin" / "xceadmin",
SRC_DIR / "build.dbg" / "xceadmin" / ".venv" / "bin" / "xceadmin",
]
_READY_TIMEOUT_S = 20.0
_VERSION_TAG_RE = re.compile(r"versionTag=(\d+)\s+ackedCount=(\d+)")
def _find_first_executable(candidates: list[Path]) -> Path | None:
for candidate in candidates:
if candidate.is_file() and (candidate.stat().st_mode & 0o111):
return candidate
return None
def _free_tcp_port() -> int:
"""A port that was free at bind-time -- the same best-effort ephemeral-
port trick used to avoid hardcoding a fixed port this test could
collide on. The bind-then-immediately-close TOCTOU window is
acceptable for a single-shot test deploy.
"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
probe.bind(("127.0.0.1", 0))
return probe.getsockname()[1]
def _wait_for_port(host: str, port: int, timeout_s: float) -> None:
deadline = time.monotonic() + timeout_s
last_err: OSError | None = None
while time.monotonic() < deadline:
try:
with socket.create_connection((host, port), timeout=1.0):
return
except OSError as exc:
last_err = exc
time.sleep(0.2)
raise TimeoutError(f"controller never opened {host}:{port}: {last_err}")
from _live_e2e_common import CONTROLLER_CANDIDATES as _CONTROLLER_CANDIDATES
from _live_e2e_common import DEPLOY_HA_SH, REPO_CONFIGS_DIR
from _live_e2e_common import READY_TIMEOUT_S as _READY_TIMEOUT_S
from _live_e2e_common import VERSION_TAG_RE as _VERSION_TAG_RE
from _live_e2e_common import XCEADMIN_CANDIDATES as _XCEADMIN_CANDIDATES
from _live_e2e_common import find_first_executable as _find_first_executable
from _live_e2e_common import free_tcp_port as _free_tcp_port
from _live_e2e_common import wait_for_port as _wait_for_port
@pytest.fixture