test: add go production acceptance smoke

This commit is contained in:
lingniu
2026-07-02 01:53:43 +08:00
parent 96514898eb
commit f4d9c9ef5a
4 changed files with 209 additions and 0 deletions

View File

@@ -154,6 +154,14 @@ curl -sS 'http://115.29.187.205:20210/api/stats/daily-metrics?dateFrom=2026-07-0
部署后推荐直接运行 Go 原生生产 smoke
```bash
python3 tools/go_prod_acceptance.py --date 2026-07-02
```
该聚合脚本会串行执行 systemd/端口检查、Kafka topic/consumer lag 检查、HTTP 数据链路检查,任一子检查失败时整体退出码为非 0。需要 SSH 可登录应用 ECS 和 Kafka ECS。
也可以单独运行子检查:
```bash
python3 tools/go_systemd_prod_smoke.py --host 115.29.187.205 --user root
python3 tools/go_native_prod_smoke.py --date 2026-07-02 --timeout 8

View File

@@ -20,6 +20,18 @@ go build ./cmd/realtime-api
## 生产原生部署验证
```bash
python3 tools/go_prod_acceptance.py --date 2026-07-02
```
该命令会聚合执行:
- `go_systemd_prod_smoke.py`:应用 ECS systemd 服务和端口归属
- `go_kafka_prod_smoke.py`Kafka topic 和消费组 lag
- `go_native_prod_smoke.py`HTTP API、TDengine/MySQL/Redis 数据链路
如需单独检查 systemd
```bash
python3 tools/go_systemd_prod_smoke.py --host 115.29.187.205 --user root
```

136
tools/go_prod_acceptance.py Executable file
View File

@@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""Run the Go native production acceptance smoke suite."""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from dataclasses import asdict, dataclass
from typing import Any
DEFAULT_APP_HOST = "115.29.187.205"
DEFAULT_KAFKA_HOST = "114.55.58.251"
DEFAULT_USER = "root"
DEFAULT_BASE_URL = "http://115.29.187.205:20210"
@dataclass(frozen=True)
class ChildCommand:
name: str
argv: list[str]
@dataclass(frozen=True)
class ChildResult:
name: str
status: str
exit_code: int
payload: dict[str, Any]
def build_commands(args: argparse.Namespace) -> list[ChildCommand]:
timeout = str(args.timeout)
return [
ChildCommand("systemd", [
sys.executable,
"tools/go_systemd_prod_smoke.py",
"--host",
args.app_host,
"--user",
args.ssh_user,
"--timeout",
timeout,
]),
ChildCommand("kafka", [
sys.executable,
"tools/go_kafka_prod_smoke.py",
"--host",
args.kafka_host,
"--user",
args.ssh_user,
"--max-lag",
str(args.max_lag),
"--timeout",
timeout,
]),
ChildCommand("http", [
sys.executable,
"tools/go_native_prod_smoke.py",
"--base-url",
args.base_url,
"--timeout",
timeout,
] + (["--date", args.date] if args.date else [])),
]
def run_child(command: ChildCommand, timeout: float) -> ChildResult:
completed = subprocess.run(
command.argv,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=timeout,
)
payload = parse_json_output(completed.stdout)
if completed.stderr.strip():
payload.setdefault("stderr", completed.stderr.strip())
status = payload.get("status")
if status not in {"pass", "fail"}:
status = "pass" if completed.returncode == 0 else "fail"
return ChildResult(command.name, status, completed.returncode, payload)
def parse_json_output(output: str) -> dict[str, Any]:
output = output.strip()
if not output:
return {}
start = output.find("{")
end = output.rfind("}")
if start < 0 or end < start:
return {"rawOutput": output}
try:
return json.loads(output[start:end + 1])
except json.JSONDecodeError:
return {"rawOutput": output}
def overall_status(results: list[ChildResult]) -> str:
if not results:
return "fail"
return "fail" if any(result.status != "pass" or result.exit_code != 0 for result in results) else "pass"
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--app-host", default=DEFAULT_APP_HOST)
parser.add_argument("--kafka-host", default=DEFAULT_KAFKA_HOST)
parser.add_argument("--ssh-user", default=DEFAULT_USER)
parser.add_argument("--base-url", default=DEFAULT_BASE_URL)
parser.add_argument("--date", help="Shanghai local date, YYYY-MM-DD. Defaults to today in go_native_prod_smoke.py.")
parser.add_argument("--timeout", type=float, default=20.0)
parser.add_argument("--max-lag", type=int, default=100)
return parser.parse_args(argv)
def main(argv: list[str]) -> int:
args = parse_args(argv)
results: list[ChildResult] = []
for command in build_commands(args):
try:
results.append(run_child(command, args.timeout + 10))
except Exception as exc:
results.append(ChildResult(command.name, "fail", 1, {"status": "fail", "error": str(exc)}))
status = overall_status(results)
print(json.dumps({
"status": status,
"checks": [asdict(result) for result in results],
}, ensure_ascii=False, indent=2))
return 0 if status == "pass" else 1
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))

View File

@@ -0,0 +1,53 @@
import argparse
import sys
import unittest
from tools import go_prod_acceptance as acceptance
class GoProdAcceptanceTest(unittest.TestCase):
def test_build_commands_runs_systemd_kafka_and_http_smokes(self):
args = argparse.Namespace(
app_host="115.29.187.205",
kafka_host="114.55.58.251",
ssh_user="root",
base_url="http://115.29.187.205:20210",
date="2026-07-02",
timeout=8.0,
max_lag=100,
)
commands = acceptance.build_commands(args)
self.assertEqual(len(commands), 3)
self.assertEqual(commands[0].name, "systemd")
self.assertEqual(commands[0].argv[:2], [sys.executable, "tools/go_systemd_prod_smoke.py"])
self.assertIn("--host", commands[0].argv)
self.assertIn("115.29.187.205", commands[0].argv)
self.assertEqual(commands[1].name, "kafka")
self.assertIn("tools/go_kafka_prod_smoke.py", commands[1].argv)
self.assertIn("--max-lag", commands[1].argv)
self.assertEqual(commands[2].name, "http")
self.assertIn("tools/go_native_prod_smoke.py", commands[2].argv)
self.assertIn("--base-url", commands[2].argv)
def test_overall_status_fails_when_any_child_fails(self):
results = [
acceptance.ChildResult("systemd", "pass", 0, {"status": "pass"}),
acceptance.ChildResult("kafka", "fail", 1, {"status": "fail"}),
]
self.assertEqual(acceptance.overall_status(results), "fail")
def test_overall_status_passes_when_all_children_pass(self):
results = [
acceptance.ChildResult("systemd", "pass", 0, {"status": "pass"}),
acceptance.ChildResult("kafka", "pass", 0, {"status": "pass"}),
acceptance.ChildResult("http", "pass", 0, {"status": "pass"}),
]
self.assertEqual(acceptance.overall_status(results), "pass")
if __name__ == "__main__":
unittest.main()