This commit is contained in:
@@ -13,6 +13,8 @@ MYSQLDUMP_BIN="${MYSQLDUMP_BIN:-}"
|
||||
MYSQL_CONTAINER="${MYSQL_CONTAINER:-}"
|
||||
PREVIOUS_REVISION="${PREVIOUS_REVISION:-}"
|
||||
HEALTH_HOST="${DEPLOY_HEALTH_HOST:-}"
|
||||
SMOKE_BASE_URL="${DEPLOY_SMOKE_BASE_URL:-}"
|
||||
SMOKE_WS_URL="${DEPLOY_SMOKE_WS_URL:-}"
|
||||
ROLLBACK_ENABLED=0
|
||||
MYSQL_CREDENTIALS_FILE=""
|
||||
MYSQL_CONTAINER_CREDENTIALS_PATH=""
|
||||
@@ -291,6 +293,17 @@ async def check():
|
||||
asyncio.run(check())
|
||||
PY
|
||||
|
||||
if [ -n "$SMOKE_BASE_URL" ] || [ -n "$SMOKE_WS_URL" ]; then
|
||||
[ -n "$SMOKE_BASE_URL" ] && [ -n "$SMOKE_WS_URL" ] \
|
||||
|| fail "DEPLOY_SMOKE_BASE_URL 和 DEPLOY_SMOKE_WS_URL 必须同时设置"
|
||||
[ -f "$PROJECT_DIR/scripts/smoke_production.py" ] \
|
||||
|| fail "缺少生产冒烟脚本: scripts/smoke_production.py"
|
||||
log "检查 Nginx、业务页面与公网 WebSocket"
|
||||
"$VENV_DIR/bin/python" "$PROJECT_DIR/scripts/smoke_production.py" \
|
||||
--base-url "$SMOKE_BASE_URL" \
|
||||
--ws-url "$SMOKE_WS_URL"
|
||||
fi
|
||||
|
||||
trap - ERR
|
||||
ROLLBACK_ENABLED=0
|
||||
log "部署完成,当前版本: $(git rev-parse --short HEAD)"
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
from urllib.request import urlopen
|
||||
|
||||
from websockets.asyncio.client import connect
|
||||
|
||||
|
||||
def fetch(base_url, path):
|
||||
url = f"{base_url.rstrip('/')}/{path.lstrip('/')}"
|
||||
with urlopen(url, timeout=10) as response:
|
||||
if response.status != 200:
|
||||
raise RuntimeError(f"{url} returned HTTP {response.status}")
|
||||
return response.read()
|
||||
|
||||
|
||||
async def check_websocket(url):
|
||||
async with connect(url, open_timeout=10, close_timeout=5) as websocket:
|
||||
payload = json.loads(await asyncio.wait_for(websocket.recv(), timeout=10))
|
||||
expected = {"status": "ok", "channel_layer": "ok"}
|
||||
if payload != expected:
|
||||
raise RuntimeError(f"{url} returned unexpected payload: {payload}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Check the deployed Hulumath Nginx surface")
|
||||
parser.add_argument("--base-url", required=True)
|
||||
parser.add_argument("--ws-url", required=True)
|
||||
parser.add_argument("--minimum-videos", type=int, default=54)
|
||||
args = parser.parse_args()
|
||||
|
||||
health = json.loads(fetch(args.base_url, "/health/"))
|
||||
if health != {"status": "ok", "database": "ok"}:
|
||||
raise RuntimeError(f"unexpected health payload: {health}")
|
||||
|
||||
homepage = fetch(args.base_url, "/").decode("utf-8")
|
||||
if "葫芦数学" not in homepage:
|
||||
raise RuntimeError("homepage marker is missing")
|
||||
|
||||
admin_login = fetch(args.base_url, "/admin/login/").decode("utf-8")
|
||||
if "进入运营后台" not in admin_login:
|
||||
raise RuntimeError("admin login marker is missing")
|
||||
|
||||
catalog = json.loads(fetch(args.base_url, "/api/v1/content/videos/catalog/"))
|
||||
if catalog.get("total", 0) < args.minimum_videos:
|
||||
raise RuntimeError(f"video catalog is incomplete: {catalog.get('total', 0)}")
|
||||
|
||||
asyncio.run(check_websocket(args.ws_url))
|
||||
print(
|
||||
"Production smoke checks passed: "
|
||||
f"health, homepage, admin, {catalog['total']} videos, websocket"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user