test: add go production acceptance smoke
This commit is contained in:
@@ -154,6 +154,14 @@ curl -sS 'http://115.29.187.205:20210/api/stats/daily-metrics?dateFrom=2026-07-0
|
|||||||
|
|
||||||
部署后推荐直接运行 Go 原生生产 smoke:
|
部署后推荐直接运行 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
|
```bash
|
||||||
python3 tools/go_systemd_prod_smoke.py --host 115.29.187.205 --user root
|
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
|
python3 tools/go_native_prod_smoke.py --date 2026-07-02 --timeout 8
|
||||||
|
|||||||
@@ -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
|
```bash
|
||||||
python3 tools/go_systemd_prod_smoke.py --host 115.29.187.205 --user root
|
python3 tools/go_systemd_prod_smoke.py --host 115.29.187.205 --user root
|
||||||
```
|
```
|
||||||
|
|||||||
136
tools/go_prod_acceptance.py
Executable file
136
tools/go_prod_acceptance.py
Executable 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:]))
|
||||||
53
tools/test_go_prod_acceptance.py
Normal file
53
tools/test_go_prod_acceptance.py
Normal 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()
|
||||||
Reference in New Issue
Block a user