107 lines
4.2 KiB
Python
107 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""通过 Paramiko 远程验收 live-console / live-edge / live.local。"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import paramiko
|
|
except ImportError:
|
|
print("请先安装: pip install paramiko", file=sys.stderr)
|
|
raise SystemExit(1)
|
|
|
|
HOST = os.environ.get("LIVE_DEPLOY_HOST", os.environ.get("LIVE_VERIFY_HOST", "192.168.21.105"))
|
|
USER = os.environ.get("LIVE_DEPLOY_USER", os.environ.get("LIVE_VERIFY_USER", "live"))
|
|
PASS = os.environ.get("LIVE_DEPLOY_PASS", os.environ.get("LIVE_VERIFY_PASS", "12345678"))
|
|
PORT = os.environ.get("LIVE_VERIFY_PORT", os.environ.get("PORT", "8001"))
|
|
MDNS_HOST = os.environ.get("LIVE_VERIFY_DOMAIN", f"{os.environ.get('LIVE_VERIFY_ALIAS', 'live')}.local")
|
|
LOG = Path(__file__).resolve().parent.parent / ".deploy_logs" / "verify_live_console.log"
|
|
|
|
|
|
def _safe_print(text: str) -> None:
|
|
try:
|
|
print(text)
|
|
except UnicodeEncodeError:
|
|
data = text.encode("utf-8", errors="replace")
|
|
sys.stdout.buffer.write(data + b"\n")
|
|
|
|
|
|
def summarize_deploy_check(raw: str) -> list[str]:
|
|
try:
|
|
data = json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
return ["deploy/check: invalid JSON payload"]
|
|
lines = [
|
|
f"deploy/check: {'READY' if data.get('ready') else 'DEGRADED'}"
|
|
f" ({data.get('pass_count', 0)}/{data.get('total_count', 0)})",
|
|
]
|
|
for item in data.get("checks") or []:
|
|
mark = "OK" if item.get("ok") else "FAIL"
|
|
label = item.get("label") or item.get("id") or "check"
|
|
detail = item.get("detail") or item.get("error") or ""
|
|
lines.append(f"{mark:4} {label}: {detail}")
|
|
for hint in data.get("hints") or []:
|
|
lines.append(f"hint: {hint}")
|
|
return lines
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="远程验收 live-console / live-edge / live.local")
|
|
parser.add_argument("--no-restart", action="store_true", help="只验收,不先重启服务")
|
|
parser.add_argument("--wait", type=float, default=5.0, help="重启后等待秒数,默认 5")
|
|
args = parser.parse_args()
|
|
|
|
LOG.parent.mkdir(parents=True, exist_ok=True)
|
|
c = paramiko.SSHClient()
|
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
c.connect(HOST, username=USER, password=PASS, timeout=30)
|
|
|
|
def run(cmd: str, timeout: int = 90) -> tuple[int, str, str]:
|
|
_, stdout, stderr = c.exec_command(
|
|
"export LC_ALL=C.UTF-8 LANG=C.UTF-8; " + cmd,
|
|
timeout=timeout,
|
|
)
|
|
out = stdout.read().decode("utf-8", errors="replace")
|
|
err = stderr.read().decode("utf-8", errors="replace")
|
|
code = stdout.channel.recv_exit_status()
|
|
block = f"\n$ {cmd}\nexit={code}\n--- out ---\n{out}\n--- err ---\n{err}\n"
|
|
with LOG.open("a", encoding="utf-8", errors="replace") as fh:
|
|
fh.write(block)
|
|
_safe_print("\n".join(block.splitlines()[-80:]))
|
|
return code, out, err
|
|
|
|
try:
|
|
if not args.no_restart:
|
|
run(
|
|
f"echo '{PASS}' | sudo -S systemctl restart live-console.service && "
|
|
f"(echo '{PASS}' | sudo -S systemctl restart live-edge.service || true)",
|
|
timeout=150,
|
|
)
|
|
time.sleep(max(0.0, args.wait))
|
|
|
|
run("systemctl is-active live-console.service", timeout=20)
|
|
run("systemctl is-active live-edge.service || true", timeout=20)
|
|
run(f"getent hosts {MDNS_HOST} || true", timeout=20)
|
|
run(f"ss -tlnp 2>/dev/null | grep -E ':80\\b|:{PORT}\\b' || true", timeout=20)
|
|
run(f"curl -sS -m 10 http://127.0.0.1:{PORT}/health", timeout=30)
|
|
run("curl -sS -m 10 -o /dev/null -w '127.0.0.1:80 /health -> HTTP %{http_code}\\n' http://127.0.0.1/health || true", timeout=30)
|
|
run(
|
|
f"curl -sS -m 10 -o /dev/null -w '{MDNS_HOST} /health -> HTTP %{{http_code}}\\n' http://{MDNS_HOST}/health || true",
|
|
timeout=30,
|
|
)
|
|
code, deploy_out, _ = run(f"curl -sS -m 12 http://127.0.0.1:{PORT}/deploy/check", timeout=40)
|
|
if code == 0:
|
|
_safe_print("\n".join(summarize_deploy_check(deploy_out)))
|
|
return 0
|
|
finally:
|
|
c.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|