merge(deploy): 修复回滚启动就绪竞态
This commit is contained in:
@@ -8,12 +8,28 @@ STATE_DIR="${AIOS_V2_STATE_DIR:-/var/lib/aios-v2}"
|
||||
BACKUP_ROOT="${AIOS_V2_BACKUP_ROOT:-/var/backups/aios-v2}"
|
||||
TARGET_RELEASE="${1:-}"
|
||||
RESTORE_STATE="${AIOS_V2_RESTORE_STATE:-0}"
|
||||
HEALTH_RETRIES="${AIOS_V2_ROLLBACK_HEALTH_RETRIES:-30}"
|
||||
HEALTH_RETRY_DELAY="${AIOS_V2_ROLLBACK_HEALTH_RETRY_DELAY:-1}"
|
||||
|
||||
fail() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_healthcheck() {
|
||||
local release="$1"
|
||||
local attempt
|
||||
for ((attempt = 1; attempt <= HEALTH_RETRIES; attempt++)); do
|
||||
if AIOS_V2_SERVICE="$SERVICE" \
|
||||
AIOS_V2_STATE_DIR="$STATE_DIR" \
|
||||
"$release/deploy/aios-v2/healthcheck/check.sh"; then
|
||||
return 0
|
||||
fi
|
||||
[[ "$attempt" -lt "$HEALTH_RETRIES" ]] && sleep "$HEALTH_RETRY_DELAY"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
[[ "${EUID}" -eq 0 ]] || fail "rollback must run as root"
|
||||
[[ -n "$TARGET_RELEASE" ]] || fail "usage: $0 <release-name-or-absolute-path>"
|
||||
|
||||
@@ -53,9 +69,7 @@ mv -Tf "${CURRENT_LINK}.next" "$CURRENT_LINK"
|
||||
systemctl daemon-reload
|
||||
systemctl start "$SERVICE"
|
||||
|
||||
if AIOS_V2_SERVICE="$SERVICE" \
|
||||
AIOS_V2_STATE_DIR="$STATE_DIR" \
|
||||
"$TARGET_PATH/deploy/aios-v2/healthcheck/check.sh"; then
|
||||
if run_healthcheck "$TARGET_PATH"; then
|
||||
echo "ROLLBACK_COMPLETE target=$TARGET_PATH backup=$BACKUP_DIR"
|
||||
exit 0
|
||||
fi
|
||||
@@ -78,9 +92,7 @@ systemctl daemon-reload
|
||||
systemctl start "$SERVICE" || true
|
||||
|
||||
if [[ -n "$PREVIOUS_PATH" && -x "$PREVIOUS_PATH/deploy/aios-v2/healthcheck/check.sh" ]]; then
|
||||
AIOS_V2_SERVICE="$SERVICE" \
|
||||
AIOS_V2_STATE_DIR="$STATE_DIR" \
|
||||
"$PREVIOUS_PATH/deploy/aios-v2/healthcheck/check.sh" || true
|
||||
run_healthcheck "$PREVIOUS_PATH" || true
|
||||
fi
|
||||
|
||||
fail "rollback target failed; previous release restored; evidence=$BACKUP_DIR"
|
||||
|
||||
@@ -492,6 +492,8 @@ POST /api/v2/hermes/results/reconcile
|
||||
- 回滚后必须执行 healthcheck。
|
||||
- Release Archive 必须保留 `rollback.sh` 与 `healthcheck/check.sh` 的 executable bit;
|
||||
验收测试必须阻止非执行模式进入候选包。
|
||||
- `systemctl start` 后的回滚健康检查必须执行有界 readiness 重试,默认最多 30 次、
|
||||
每次间隔 1 秒;不得因服务尚未监听的一次瞬时拒绝误判目标 Release 失败。
|
||||
|
||||
## 14. 契约变更规则
|
||||
|
||||
|
||||
@@ -79,6 +79,12 @@ class CandidateFlowTests(unittest.TestCase):
|
||||
mode = (REPO_ROOT / relative).stat().st_mode
|
||||
self.assertTrue(mode & stat.S_IXUSR, relative)
|
||||
|
||||
def test_rollback_waits_for_bounded_service_readiness(self) -> None:
|
||||
script = (REPO_ROOT / "deploy/aios-v2/rollback/rollback.sh").read_text()
|
||||
self.assertIn("AIOS_V2_ROLLBACK_HEALTH_RETRIES", script)
|
||||
self.assertIn("run_healthcheck", script)
|
||||
self.assertIn("HEALTH_RETRY_DELAY", script)
|
||||
|
||||
def test_build_application_propagates_openclaw_signing_key_id(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
material_path = Path(temp_dir) / "signing-material.bin"
|
||||
|
||||
Reference in New Issue
Block a user