diff --git a/docs/operations/current-ecs-deployment.md b/docs/operations/current-ecs-deployment.md index da85cca4..c800e7ae 100644 --- a/docs/operations/current-ecs-deployment.md +++ b/docs/operations/current-ecs-deployment.md @@ -167,7 +167,7 @@ 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 ``` -`go_systemd_prod_smoke.py` 通过 SSH 检查四个 Go systemd 服务是否 `active`,并确认 `808`、`32960` 由 `gateway` 监听、`20210` 由 `realtime-api` 监听,避免旧 Java/Docker 进程占用生产端口。运行环境需要已配置 SSH 免密或已建立可用的 SSH 认证方式。 +`go_systemd_prod_smoke.py` 通过 SSH 检查四个 Go systemd 服务是否 `active` 且 `enabled`,并确认 `808`、`32960` 由 `gateway` 监听、`20210` 由 `realtime-api` 监听,避免旧 Java/Docker 进程占用生产端口。运行环境需要已配置 SSH 免密或已建立可用的 SSH 认证方式。 `go_native_prod_smoke.py` 通过 `20210` HTTP API 验证 GB32960、JT808、宇通 MQTT 的 RAW 查询,三类协议的位置和里程点查询,GB32960/JT808 的 `daily_mileage_km`、`daily_total_mileage_km` 统计,以及三类协议的 Redis realtime snapshot、online、protocol 查询。默认检查今天东八区数据时,还会要求三类 RAW 最新样本不超过 15 分钟,避免旧数据误判为接收正常;任一检查不达标时退出码为非 0。 @@ -212,7 +212,7 @@ journalctl -u lingniu-go-realtime-api --since '5 minutes ago' --no-pager 2026-07-02 01:30 CST 已验证: -- 四个 Go systemd 服务均为 `active`。 +- 四个 Go systemd 服务均为 `active` 且 `enabled`。 - `gateway` 正在监听 `32960` 和 `808`。 - `realtime-api` 正在监听 `20210`。 - GB32960、JT808、YUTONG_MQTT 的东八区 RAW 查询均可命中生产数据。 diff --git a/docs/operations/go-vehicle-gateway-verification.md b/docs/operations/go-vehicle-gateway-verification.md index 56c5dc00..f2da60b7 100644 --- a/docs/operations/go-vehicle-gateway-verification.md +++ b/docs/operations/go-vehicle-gateway-verification.md @@ -26,7 +26,7 @@ python3 tools/go_prod_acceptance.py --date 2026-07-02 该命令会聚合执行: -- `go_systemd_prod_smoke.py`:应用 ECS systemd 服务和端口归属 +- `go_systemd_prod_smoke.py`:应用 ECS systemd 服务 active/enabled 和端口归属 - `go_kafka_prod_smoke.py`:Kafka topic 和消费组 lag - `go_native_prod_smoke.py`:HTTP API、TDengine/MySQL/Redis 数据链路 @@ -42,6 +42,7 @@ python3 tools/go_systemd_prod_smoke.py --host 115.29.187.205 --user root - `lingniu-go-history-writer.service` - `lingniu-go-stat-writer.service` - `lingniu-go-realtime-api.service` +- 上述服务均为 `active` 且 `enabled` - `808`、`32960` 由 `gateway` 监听 - `20210` 由 `realtime-api` 监听 diff --git a/tools/go_systemd_prod_smoke.py b/tools/go_systemd_prod_smoke.py index e3145f55..c693a3d4 100755 --- a/tools/go_systemd_prod_smoke.py +++ b/tools/go_systemd_prod_smoke.py @@ -32,12 +32,19 @@ class Check: message: str -def parse_service_states(output: str) -> dict[str, str]: - states: dict[str, str] = {} +@dataclass(frozen=True) +class ServiceState: + active: str + enabled: str + + +def parse_service_states(output: str) -> dict[str, ServiceState]: + states: dict[str, ServiceState] = {} for line in output.splitlines(): parts = line.split() if len(parts) >= 2: - states[parts[0]] = parts[1] + enabled = parts[2] if len(parts) >= 3 else "enabled" + states[parts[0]] = ServiceState(parts[1], enabled) return states @@ -45,9 +52,13 @@ def service_checks(output: str, services: list[str]) -> list[Check]: states = parse_service_states(output) checks: list[Check] = [] for service in services: - state = states.get(service, "missing") - status = "pass" if state == "active" else "fail" - checks.append(Check("service." + service, status, "state=" + state)) + state = states.get(service, ServiceState("missing", "missing")) + status = "pass" if state.active == "active" and state.enabled == "enabled" else "fail" + checks.append(Check( + "service." + service, + status, + "state=" + state.active + "; enabled=" + state.enabled, + )) return checks @@ -89,7 +100,9 @@ def remote_command(services: list[str]) -> str: quoted = " ".join(services) return ( "for svc in " + quoted + "; do " - "printf '%s ' \"$svc\"; systemctl is-active \"$svc\" || true; " + "printf '%s ' \"$svc\"; " + "printf '%s ' \"$(systemctl is-active \"$svc\" 2>/dev/null || true)\"; " + "systemctl is-enabled \"$svc\" 2>/dev/null || true; " "done; " "printf '\\n--PORTS--\\n'; " "ss -lntp" diff --git a/tools/test_go_systemd_prod_smoke.py b/tools/test_go_systemd_prod_smoke.py index 09eef163..7d7f5b5e 100644 --- a/tools/test_go_systemd_prod_smoke.py +++ b/tools/test_go_systemd_prod_smoke.py @@ -6,10 +6,10 @@ from tools import go_systemd_prod_smoke as smoke class GoSystemdProdSmokeTest(unittest.TestCase): def test_service_checks_require_all_go_units_active(self): output = """ -lingniu-go-gateway.service active -lingniu-go-history-writer.service active -lingniu-go-stat-writer.service active -lingniu-go-realtime-api.service active +lingniu-go-gateway.service active enabled +lingniu-go-history-writer.service active enabled +lingniu-go-stat-writer.service active enabled +lingniu-go-realtime-api.service active enabled """ checks = smoke.service_checks(output, smoke.DEFAULT_SERVICES) @@ -18,8 +18,8 @@ lingniu-go-realtime-api.service active def test_service_checks_fail_when_unit_is_missing_or_inactive(self): output = """ -lingniu-go-gateway.service active -lingniu-go-history-writer.service failed +lingniu-go-gateway.service active enabled +lingniu-go-history-writer.service failed enabled """ checks = smoke.service_checks(output, smoke.DEFAULT_SERVICES) @@ -28,6 +28,20 @@ lingniu-go-history-writer.service failed self.assertEqual(by_name["service.lingniu-go-history-writer.service"].status, "fail") self.assertEqual(by_name["service.lingniu-go-stat-writer.service"].status, "fail") + def test_service_checks_fail_when_unit_is_not_enabled(self): + output = """ +lingniu-go-gateway.service active disabled +lingniu-go-history-writer.service active enabled +lingniu-go-stat-writer.service active enabled +lingniu-go-realtime-api.service active enabled +""" + + checks = smoke.service_checks(output, smoke.DEFAULT_SERVICES) + by_name = {check.name: check for check in checks} + + self.assertEqual(by_name["service.lingniu-go-gateway.service"].status, "fail") + self.assertIn("enabled=disabled", by_name["service.lingniu-go-gateway.service"].message) + def test_port_checks_require_expected_go_processes(self): output = """ LISTEN 0 4096 *:32960 *:* users:(("gateway",pid=10,fd=3))